Document Conversion — Errors and Limits¶
Applies to /api/v1/pdf_handler/. See Errors for the
platform-wide status reference.
Status codes to handle¶
POST /convert/ and POST /convert/json/¶
| Status | error |
Cause | Retry? |
|---|---|---|---|
202 |
— | Accepted; poll GET /conversion/{id}/. |
— |
400 |
target_formats is required |
No target_formats and no options. |
Fix the request. |
400 |
unsupported_output_format |
An output format the service does not produce. formats lists the offenders. |
Fix the request. |
400 |
unsupported_conversion |
The pair is not available. input_format and output_formats name it. |
Fix the request. |
400 |
Unsupported input file format: … |
The input extension is not accepted. | Fix the request. |
400 |
unknown_engine |
The engine you forced does not exist. |
Fix the request. |
400 |
Provide either a file, an s3_key, or a url |
No input source. | Fix the request. |
400 |
Provide only one of file, s3_key, or url |
More than one input source. | Fix the request. |
400 |
file_content_mismatch |
The bytes do not match the extension. claimed and detected say what was found. |
Fix the file. |
402 |
anonymous_quota_exceeded |
Daily free allowance used up. limit, used_today, and remaining are included. |
Tomorrow, or sign in. |
402 |
Insufficient balance / Insufficient credit |
Signed in, no credits left. | After topping up. |
413 |
File size exceeds the NMB limit for … |
Upload over the cap for your tier. | No — send a smaller file. |
503 |
quota_check_failed |
The quota service could not be reached; the request was refused rather than risk a free ride. | Yes, shortly. |
A rejected request never creates a conversion and never spends a credit.
GET /conversion/{id}/¶
| Status | Cause |
|---|---|
200 |
Status returned. Read status, then download_url. |
404 |
Unknown or removed conversion id. |
A failed conversion is still a 200 — the failure is in the body
(status: "failed", error_message), not in the status code. Check both.
GET /{id}/download/¶
| Status | Cause |
|---|---|
200 |
The file, streamed, with a Content-Disposition filename. |
302 |
Redirect to the file at your cloud destination. Follow it. |
400 |
file not ready — the conversion has produced no output yet. |
404 |
Unknown, removed, or purged conversion. |
500 |
Storage read failed. Safe to retry. |
POST /save/¶
| Status | Cause |
|---|---|
201 |
Saved and delivered. |
400 |
file_or_file_base64_required, invalid_base64, or missing_<provider>_token. |
Idempotency¶
POST /convert/ and POST /convert/json/ accept an optional Idempotency-Key
header. Send a UUID v4 that stays stable across retries of the same intent:
| Situation | Result |
|---|---|
| First request with a key | 202 — the conversion is created as normal |
| Retry with the same key and the same payload | 202 — returns the original conversion, no second job, no second charge |
| Same key, different payload | 422 Idempotency-Key reused with a different payload |
| No header at all | Unchanged behaviour — every call creates a new conversion |
The header is optional. If you omit it, a blind retry after a timeout can create a
second conversion, so either send a key or poll GET /history/ before resubmitting.
Keys are scoped to the caller, so two different accounts may use the same key value without colliding.
File size¶
| Caller | Maximum upload to /convert/ |
|---|---|
| Anonymous (free tier) | 5 MB |
| Signed in | 25 MB |
These are the platform defaults and can be raised by the operator, up to
500 MB. Over the limit you get 413 with the effective limit stated in the
message; nothing is stored and no credit is spent.
The cap applies to the multipart upload itself. A url or s3_key input is
not measured against it.
Credits¶
- Anonymous callers get 10 credits per day, counted per device. Each
requested output format costs one credit, so a single
pdf,docxrequest costs two. - Signed-in callers spend from their balance, one credit per requested format by default. See Billing.
Retention¶
Converted and saved files are deleted from storage on a schedule. The history
row — filename, formats, status, timestamps — is kept, so an expired
conversion still appears in GET /history/, but downloading it no longer
returns a file.
| Caller | Files kept |
|---|---|
| Anonymous (free tier) | 24 hours after the conversion reaches a terminal state |
| Signed in | 180 days after the conversion was created |
The purge runs hourly, so deletion happens shortly after the window closes, not to the second. These are the default windows and are operator-configurable — check your plan if you depend on a specific figure. Download anything you need to keep, or send it straight to a cloud destination, where indox's retention does not apply.