Skip to content

Rate limits

The /v1 API enforces a fixed-window rate limit per root team:

60 requests per minute per root team, regardless of authentication method or whether requests come from one machine or many.

Child teams share their root team’s quota. Calls authenticated via API key or via a Firebase JWT count toward the same bucket.

Every successful or rate-limited response includes:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1745800140
  • Limit — total requests allowed in the current window
  • Remaining — requests left before you’ll get a 429
  • Reset — unix-seconds timestamp when the window rolls over

Subsequent requests within the same one-minute window return 429:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1745800140
Retry-After: 42
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded: 60 requests/minute",
"retry_after_seconds": 42
}
}

Every 429 also carries a Retry-After header and a matching retry_after_seconds field in the body. Wait that many seconds and retry.

The endpoints you poll while work runs have tighter limits of their own, on top of the 60 requests per minute above:

  • GET /v1/clips/:id
  • GET /v1/clips/:id/posts
  • GET /v1/renders/:id
  • GET /v1/posts/:id
LimitValue
Checks of the same job, render or post6 per minute (one every 10 seconds)
All status checks for your team30 per minute
Status checks for ids that don’t exist (404)20 per hour

After 20 not-found responses in an hour, every status check returns 429 until the hour ends. This protects against an integration stuck polling ids it made up or lost — check the job_id you got back from POST /v1/clips.

Status limits return the same 429 shape with code STATUS_RATE_LIMITED, plus Retry-After.

The flows most callers run aren’t anywhere near 60 rpm:

  • Per clip job: 1 POST, then poll GET /v1/clips/:id every ~10s for ~2 minutes — about 12 requests total.
  • Per render: 1 POST, then poll GET /v1/renders/:id every ~10s for ~30s — about 3 requests.

If you find yourself hitting the limit, the fix is usually to register a webhook and stop polling. A single webhook delivery replaces an entire polling loop.