rate_limited
HTTP 429 · code: "rate_limited" · type: https://docs.formfeed.dev/errors/rate-limited
The workspace sent more requests that start work than its plan allows per second. Nothing was done. limit is the size of the bucket that ran out and window_seconds the time it refills over (1 for the plan's requests per second, 60 for the minute of a Free test key), and the answer carries Retry-After (seconds to wait) and the RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers.
The limit is a token bucket per workspace, shared by all its keys, live and test: its size and its refill per second are the plan's requests per second, so a short burst up to that size passes.
| Plan | Requests per second |
|---|---|
| Free | 1 |
| Starter | 5 |
| Pro | 15 |
| Business | 40 |
Test keys on the Free plan have a second bucket of 30 requests per minute.
Example
{
"type": "https://docs.formfeed.dev/errors/rate-limited",
"title": "Rate limited",
"status": 429,
"detail": "Too many requests for the starter plan; retry in 1s",
"code": "rate_limited",
"limit": 5,
"window_seconds": 1,
"request_id": "req_4b7c1e9a2f3d4c5b8a6e7f9012"
}
Retry-After: 1
RateLimit-Limit: 5
RateLimit-Remaining: 0
RateLimit-Reset: 1
Common causes
- A loop that renders documents one by one as fast as it can.
- Several services sharing one workspace.
- Free test keys sending more than 30 requests in a minute (then
limitis 30).
Only requests that start work count: POST to /v1/renders and /v1/renders/batch, the PDF tools under /v1/pdf/, the apitemplate.io-compatible endpoints, and saving or testing a storage connection. Reads such as GET /v1/renders/{id} are not limited, so polling never trips it.
How to fix it
- Wait
Retry-Afterseconds and retry with the sameIdempotency-Key. The SDKs do this for you. - Send many documents as one batch instead of many single requests: up to 1,000 documents in one request.
- See Limits and usage for the limits per plan.
Retry?
Yes, after the number of seconds in Retry-After, with the same Idempotency-Key. This answer is given before the key is looked at and is not stored, so the retry really runs.