Errors & limits
One error shape everywhere, a fixed set of codes, and clear limits. Every
response carries a request_id — quote it and we can find your
exact request in seconds.
Error envelope
Every error response has the same body:
{ "error": { "code": "...", "message": "...", "request_id": "..." } }
The same value is also on the X-Request-Id response header.
Always log request_id when reporting an issue.
Error-code reference
code | HTTP | Meaning |
|---|---|---|
missing_api_key | 401 | No X-API-Key header sent. |
invalid_api_key | 401 | Key unknown, revoked, or partner suspended. |
invalid_file_type | 400 | Uploaded file isn't a PDF. |
file_too_large | 413 | PDF exceeds the 50 MB limit. |
empty_file | 400 | Uploaded file has zero bytes. |
unsupported_price_list | 400 | price_list isn't a currently-loaded list. The message includes the supported set. |
unsupported_output_format | 400 | output_formats contains something other than esx or json. |
feature_not_enabled | 400 | Requested "json" output while it's disabled (current production state). |
too_many_active_jobs | 429 | You already have 3 jobs queued/processing. Wait for one to finish. |
daily_limit_exceeded | 429 | You've hit 200 conversions today. |
not_found | 404 | Job doesn't exist, or belongs to another partner. |
timeout | job error.code | Job stuck in processing past 20 minutes; force-failed. |
unsupported_estimate | job error.code | PDF parsed to zero line items — unsupported layout (abbreviated, rough-draft-only, XactView, statement-of-loss). |
conversion_failed | job error.code | Generic unexpected failure. Check error.message and quote job_id. |
Codes marked "job error.code" appear inside a job's
error object (from
GET /v1/conversions/{id}),
not as an HTTP status — the request itself succeeded, the conversion didn't.
Rate limits
Per API key:
- 3 concurrent jobs (
queuedorprocessing). A 4thPOSTreturns 429too_many_active_jobs. - 200 conversions per day (UTC calendar day). Exceeding
returns 429
daily_limit_exceeded.
Need different limits? Tell us about your integration.
Retention
- Source PDFs and generated ESX files are retained 30 days from job completion, then deleted.
-
After that,
GET /v1/conversions/{id}still returns the job record —summarystays available indefinitely — butesx_urlisnullandesx_expiredistrue. Download your ESX before the window closes if you need to keep it.
Idempotency
Pass Idempotency-Key: <your-key> on
POST /v1/conversions.
A second POST with the same key from the same partner, within the retention
window, returns the same job_id instead of
creating a new job or re-uploading the PDF. Use it to make retries after a
network timeout safe.
Debugging with request_id
Every response under /v1/* — success or error — carries
X-Request-Id, and error bodies repeat it inline as
error.request_id. When something looks wrong, include that ID
(and the job_id if you have one) in your report. It points
straight to your request in our logs.
The service exposes an interactive Swagger UI at
/docs and the raw
OpenAPI schema
at /api/v1/openapi.json on the same host — handy for generating a
typed client.