Rate limits
CastPDF rate limits per plan and key mode, PDFs rendering at once, background and webhook limits, the x-ratelimit headers, and how to back off.
On this page
Rate limits keep one busy client from slowing down everyone else, and they protect you from a runaway loop in your own code. CastPDF limits how many requests a team sends per minute, how many PDFs it renders at the same time, and a few slower kinds of work. A request over a limit gets 429 with the code rate_limited and, for most limits, a Retry-After header that says how long to wait. This page lists every limit and how to handle it.
Requests per minute
Requests are counted per team and per key mode: all live keys of a team share one budget, and all test keys share another. A live key's budget depends on your plan; test keys get the same budget on every plan:
| Key | Requests per minute |
|---|---|
| Live key, Free plan | 30 |
| Live key, Starter plan | 60 |
| Live key, Growth plan | 120 |
| Live key, Pro plan | 300 |
| Live key, Scale plan | 600 |
| Test key (any plan) | 20 |
The window is fixed: it starts with the first request and lasts one minute, then the count starts again from zero. The dashboard shares these budgets. Its Generate PDF button draws from the team's live budget, and its previews and test renders draw from the test budget, so heavy work in the editor never takes from your live traffic.
Every request to /v1 counts once, whatever it does, but a batch is one request: the PDFs it makes are not counted against the per-minute budget. GET /v1/me returns your key's limit as plan.rate_limit_per_min.
The headers
Responses to authenticated requests carry the state of the budget that applies:
| Header | Description |
|---|---|
x-ratelimit-limit | Requests allowed per minute |
x-ratelimit-remaining | Requests left in the current window |
x-ratelimit-reset | Seconds until the window resets |
Retry-After | On a 429 only: seconds to wait before retrying |
Previews and template changes have their own, tighter budgets (below); their responses show that budget in the same headers. A request over the per-minute limit gets this body:
{
"error": {
"code": "rate_limited",
"message": "Rate limit of 60 requests per minute exceeded. Retry in 42 seconds.",
"docs_url": "https://castpdf.com/docs/errors#rate_limited"
}
}Per IP address
All requests from one IP address together are limited to 600 per minute. This limit covers everything, including signed file downloads, requests without a key and requests with an invalid key. Addresses in the same IPv6 /64 network count as one. You only meet it if many teams or scripts share one address, or if a client sends requests in a tight loop.
PDFs rendering at the same time
Each team can have a limited number of PDFs rendering at once: Free 1, Starter 2, Growth 3, Pro 4 and Scale 4. Live keys, test keys, previews and the dashboard share this number. One more request gets 429 rate_limited with a details.reason of concurrency, the limit in details.limit, and a Retry-After of 1 second:
{
"error": {
"code": "rate_limited",
"message": "Too many PDFs rendering at once for this team (limit 2). Retry in a moment.",
"docs_url": "https://castpdf.com/docs/errors#rate_limited",
"details": { "reason": "concurrency", "limit": 2 }
}
}Renders take a few seconds, so this is the limit a client sending PDFs in parallel meets first. Send at most as many requests at once as your plan allows, or queue the rest on your side. For large jobs, use background rendering or a batch: background PDFs use the same slots, but they wait in CastPDF's queue instead of being refused, and the queue leaves one slot free for direct requests when your plan has more than one.
Previews and template changes
- Previews (
POST /v1/templates/:id/preview) are limited to 30 per minute per team and mode, on top of the per-minute budget. The dashboard editor's live preview shares the test-mode preview limit. - Template changes (creating, updating, pinning and deleting templates) are limited to 60 per minute per team, across API keys and the dashboard.
Both answer with 429 rate_limited and Retry-After when the limit is reached.
Background PDFs
Two limits apply to background PDFs ("async": true) and batch items, checked when the work is queued:
| Limit | Per plan | details.limit |
|---|---|---|
| Background PDFs waiting or rendering at once | Free 50, Starter 1,000, Growth 2,000, Pro 5,000 and Scale 10,000 | open_jobs |
| Test-mode background PDFs queued in 24 hours | Free 100, Starter 2,000, Growth 4,000, Pro 10,000 and Scale 20,000 | test_jobs_per_day |
A request over either limit gets 429 rate_limited with details.limit and details.max. A whole batch is checked at once: a batch that would go over the limit is refused, not cut short. These answers carry no Retry-After, because the limit frees up as work finishes (or, for test PDFs, as the 24 hours pass): wait for some PDFs to finish, check the batch's progress, or use a live key for large runs.
Webhook test events and retries
Sending a test event (POST /v1/webhooks/:id/test) and retrying a delivery by hand are limited to 10 a minute per endpoint, because each one sends a request to your server. Over it, you get 429 rate_limited with details.reason set to webhook_sends and a Retry-After. Regular deliveries are not limited this way; CastPDF sends at most 2 deliveries to one endpoint at a time and retries failed ones on its own schedule (webhooks).
Other 429 codes
Two other codes use the 429 status, but they are not per-minute limits and retrying soon gives the same answer:
email_limit_reached: the request would email more PDFs than allowed: Free 10, Starter 500, Growth 2,000, Pro 5,000 and Scale 10,000 per team in any 24 hours, and 20 a day to the same address. See email delivery.storage_limit_reached: the team's stored PDFs are at the plan's limit. Request the PDF directly ("response": "binary", noIdempotency-Key) or wait for older files to expire (stored PDF limits).
Always check error.code before you retry a 429.
How to back off
- Read
Retry-Afteron every429and wait at least that many seconds before the next attempt. - Without
Retry-After, back off exponentially (1, 2, 4, 8 seconds and so on, with a cap) and add random jitter, so parallel workers do not retry in step. - Watch
x-ratelimit-remaining. When it gets close to zero, slow down before you are refused;x-ratelimit-resetsays when the budget refills. - Limit your own parallelism to the number of PDFs your plan renders at once. A small worker pool with that many workers keeps you under the concurrency limit without any
429. - Retry PDF requests with the same
Idempotency-Key, so a retry after a timeout never makes or counts a document twice. The idempotency page has a retry function in Node.js and Python that does all of this.
If your normal traffic needs more requests per minute or more PDFs at once, a larger plan raises both (pricing).
Next steps
- Errors: every error code and what to do.
- Idempotency: safe retries.
- Troubleshooting: common problems and their fixes.
- Test and live mode: the separate test budget.