Reference

Limits

Rate limits, concurrency, upload sizes, page sizes, waits and link lifetimes.

Read as Markdown

Every 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.

BucketPer minuteSpent by
agent_read120Capability list and detail, actors, voices, generation reads, the wait, batch reads, templates, workflow run reads
agent_estimate60POST /estimates, estimate_generation, riffads estimate
agent_submit20POST /generations, submit_generation, riffads generate
agent_upload30Reserving and finalizing an upload
workflow_run20Both workflow invoke routes and run_workflow
  • One wait call costs one agent_read token, however long it blocks.
  • No X-RateLimit-* headers. Only Retry-After, and only on a 429 that has retry_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.

RuleLimitWindow
Dynamic client registration1010 minutes
Every other MCP path12060 seconds

This 429 is OAuth shaped ({"error": "too_many_requests", ...}), not the error envelope.

Concurrency

RuleValue
Agent submissions running, REST1 per API key
Agent submissions running, MCP1 per person
A stuck job stops holding the slot after600 seconds
Renders at once, per workspaceSet by plan
  • Slot busy: submission_in_flight with in_flight.generation_id. Wait on that job.
  • Render cap hit: rate_limited with no retry_after_seconds. Wait for a job to finish.
  • One submit with several variants takes one slot.

Variants

RuleValue
Variants per submitmax 4
Real ceilingthe smaller of the capability's max and 4
Over the ceilingrefused, never clamped

Every variant is a full generation and is charged.

Uploads

KindAccepted typesMax size
Imageimage/jpeg, image/png, image/webp, image/gif20 MB
Videovideo/mp4, video/webm, video/quicktime100 MB
Audioaudio/mpeg, audio/mp3, audio/wav, audio/x-wav, audio/webm, audio/ogg25 MB
RuleValue
Pixels per imagemax 40,000,000
Longest image sidemax 16,384 px
filename1 to 255 characters
Signed PUT link300 seconds, one use, exact byte count
Unfinished reservations per workspacemax 50
Abandoned reservation clears after15 minutes
Reservations per workspace30 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.

Uploads.

Page sizes

ListDefaultMax
GET /capabilities2550
GET /actors2550
GET /voices2550
  • limit above the max is clamped, not refused. Junk values (abc, 0, -1, 2.5) read as omitted.
  • Cursors are opaque. next_cursor is null on the last page. A stale cursor restarts from the top.

Waits and timeouts

ThingValue
One wait call blocks at most20 seconds
Wait route wall clock60 seconds
MCP route wall clock60 seconds
Image inspection on finalize5 seconds
  • Wait budget used up: 200 with still_running: true. Never 408 or 504. Call again.
  • No timeout parameter.

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

ThingValue
Request timeout30 seconds
Wait call timeout90 seconds
riffads generate, riffads status --wait give up after30 minutes (--timeout <seconds>)
riffads workflows invoke --wait, riffads workflows run --wait give up after60 minutes (--timeout <seconds>)
Retryable refusals in a row before it stops5

Giving up is not cancelling. The job keeps running. CLI commands.

LinkLifetime
Output link (REST, MCP, CLI)600 seconds (output_urls_expire_in_seconds: 600)
Signed upload PUT link300 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

WindowValue
Failures in a row that pause a request3
Pause lastsabout 1 hour
Content refusals that block a request2
Block lasts30 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. maxCredits is 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.

On this page