Errors & limits
Every error uses one envelope: a stable type for programmatic handling, a specific code, and a human-readable message.
{
"error": {
"type": "invalid_request_error",
"code": "unsupported_file_type",
"message": "Supported types: PDF, JPEG, PNG, HTML."
}
}Status codes
| Status | type / code | When |
|---|---|---|
| 200 | — | Successful GETs; POST with wait=true that finished in time. |
| 202 | — | POST accepted; extraction continues in the background. |
| 400 | invalid_request_error · missing_file, invalid_base64, invalid_body, invalid_parameter | Malformed body or parameters. |
| 401 | authentication_error · missing_api_key, invalid_api_key | No key, unknown key, or revoked key. |
| 403 | authentication_error · insufficient_scope | A read-only key called a write endpoint. |
| 404 | not_found · invoice_not_found, route_not_found | Unknown id (or an id on another account) or path. |
| 405 | invalid_request_error · method_not_allowed | Wrong verb on a known path. |
| 413 | invalid_request_error · file_too_large | Over 15 MB. |
| 415 | invalid_request_error · unsupported_file_type | Not PDF/JPEG/PNG/HTML. |
| 409 | api_error · idempotency_in_flight | A request with the same Idempotency-Key is still processing. |
| 422 | invalid_request_error · file_url_unreachable | file_url could not be fetched. |
| 429 | rate_limit_error · rate_limited, or quota_exceeded · monthly_quota_reached | Per-key rate limit (respect Retry-After) or the account's monthly plan quota. |
| 500 | api_error · internal | Our fault. Safe to retry with backoff. |
Rate limits
60 requests per minute per API key. Every authenticated response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (unix seconds) so clients can back off before hitting 429; 429 responses also include Retry-After. Rate limits protect the platform; they are separate from your plan's monthly extraction quota, which counts extractions across all channels (app, email, API). Check remaining quota anytime with GET /v1/me.
Idempotency & retries
Make POST /v1/invoices safe to retry by sending an Idempotency-Key header (any unique string up to 255 characters; a UUID works well). Retrying with the same key within 24 hours replays the original response (marked with an Idempotency-Replayed: true header) instead of ingesting the document again. If the original request is still processing, the retry gets 409 idempotency_in_flight; wait a moment and try again. Without the header, a retried create ingests twice (duplicate detection flags exact vendor + invoice number + amount matches, but sending a key is the safer path). GETs retry freely.
import { randomUUID } from "node:crypto";
await client.invoices.extract({
fileUrl: "https://example.com/invoices/june.pdf",
idempotencyKey: randomUUID(),
});