Limits
Rate limits, concurrency, upload sizes, page sizes, waits and link lifetimes.
Read as MarkdownEvery number RiffAds enforces on the API, MCP and the CLI. All are fixed. No plan or header changes them unless the row says so.
Rate limits
REST gets both brakes below. MCP has no key, so it gets only the per-action buckets. Over either one: rate_limited with retry_after_seconds.
Per key
- 120 requests a minute per API key, 60 second window.
- Copied onto the key when it is created.
Per action
Fixed 60 second windows. Counted per API key on REST, per person on MCP.
| Bucket | Per minute | Spent by |
|---|---|---|
agent_read | 120 | Capability list and detail, actors, voices, generation reads, the wait, batch reads, templates, workflow run reads |
agent_estimate | 60 | POST /estimates, estimate_generation, riffads estimate |
agent_submit | 20 | POST /generations, submit_generation, riffads generate |
agent_upload | 30 | Reserving and finalizing an upload |
workflow_run | 20 | Both workflow invoke routes and run_workflow |
- One wait call costs one
agent_readtoken, however long it blocks. - No
X-RateLimit-*headers. OnlyRetry-After, and only on a 429 that hasretry_after_seconds. quota_exceeded(the key's request allowance is spent) is also 429, but not retryable. Error codes.
MCP host brake
Per IP, before any token check, on https://mcp.riffads.com only.
| Rule | Limit | Window |
|---|---|---|
| Dynamic client registration | 10 | 10 minutes |
| Every other MCP path | 120 | 60 seconds |
This 429 is OAuth shaped ({"error": "too_many_requests", ...}), not the error envelope.
Concurrency
| Rule | Value |
|---|---|
| Agent submissions running, REST | 1 per API key |
| Agent submissions running, MCP | 1 per person |
| A stuck job stops holding the slot after | 600 seconds |
| Renders at once, per workspace | Set by plan |
- Slot busy:
submission_in_flightwithin_flight.generation_id. Wait on that job. - Render cap hit:
rate_limitedwith noretry_after_seconds. Wait for a job to finish. - One submit with several variants takes one slot.
Variants
| Rule | Value |
|---|---|
| Variants per submit | max 4 |
| Real ceiling | the smaller of the capability's max and 4 |
| Over the ceiling | refused, never clamped |
Every variant is a full generation and is charged.
Uploads
| Kind | Accepted types | Max size |
|---|---|---|
| Image | image/jpeg, image/png, image/webp, image/gif | 20 MB |
| Video | video/mp4, video/webm, video/quicktime | 100 MB |
| Audio | audio/mpeg, audio/mp3, audio/wav, audio/x-wav, audio/webm, audio/ogg | 25 MB |
| Rule | Value |
|---|---|
| Pixels per image | max 40,000,000 |
| Longest image side | max 16,384 px |
filename | 1 to 255 characters |
| Signed PUT link | 300 seconds, one use, exact byte count |
| Unfinished reservations per workspace | max 50 |
| Abandoned reservation clears after | 15 minutes |
| Reservations per workspace | 30 a minute |
- Other file types are refused at reserve, before any bytes move.
- A capability can set stricter file rules. The refusal names its number.
Page sizes
| List | Default | Max |
|---|---|---|
GET /capabilities | 25 | 50 |
GET /actors | 25 | 50 |
GET /voices | 25 | 50 |
limitabove the max is clamped, not refused. Junk values (abc,0,-1,2.5) read as omitted.- Cursors are opaque.
next_cursorisnullon the last page. A stale cursor restarts from the top.
Waits and timeouts
| Thing | Value |
|---|---|
| One wait call blocks at most | 20 seconds |
| Wait route wall clock | 60 seconds |
| MCP route wall clock | 60 seconds |
| Image inspection on finalize | 5 seconds |
- Wait budget used up: 200 with
still_running: true. Never 408 or 504. Call again. - No
timeoutparameter.
This is not a render time
There is no published render time. Loop the wait until still_running is false. Don't build a deadline from the wait budget.
CLI
| Thing | Value |
|---|---|
| Request timeout | 30 seconds |
| Wait call timeout | 90 seconds |
riffads generate, riffads status --wait give up after | 30 minutes (--timeout <seconds>) |
riffads workflows invoke --wait, riffads workflows run --wait give up after | 60 minutes (--timeout <seconds>) |
| Retryable refusals in a row before it stops | 5 |
Giving up is not cancelling. The job keeps running. CLI commands.
Link lifetimes
| Link | Lifetime |
|---|---|
| Output link (REST, MCP, CLI) | 600 seconds (output_urls_expire_in_seconds: 600) |
| Signed upload PUT link | 300 seconds |
Never store a link
Download right away. If it fails, read the generation again for a fresh link. Don't retry the old URL. Results.
Blocks
| Window | Value |
|---|---|
| Failures in a row that pause a request | 3 |
| Pause lasts | about 1 hour |
| Content refusals that block a request | 2 |
| Block lasts | 30 days |
Only moderation_blocked, provider_unavailable and internal_error count. Error codes.
Request bodies
- Every write body is strict. Unknown keys are refused, including inside
config.maxCreditsis a 400. - Workflow invoke: max 60 input entries.
- Validation refusals list max 5 issues, then
Plus N more. - No published body size limit. Send files through uploads.
Spend caps
Agent spend is capped by max_credits on each request, plus caps a workspace owner or admin sets. Over one: max_credits_exceeded or spend_limit_exceeded.