Skip to main content

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.

PlanRequests per second
Free1
Starter5
Pro15
Business40

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 limit is 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-After seconds and retry with the same Idempotency-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.