The two limits
| Limit | Applies to | Value | When exceeded |
|---|---|---|---|
| Request rate | All GET requests, POST /api/v1/mcp, and /api/v1/b2b/* | 60 per minute, per account and key mode (live and sandbox are counted separately) | 429 RATE_LIMIT_EXCEEDED |
| Render concurrency | Heavy render requests (/generate-and-post, /blog-to-video, POST /jobs) on live keys | 5 active heavy render jobs per account | 429 RENDER_CONCURRENCY_LIMIT_EXCEEDED |
There are no per-plan tiers. If you need more throughput, contact admin@reelsbuilder.ai.
Request-rate headers
Responses covered by the request-rate limit include:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1791288060- Limit — requests allowed per window.
- Remaining — requests left in the current window.
- Reset — Unix time (seconds) when the window resets.
The window is a fixed 60 seconds that starts with your first request after the previous window expired — not a sliding window and not aligned to the clock minute.
When you hit the request-rate limit
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1791288060
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many metadata requests for this API key. Retry after the reset time.",
"retryable": true
},
"meta": { "retryable": true, "retry_after_seconds": 23 }
}Wait Retry-After seconds before the next request.
Render concurrency
Stock, blog-to-video and generation jobs submitted through POST /jobs check the account's active heavy renders (default limit: 5). These routes reject a request at the limit instead of queueing it. AI video, AI influencer and clipping do not enforce this guard today; cap their concurrency in your application. A guarded route returns:
HTTP/1.1 429 Too Many Requests
X-Render-Concurrency-Limit: 5
X-Render-Concurrency-Used: 5
{
"success": false,
"error": {
"code": "RENDER_CONCURRENCY_LIMIT_EXCEEDED",
"message": "This API key already has 5 active heavy render jobs. Retry after one completes.",
"retryable": true
},
"meta": { "retryable": true, "active_render_jobs": 5, "active_render_limit": 5 }
}Poll your existing jobs and submit the next one when an active job reaches a terminal state. Accepted render requests also carry the X-Render-Concurrency-* headers so you can track usage. Sandbox keys are not subject to this limit.
Client-side handling
TypeScript
async function getWithBackoff(url: string) {
for (;;) {
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.REELSBUILDER_API_KEY}` },
});
if (res.status !== 429) return res;
const retryAfter = Number(res.headers.get("retry-after") ?? 5);
await new Promise((r) => setTimeout(r, retryAfter * 1000));
}
}Python
import os, time, requests
def get_with_backoff(url):
while True:
r = requests.get(
url,
headers={"Authorization": f"Bearer {os.environ['REELSBUILDER_API_KEY']}"},
timeout=30,
)
if r.status_code != 429:
return r
time.sleep(int(r.headers.get("retry-after", "5")))When retrying a POST after a 429, use a new Idempotency-Key: the 429 response itself is cached against the original key for 24 hours (see Idempotency).