Error codes
Every error response carries a machine-readable code, a human explanation, a request_id and a
link back to this documentation.
{ "error": {
"code": "insufficient_credits",
"message": "Account balance is $0.0031, this operation requires $0.0400.",
"type": "billing_error",
"doc_url": "https://justapi.tech/docs/errors/insufficient_credits/",
"request_id": "req_01JBX..." } }
Switch on type, not on code
code names the exact failure. type is its family — and it is what your integration should branch
on, so you do not have to hard-code all twenty-four codes to know whether retrying makes sense.
type |
What to do |
|---|---|
invalid_request_error |
Fix the request. Retrying it unchanged will fail again |
authentication_error |
Check the key |
authorization_error |
The key or account lacks permission. Do not retry |
billing_error |
Top up the balance, or raise that key's spend cap |
not_found_error |
The resource is not there. Do not retry |
conflict_error |
Retry with a different Idempotency-Key, or with the original body |
rate_limit_error |
Wait for Retry-After, then retry |
processing_error |
The file is the problem, not the request |
api_error |
Ours. Retry with backoff — you were not charged |
Every code below links to its own page, with what to do about it and the exact response shape.
Client errors
| Code | HTTP | Cause | Charged? |
|---|---|---|---|
invalid_request |
400 | Malformed body or bad parameter | No |
invalid_source |
400 | Zero or more than one of url/base64/file_id | No |
unsupported_format |
400 | That format is not supported by this endpoint | No |
sync_not_available |
400 | Too large for sync; response suggests async | No |
invalid_api_key |
401 | Key is wrong, revoked or does not exist | No |
insufficient_credits |
402 | Balance too low for this operation | No |
spend_limit_exceeded |
402 | This key hit its own spend cap. The account may still have credit — raise the key's limit instead of topping up | No |
account_suspended |
403 | Unpaid after the dunning sequence | No |
scope_denied |
403 | Key is not scoped to this service | No |
not_found |
404 | No such route | No |
job_not_found |
404 | Unknown job id | No |
file_not_found |
404 | Unknown file id | No |
file_expired |
404 | Result passed its retention window | No |
method_not_allowed |
405 | Route exists but not for that HTTP method; see the Allow header |
No |
idempotency_conflict |
409 | Same key reused with a different body | No |
file_too_large |
413 | Over your plan limit | No |
unsupported_media_type |
415 | Not a valid PDF or image | No |
Processing errors
| Code | HTTP | Cause | Charged? |
|---|---|---|---|
processing_failed |
422 | File is corrupt or unprocessable | No |
encrypted_pdf |
422 | Needs a user password; send options.password |
No |
signed_pdf |
422 | Compressing would invalidate a digital signature | No |
conformance_break |
422 | Would break PDF/A or PDF/X conformance | No |
Rate and capacity
| Code | HTTP | Cause | Charged? |
|---|---|---|---|
rate_limited |
429 | Too many requests; see Retry-After |
No |
internal_error |
500 | Our fault | No |
capacity_unavailable |
503 | All providers saturated; retry shortly | No |
You are never charged for any error on this page. Billing only ever happens on a successful operation that produced a smaller file.