Docs menu
API referenceErrors and quota
Errors and quota
The stable error codes, what each one means, and how the monthly quota is counted.
This page is the code reference: what each refusal means and what to do about it. The numbers behind the refusals are on Limits: what each plan allows, how a document is measured and what a refusal costs.
Error format#
Every error is a JSON body with at least two fields. code is the stable contract: it never changes meaning, so script against it. error is a sentence for people and may be reworded at any time. A refusal against a limit (413 payload_too_large, 422 too_many_resources, 422 too_many_pages, 429 rate_limited and 422 render_timeout) adds limit: the limit's name, the value that was exceeded and the plan it came from, so a client reads the number without parsing the sentence. The names are the plan columns Limits lists. One refusal in that set carries no limit: a 413 with reason preparation_limit, where the renderer, not the plan, set the bound. Treat limit as optional and read reason when it is missing. A failed remote resource adds reason and resource_id. When the server knows how long to wait, a Retry-After header gives the number of seconds. Only the codes the table marks send it.
The three shapes, as the document publishes them:
{
"code": "empty_document",
"error": "The request carried no Markdown to render."
}{
"code": "payload_too_large",
"error": "The document, one of its resources or the request as a whole is over a size limit.",
"reason": "markdown_limit",
"limit": {
"name": "markdown_kb",
"value": 256,
"plan": "free"
}
}{
"code": "invalid_request",
"error": "A field of the request was missing, of the wrong type or out of range.",
"reason": "render_options"
}401 responses also carry WWW-Authenticate: Bearer. All API responses, errors included, are sent with Cache-Control: no-store.
Error codes#
The codes below are every value the documented routes can produce, with the fields the envelope carries beside code and error. The sentences are the current wording and may change.
| Status | Code | Meaning | What to do | Also carries | Retry-After |
|---|---|---|---|---|---|
| 401 | unauthorized | The request carried no API key, or one that is no longer valid. | Check the Authorization header and that the key has not been revoked. | None | No |
| 403 | account_blocked | The account is blocked and cannot render or download until support lifts it. | Contact support; retrying answers the same. | None | No |
| 403 | email_unverified | The account's email address has not been verified yet. | Open the link in the sign-up mail or request a new one, then call again. | None | No |
| 400 | invalid_request | A field of the request was missing, of the wrong type or out of range. | Fix the field reason names and send the request again. | reason | No |
| 413 | payload_too_large | The document, one of its resources or the request as a whole is over a size limit. | Send a smaller document, and on preparation_limit make it simpler rather than changing plan. | limit, reason, renderer_bound | No |
| 400 | empty_document | The request carried no Markdown to render. | Send a document with content in it. | None | No |
| 422 | too_many_pages | The rendered document has more pages than were allowed for it: the plan's page limit, or the deployment's ceiling where that is lower. | Split the document. limit.plan names the plan whose limit it was, and a plan with a higher one lifts it; limit.plan null means the deployment capped it below the plan, and only the operator can raise that. | limit | No |
| 422 | too_many_resources | The document references more resources than one render loads. | Reference fewer resources, or split the document into several renders. | limit | No |
| 422 / 503 | resource_fetch_failed | A resource the document references could not be loaded. | Check that the resource resource_id names is reachable, then retry. | reason, resource_id | No |
| 409 | idempotency_in_progress | A render with the same Idempotency-Key is still running. | Wait the Retry-After seconds and retry with the same key. | None | Yes |
| 422 | idempotency_mismatch | The Idempotency-Key was already used for different document or resource content. | Use one Idempotency-Key per document; retrying unchanged answers the same. | None | No |
| 429 | idempotency_replay_limit | The Idempotency-Key has been replayed as often as the plan allows. | Render under a new key, which uses one of the period's documents, or wait for the key to expire. | None | Yes |
| 429 | rate_limited | The account sent more requests, or asked for more concurrent renders, than its plan allows. | Wait the Retry-After seconds and retry, letting the running renders finish first when limit names concurrency. | limit | Yes |
| 402 | quota_exhausted | The account has no documents left for the current period. | Check GET /v1/usage, then upgrade the plan or wait for the next period. | None | No |
| 503 | quota_unavailable | The account's usage could not be checked, so no render was started. | Retry after a short pause; nothing was counted. | None | No |
| 503 | queue_full | The render queue is full; the same request can be sent again shortly. | Retry after the Retry-After delay; the document was returned to your period. | None | Yes |
| 422 | render_timeout | The render ran past the time allowed for it: the plan's time limit, or the deployment's ceiling where that is lower. | Simplify or split the document; retrying unchanged answers the same. limit.plan null means the deployment capped the time below the plan, and only the operator can raise that. | limit | No |
| 503 | render_unavailable | No render capacity was available for this request, or this deployment does not offer what it asked for, such as upload delivery. | Retry after a pause, and report it if it persists. | None | No |
| 422 | render_failed | The document could not be rendered. | Retry once; if it fails again the document is at fault. | None | No |
| 403 | plan_required | The account's plan does not include what the request asked for. | Move to a plan that includes what reason names. | reason | No |
| 400 | invalid_bundle | The certificate bundle in the request could not be read. | Check the bundle's format against what reason says and send it again. | reason | No |
| 400 | certificate_expired | The certificate is past its validity period. | Seal with a certificate that is still inside its validity period. | reason | No |
| 422 | signing_failed | The document was rendered but could not be sealed. | Retry once, and check the certificate if it fails again. | None | No |
| 503 | signing_unavailable | Sealing is temporarily unavailable. | Retry after a pause; reason says what was unavailable. | reason | No |
| 400 | unknown_signing_identity | No stored certificate of this account matches the one the request named. | Name one of the account's stored certificates, or send the bundle in the request. | None | No |
| 404 | not_found | The usage period asked for does not belong to this account, or the path does not exist. | Check the path and, on GET /v1/usage, that period is one of this account's periods. | None | No |
| 405 | method_not_allowed | The path exists but does not answer this HTTP method; the Allow header lists the ones it does. | Use a method the Allow header names. | None | No |
| 424 | upload_failed | The storage at upload_url did not accept the PDF or did not answer in time; status is its HTTP status, or null when no answer arrived. | Check that the pre-signed URL is still valid and signed for Content-Type: application/pdf, then retry with a fresh one; the document was returned to your period. | status | No |
| 500 | internal_error | The request failed for a reason the service could not classify. | Retry after a pause, and report it if it persists. | None | No |
Monthly quota#
Each plan includes a number of documents per period; the counts are on Limits. Periods start each month at 00:00 UTC on the day the account signed up or, after a purchase, on the day of the purchase. A monthly upgrade starts a new period on its day. The counter is transactional in the database, so it is exact under concurrency. It counts only PDFs that were actually served: a document is reserved before the render starts and returned to your period when the render fails, times out, exceeds the page limit, or cannot be dispatched. Which refusals return the document to your period is on Limits.
When the period's documents are used up, /v1/render answers 402 quota_exhausted; the message says the period's documents are used up and links to pricing. Grants add documents to the period. Check credits_used, credits_included, credits_granted and the period boundaries with GET /v1/usage.