API docs · Reference
Rate limits
Per-account request limits per minute, the active-job cap, the rate-limit headers and how to back off.
Limits
Limits apply per account: every key of the account draws from the same buckets. Windows are one minute.
| Bucket | Limit | Counts |
|---|---|---|
| Reads | 600 / minute | Every GET with a key: jobs, results, quota, delivery log |
| Job creation | 30 / minute | POST /jobs, POST /jobs/from-url, POST /jobs/batch (one request, whatever its clip count) |
| Other writes | 120 / minute | Everything else, for example POST /jobs/{jobId}/cancel |
| Keyless discovery | 120 / minute per client IP | GET /openapi.json, /pricing, /pricing/estimate, /formats |
| Webhook test | 5 / minute | POST /webhooks/test |
Discovery responses are also cacheable (Cache-Control: public, max-age=300), so cache them rather than calling them per job.
Active jobs
Separately from request rates, an account can have a limited number of unfinished jobs at once (50 by default). Creating more returns 429 TOO_MANY_ACTIVE_JOBS with Retry-After: 30 and a body that includes maxActiveJobs and activeJobs. Each clip of an independent batch counts as one job. If your workload needs more concurrency, contact us.
Headers
Every response to a request that passed key authentication (success or error), and every discovery response, carries:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed per window in the bucket this request was charged to |
X-RateLimit-Remaining | Requests left in the current window |
X-RateLimit-Reset | When the window resets, as a Unix timestamp in seconds (not a number of seconds to wait) |
Retry-After | On 429: seconds to wait before retrying |
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1760356800Backing off
On 429 (and 503), wait exactly Retry-After seconds and retry the same request, with the same Idempotency-Key for creates. Without a Retry-After (a 500), use exponential backoff with jitter.
import random, time
import httpx
def request_with_retries(client: httpx.Client, method: str, url: str, **kwargs) -> httpx.Response:
for attempt in range(5):
resp = client.request(method, url, **kwargs)
if resp.status_code in (429, 503) or (resp.status_code == 409 and "Retry-After" in resp.headers):
time.sleep(float(resp.headers.get("Retry-After", "1")))
continue
if resp.status_code >= 500:
time.sleep(min(8, 0.5 * 2 ** attempt) * (1 - 0.25 * random.random()))
continue
return resp
return respThe SDKs do this for you, honouring Retry-After up to 60 seconds. THROTTLED (Retry-After: 3600) is not something to wait out in a loop; see Errors.
Staying under the limits
- Poll with
GET /jobs/{jobId}?include=noneevery 5 to 15 seconds, not faster, or use webhooks. - For many URLs, one
POST /jobs/batchwith up to 100 clips uses one job-creation request. - To list work, page through
GET /jobs?state=...rather than polling each job.