Guides
Branch on one error shape and know which codes to retry.
Every error has one shape:
{
"error": {
"type": "invalid_request_error",
"code": "invalid_duration",
"message": "duration must be an integer between 4 and 30 seconds for seedance-2.5",
"param": "duration",
"request_id": "0b7c6f1e-2d4a-4e8b-9c3f-5a1d7e2b8c40"
}
}type: the family to branch on.code: the stable reason.param: the field at fault, when there is one.request_id: matches X-Request-Id. Quote it to support.message is for your logs. Don't parse it.
Status · type | code | When | Retry |
|---|---|---|---|
400 invalid_request_error | invalid_json, invalid_body, invalid_type, invalid_enum, too_long and the field codes | The request doesn't validate. param names the field; invalid_json, invalid_body and invalid_idempotency_key have none. | No. Fix the request. |
401 authentication_error | invalid_api_key | The key is missing, unknown, revoked or expired. | No. |
402 insufficient_ink_error | insufficient_ink | Your balance can't cover the hold; message gives the Ink required and available. | After a top-up. |
402 insufficient_ink_error | key_ink_limit_reached | The hold would cross the key's Ink cap. | After raising the cap. |
403 permission_error | tier_gated | Your plan doesn't include video: it needs a paid plan or pay-as-you-go Ink. | After an upgrade or a top-up. |
404 not_found_error | not_found, model_not_found, generation_not_found | Unknown route, model or generation. | No. |
404 not_found_error | external_api_disabled | The API isn't open yet. | Later. |
413 invalid_request_error | payload_too_large | The body is over 64 KiB. | No. |
415 invalid_request_error | unsupported_media_type | The body isn't application/json. | No. |
429 rate_limit_error | rate_limit_exceeded, concurrency_limit, daily_cap_reached | A rate limit was reached. | After Retry-After. |
500 server_error | internal_error | Loopling failed. | Yes, with backoff. |
502 provider_error | provider_error | The provider refused the request. Not charged. | Read message first; retry with a new key. |
503 provider_error | provider_unavailable | Video generation is paused or busy, or couldn't be admitted. Not charged. | After Retry-After; with a new key if it couldn't be admitted. |
503 server_error | rate_limiter_unavailable | The rate limiter is unreachable. | After Retry-After. |
Download codes (generation_not_ready, result_unavailable, result_expired) are on Download a generation.
A generation that fails after 202 doesn't answer with an error status. It reads status: "failed", with the reason in error and ink_charged: 0:
{
"id": "vgen_01J9X6K3M8Q2",
"object": "video.generation",
"status": "failed",
"model": "seedance-2.5",
"created_at": "2026-10-04T08:12:44Z",
"completed_at": "2026-10-04T08:13:10Z",
"ink_reserved": 320,
"usd_payg_reserved": 3.2,
"ink_charged": 0,
"usd_payg_charged": 0,
"request": {
"model": "seedance-2.5",
"prompt": "A paper lantern drifting over a night harbour, slow dolly in",
"aspect_ratio": "16:9",
"resolution": "1080p",
"duration": 5,
"generate_audio": true
},
"error": {
"code": "provider_failed",
"message": "The render did not complete."
}
}What each reason means: Get a video generation.
429 and 503: wait at least Retry-After seconds, then retry.500: retry with exponential backoff.4xx: fix the request before you resend it.Idempotency-Key with every submit, so a retry never starts a second render. See Idempotency.502 provider_error, or a 503 provider_unavailable saying the generation couldn't be admitted (Retry-After: 5), arrives after the hold was taken. Your Idempotency-Key is already bound to a failed generation, and resending with it only replays that failure: 202, Idempotent-Replayed: true, status: "failed". Retry those with a new key.loopling.ai · grows with you, grows itself
hello@loopling.ai · for agents · community · pricing · Enterprise · API · privacy · terms