Error codes
Updated
Envelope#
{
"success": false,
"error": {
"code": "invalid_file_type",
"message": "Unsupported file type. Accepted types: PDF, JPG, PNG",
"details": { "uploaded_type": "image/gif" }
}
}
Branch on error.code, never on error.message. Codes are stable; wording is not. details is present on some errors only.
Authentication and quota#
| Code | HTTP | Meaning |
|---|---|---|
invalid_api_key | 401 | Missing, malformed, revoked, or inactive key |
expired_api_key | 401 | Key past its expiry date |
usage_limit_exceeded | 402 | Key's request cap is reached |
insufficient_credits | 402 | Remaining credits do not cover this document's pages |
rate_limit_exceeded | 429 | Default limits: 10/min, 100/hour, 1000/day, 10000/month |
too_many_open_jobs | 429 | Maximum 5 unexpired multi-page jobs per key |
Rate-limited responses carry X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix timestamp). Read them instead of guessing a backoff.
Request problems#
| Code | HTTP | Meaning |
|---|---|---|
invalid_request | 400 | Missing a required parameter, e.g. job_id |
invalid_file | 400 | No file uploaded, or the upload failed |
invalid_file_type | 400 | Not PDF, JPG, or PNG. details.accepted_types lists what is allowed |
file_too_large | 413 | Over 10 MB |
invalid_options | 400 | options is not valid JSON, or the form field is misnamed |
unknown_model | 400 | No active prompt template by that name |
page_limit_exceeded | 400 | Over 150 pages |
method_not_allowed | 405 | Wrong HTTP verb |
Processing#
| Code | HTTP | Meaning |
|---|---|---|
extraction_failed | 422 | File accepted, extraction did not succeed |
persist_failed | 500 | Extraction succeeded but storage failed — no credits charged, safe to retry |
server_error | 500 | Unhandled error |
server_busy | 503 | Staging capacity exhausted; retry in a few minutes |
Warning
extraction_failed on the synchronous path still deducts 1 credit. Budget
for this when reconciling usage. On the multi-page path the chunk stays available and is
not charged until it succeeds.Multi-page specific#
| Code | HTTP | Meaning |
|---|---|---|
job_not_found | 404 | No such job for this key. Jobs are key-scoped, so another key's job is indistinguishable from a missing one |
job_expired | 410 | Past the 2-hour TTL, or already finalized. Re-upload |
chunk_in_progress | 409 | Another request holds the claim. Retry shortly; a stuck claim frees after 120 seconds |
chunk_source_lost | 410 | Staged source file is gone. Re-upload |
Retry guidance#
| Situation | Action |
|---|---|
rate_limit_exceeded | Back off until X-RateLimit-Reset |
chunk_in_progress | Retry the same chunk after a short delay |
extraction_failed on a chunk | Retry that chunk job_id — replay is idempotent and not double-charged |
persist_failed | Retry; nothing was charged |
server_busy | Retry after a few minutes |
job_expired, chunk_source_lost | Do not retry — re-upload the document |
insufficient_credits, usage_limit_exceeded | Do not retry — top up first |
Something wrong or missing on this page? Tell us.