Errors and retries
The error envelope, every code, what is charged, and how to retry.
Every error has one shape:
{ "error": { "type": "billing_error", "code": "insufficient_credits", "message": "…", "doc_url": "https://sfwapi.dev/docs/errors#insufficient_credits", "request_id": "01JD…" } }Failed requests are never charged.
| HTTP | code | Retry? |
|---|---|---|
| 400 | invalid_request, url_not_allowed | No, fix the request |
| 401 | invalid_api_key | No |
| 402 | insufficient_credits | After a top-up |
| 403 | key_revoked, account_suspended, email_not_verified | No |
| 413 | media_too_large (details.limit_bytes) | No |
| 415 | unsupported_media | No |
| 422 | download_failed, decode_failed | Fix the file or URL |
| 429 | rate_limited, concurrency_exceeded, quota_exceeded | Yes, honour Retry-After |
| 502 | upstream_internal | Yes, with backoff |
| 503 | unavailable, overloaded, bandwidth_limited | Yes, honour Retry-After |
| 504 | timeout | Once, with a smaller file or fewer frames |
Retry 429, 502, 503 and 504 with exponential backoff and jitter (the SDKs do this twice by default, except for uploads). Everything else is a request problem.
Codes in detail
url_not_allowed: only public http(s) URLs; addresses that resolve to private, loopback or link-local ranges are refused.download_failed: the host did not answer, answered 4xx/5xx, or redirected too often.decode_failed: the bytes are not a decodable image, GIF or video.bandwidth_limited: large media and video are paused while the service is near its monthly traffic allowance; images under 10 MB keep working.quota_exceeded: the anonymous playground allowance for today is used up.