Errors
One shape, stable codes. The docs_url in any error response links straight to its heading below.
{ "error": {
"code": "quota_exceeded",
"message": "Monthly render quota reached (100/100). …",
"docs_url": "https://pdfcraft.dev/errors#quota_exceeded"
} }| HTTP | Code | Billed? |
|---|---|---|
| 400 | invalid_request | free |
| 401 | invalid_api_key | free |
| 402 | payment_required | free |
| 404 | not_found | free |
| 408 | render_timeout | free |
| 422 | render_failed | billed |
| 429 | rate_limited | free |
| 429 | quota_exceeded | free |
| 415 | unsupported_file | free |
| 422 | extraction_failed | free |
| 500 | internal_error | free |
What counts as billable
A render is billed if Chromium actually ran. Successes count, and so does render_failed — the browser did the work, your HTML is what broke. Timeouts, our own failures, and every 4xx that never reached the browser are free.
invalid_request · HTTP 400 · free
Both html and url were supplied, or neither, or an option is out of range.
Read the message — it names the offending field. Send exactly one of html or url.
invalid_api_key · HTTP 401 · free
The Authorization header is missing, malformed, or the key has been revoked.
Send "Authorization: Bearer sk_live_…". Issue a fresh key from the dashboard.
payment_required · HTTP 402 · free
The last subscription payment failed, so the account is past_due.
Update the card on the billing portal. Rendering resumes the moment payment clears.
not_found · HTTP 404 · free
No render with that id belongs to your account.
Check the id. Ids from another account also return 404, never 403, so they cannot be probed.
render_timeout · HTTP 408 · free
The page did not finish within timeoutMs and was force-closed.
Raise timeoutMs (max 120000), or relax waitFor — networkIdle on a page with a long-poll never settles.
render_failed · HTTP 422 · billed
Chromium ran but the page threw, navigation failed, or waitFor.selector never appeared.
Load the same HTML in a browser. This one is billable because the browser did the work.
rate_limited · HTTP 429 · free
More requests per second than the plan allows.
Honour the Retry-After header and back off. Paid plans allow 20 req/s.
quota_exceeded · HTTP 429 · free
A free-plan account has used all 100 renders in the current period.
Upgrade, or wait for resets_at from GET /v1/usage. Paid plans never hard-stop; they accrue overage.
unsupported_file · HTTP 415 · free
The file is not a PDF, is password-protected, or is corrupt beyond parsing.
Send an unencrypted PDF. Decrypt it first — we deliberately do not accept passwords, so we never hold one.
extraction_failed · HTTP 422 · free
The PDF parsed but carries no text layer, so there is nothing to extract. Usually a scan or a photo.
Check pages_without_text in the response. A scanned document needs OCR, which this endpoint does not do.
internal_error · HTTP 500 · free
Something on our side broke.
Retry with backoff. Never billed. If it persists, send us the render id.