Guides
Errors
Every Cheaper Gen. API error code and HTTP status — 400, 401, 402, 404, 409, 429, 502, 503 — with causes and how to handle or retry each one.
The API uses conventional HTTP status codes: 2xx means success, 4xx means the request needs to change, and 5xx means something failed on our side or upstream. Every error response has the same JSON shape, so you can handle them in one place.
Error format#
{
"error": {
"code": "invalid_request",
"message": "\"resolution\" must be one of: 480p, 720p",
"param": "resolution"
}
}code— a stable, machine-readable string. Branch on this, not on the message.message— a human-readable explanation, safe to log. Wording may change.param— present when a specific request field caused the error.
Error codes#
| HTTP | code | Meaning | What to do | Retry? |
|---|---|---|---|---|
| 400 | invalid_request | The body is not valid JSON, a required field is missing, a value is out of range or not allowed, the body contains an unknown parameter, a prompt mention points at nothing, or a reference video / audio file is unreadable or outside the model's limits (duration, size, resolution, frame rate). param names the offending field. | Fix the request. The message says exactly what is wrong. | After fixing |
| 401 | unauthorized | The API key is missing, malformed or revoked. | Check the Authorization: Bearer header and that the key is active. | After fixing |
| 402 | insufficient_balance | Your balance does not cover the price of this generation. Nothing was charged. | Add credits or enable auto top-up in Dashboard → Billing. See Billing. | After fixing |
| 404 | not_found | The endpoint ID, model or generation does not exist — or the generation belongs to another account. | Check the ID against the model list. | No |
| 409 | conflict | The request conflicts with the resource's state — typically canceling a generation that is already running or finished. | Read the generation to see its current status. | No |
| 429 | rate_limited | Too many requests per second, or too many generations in progress at once. | Back off and retry. See Rate limits. | With backoff |
| 500 | internal_error | Something went wrong on our side. | Retry with backoff. If it persists, contact support with the time of the request. | With backoff |
| 501 | direct_upload_unavailable | Direct (presigned) uploads are not available on this deployment. | Upload with multipart/form-data instead. See Uploads. | No |
| 502 | upstream_error | Model capacity returned an error we could not recover from. | Retry with backoff. Any held amount is refunded. | With backoff |
| 503 | model_unavailable | The model is temporarily not accepting new generations. | Retry later or switch model. Check the status page. | With backoff |
Errors inside a generation#
An HTTP error means the request itself was rejected and nothing was charged. A generation that was accepted but later fails is different: the API call succeeded, and the generation's status becomes failed with an error object. Its hold is refunded automatically. See Failures and refunds for those codes.
Retrying safely#
Retry 429, 500, 502 and 503 with exponential backoff and jitter. Don't retry other 4xx errors unchanged — they will fail the same way.
Submits are not idempotent
Each successful POST /run/{endpoint} creates and bills a new generation. If a submit times out on the network and you don't know whether it was accepted, check your recent generations before sending it again.
import random, time, requests
RETRYABLE = {429, 500, 502, 503}
def post_with_retry(url, headers, json, attempts=5):
for attempt in range(attempts):
resp = requests.post(url, headers=headers, json=json, timeout=30)
if resp.status_code not in RETRYABLE:
break
# exponential backoff with jitter: ~1s, 2s, 4s, 8s…
time.sleep(min(2 ** attempt, 30) + random.random())
if not resp.ok:
err = resp.json()["error"]
raise RuntimeError(f"{resp.status_code} {err['code']}: {err['message']}")
return resp.json()Validation examples#
Validation messages name the field and the allowed values, so they can be shown to developers as-is:
[
{
"error": {
"code": "invalid_request",
"message": "\"prompt\" is required",
"param": "prompt"
}
},
{
"error": {
"code": "invalid_request",
"message": "Unknown parameter \"resoluton\"",
"param": "resoluton"
}
},
{
"error": {
"code": "not_found",
"message": "Unknown endpoint \"bytedance/seedance-2.0-fast/text-to-gif\". See GET /api/v1/models."
}
}
]You can also price a request without running it: POST /api/v1/quote/{endpoint} validates the price-relevant fields and returns the cost. See the API reference.
Something unclear or missing? Email support@cheapergen.com — we read everything.