Errors
All non-2xx responses are JSON with at least an error field.
{ "error": "quota_exhausted" }| HTTP | error | Meaning |
|---|---|---|
| 400 | invalid_input | Body or query missing/invalid (details usually in hint or details). |
| 400 | unsupported_or_corrupt_image | The bytes don't decode as a supported image. |
| 400 | unsupported_format | Decoded fine, but the format isn't in the allowed set. |
| 400 | invalid_url / unsupported_scheme / blocked_private_address / dns_resolution_failed | SSRF guards on /v1/shrink-from-url. |
| 400 | invalid_widths | ?widths=… empty or out of bounds (16…8192, up to 6). |
| 401 | missing_token / invalid_token / missing_api_key / invalid_api_key | Auth issue. |
| 402 | quota_exhausted | Free quota and token balance both at 0. |
| 403 | account_blocked / forbidden | Account blocked by admin or admin token invalid. |
| 404 | job_not_found | GET /v1/jobs/:id for a job that doesn't belong to your account. |
| 409 | email_taken / no_customer / no_active_subscription | State conflict; body explains. |
| 410 | expired / already_used | Verify/reset token used or past TTL. |
| 413 | file_too_large | Upload over MAX_UPLOAD_BYTES (25 MB by default). |
| 429 | (rate-limit body) | Slow down; honor Retry-After. |
| 500 | (Fastify default) | We log and Sentry these — retry with backoff. |
| 501 | stripe_not_configured | Server has no Stripe keys configured. |
| 503 | (readiness body) | GET /ready returns 503 when Postgres or Redis is unreachable. |
| 504 | (Traefik default) | Upstream timeout — guetzli on big JPEGs can exceed 30 s; use ?async=true and poll. |