API reference / Rate limits & quotas
Rate limits & quotas
Two independent limits apply to /v1/generate: a monthly quota (how many documents you can
generate per calendar month) and a per-minute rate limit (how fast you can send requests).
They exist for different reasons and return different errors.
Limits by plan
| Plan | Monthly quota | Requests / min |
|---|---|---|
| Free | 50 | 5 |
| Starter | 1,000 | 30 |
| Pro | 5,000 | 120 |
| Scale | 25,000 | 300 |
(Plans also differ on active API keys and history retention. See Plans & limits.)
Monthly quota: limit_exceeded (429)
Each plan has a monthly generation quota, counted per calendar month in UTC (a period looks
like 2026-06). When you reach it, further calls return limit_exceeded (429) until the next
month begins. The fix is to upgrade or wait for the reset.
Only successful generations count toward the quota. A failed call never does.
Request rate: rate_limited (429)
Each document is a full browser render, so there is also a short-term cap on how many requests
per minute you may send. Bursting past it returns rate_limited (429) with a Retry-After
header telling you how long to wait. This is "slow down", not "you're out of quota".
Both limits use HTTP 429, but the code distinguishes them. rate_limited means wait a
moment and retry. limit_exceeded means you've hit your monthly allowance. Always branch on
code, not the status alone.
Capacity: service_unavailable (503)
If the render engine is momentarily saturated, a request can be shed with service_unavailable
(503) rather than queued indefinitely. This is transient: retry after a short, backed-off delay,
honoring the Retry-After header when the response carries one.
Accounting guarantee
The successful generation and its usage increment are written in a single transaction, so your usage figure can never disagree with what actually rendered, and a failed render leaves the counter untouched.
Retrying safely
A failed call never produces a document or consumes quota, so retrying a failure is always
safe. The one genuinely ambiguous case is a lost response: the request may have succeeded
(and billed) even though you never saw the bytes. That's what the
Idempotency-Key header is for: retry with the same key and
you get the first outcome back, billed exactly once. So:
- Retry the transient failures with exponential backoff:
rate_limited(wait at least theRetry-Aftervalue),service_unavailable, and usuallyinternal_error. - Retry network failures only under an idempotency key: without one, a retry after a lost response can generate (and bill) a second document.
- Don't retry request errors (
invalid_json,invalid_data,template_not_found,template_not_published): fix the request first, or you'll just fail again. - Guard user-triggered generation against double-submits: an idempotency key derived from the user action makes the second click a free replay.
A reasonable policy: up to ~3 retries, starting around 1s and doubling, capped at a few seconds, and never retrying a 4xx that's your request's fault. The TypeScript SDK implements exactly this policy out of the box, including the key-gated network retries. See Errors for the per-code guidance.