Error envelope
Every error response uses the same shape:code- stable, machine-readable; branch on this.message- human-readable.details- present on validation errors (400); the offending fields and why they failed.
Common codes
| HTTP | code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | Request body/params failed validation (see details). |
| 401 | UNAUTHORIZED | Missing, invalid, revoked, or expired API key. |
| 402 | INSUFFICIENT_CREDITS | Not enough credits for this generation. |
| 402 | SUBSCRIPTION_REQUIRED | Your plan can’t perform this action. |
| 404 | NOT_FOUND | The job, style, or upload doesn’t exist (or isn’t yours). |
| 429 | RATE_LIMIT_ERROR | Per-key rate limit hit - see Rate limits. |
| 429 | CONCURRENT_LIMIT_REACHED | Too many generations in flight at once. |
| 429 | USAGE_LIMIT_EXCEEDED | Usage allowance exceeded for the period. |
| 500 | INTERNAL_ERROR | Unexpected server error - safe to retry with backoff. |
Job failures vs request errors
The errors above are returned by the HTTP request itself. A generation that starts but fails returns200 with a terminal job whose status is failed:
error.code is one of GENERATION_FAILED or EXTRACTION_FAILED.
Handling guidance
402→ top up credits / upgrade before retrying.429→ honorRetry-Afterand back off (Rate limits).400→ fix the request usingdetails; don’t blind-retry.5xx→ retry with exponential backoff (use an Idempotency-Key on creates).

