Every error a Kling API call can return, what usually causes it, and what to do
Updated 2026-10-02
Errors from a Kling call arrive in two places, and confusing them is the most common debugging mistake. A request can be refused at creation with an HTTP error status, or it can be accepted and later fail, in which case the HTTP response was fine and the failure lives in the job's status and error fields. This page covers both, mapped to the situations you meet when calling Kling v3 (kling-v3.0-std, -pro, -4k), Kling O3 and Motion Control through VideoRouter.
The envelope
Every error uses the same OpenAI-style body, whichever host was involved:
{"error": {"message": "...", "type": "invalid_request_error", "code": "invalid_api_key"}}
code is optional, so branch on the HTTP status first and use the code to refine. The statuses below come from VideoRouter's error documentation; the "Kling causes" column is where that table meets the Kling-specific rules documented elsewhere.
Status codes mapped to Kling causes
| Status / code | Meaning | Typical Kling-side cause | Action |
|---|---|---|---|
400 invalid_request_error | Bad request or unknown model id | A mistyped tier (kling-v3-std instead of kling-v3.0-std); a model id not in the catalog, which includes any Kling 4.0 id because that model is not available on any host yet; start_image_url sent to a model that has no image input; end_image_url sent at all (documented as rejected on every model); a start image combined with reference arrays on a non-Motion-Control model; input_video_url without a prompt on an editing model; too many reference items | Fix the request. Never retry. |
401 invalid_api_key | Key missing, malformed, revoked or expired | Header typo, or an expired key in a deployed environment | Alert; never retry. |
402 spend_cap_exceeded | This key's monthly cap reached | A loop over many prompts on kling-v3.0-4k hit a cap you set | Stop. Raise the cap or wait. |
402 insufficient_credits | Organisation balance is empty | Balance ran out during a batch | Top up, then resume. |
403 model_not_allowed | Model not in this key's allow-list | The key was restricted to Std and your code asked for Pro | Fix key policy or the model id. |
| 429 rate limit | Per-key requests per minute exceeded | Too many parallel submissions | Sleep for Retry-After, then retry. |
500, 502, 503, 504 upstream_error | Every candidate in the chain failed | All hosts serving that Kling tier were failing or rejecting | Retry with backoff. Not billed. |
The "unknown model" 400 deserves its own check
Because Kling tiers are separate model ids, a wrong string is the cheapest bug to make and it fails immediately, before billing. GET /v1/videos/models returns the live list of valid slugs and is unbilled; call it in a health check or when you see an unexpected 400. If you are waiting for Kling 4.0, an id for it will keep returning 400 until it appears in that list, which has not happened yet.
Single-host models have less fallback
The documentation describes Motion Control as served by one provider, with no cross-provider failover pool. A 5xx there means the one host failed, not "every host in a pool", so do not expect automatic rescue. The documented behaviour for failed upstream jobs is that they are not billed, so a bounded retry is reasonable.
Accepted, then failed
A job whose creation returned 202 can still end with status: "failed". The HTTP status of the poll is a normal 200; the failure is in the body, where error carries the provider's message and data is null. For Kling the causes you can verify are mostly input problems: a start image URL the host cannot fetch (expired presigned links are the classic), or a reference video the host cannot read. Test with a short clip first, as the Motion Control guide recommends, so those surface cheaply. The docs state that a job failing because every upstream host failed is not billed, which is what makes a retry after failed reasonable, in contrast to a retry after a lost connection.
Handling code
import time, random, requests
BASE = "https://videorouter.sh/api/v1"
H = {"Authorization": "Bearer llmr_sk_live_..."}
class Permanent(Exception): pass # fix the request or the key
class Transient(Exception): pass # safe to retry
class Ambiguous(Exception): pass # create may have succeeded
def create(payload, attempts=4):
for n in range(attempts):
try:
r = requests.post(f"{BASE}/videos", headers=H, json=payload, timeout=30)
except (requests.ConnectionError, requests.Timeout):
raise Ambiguous("connection lost on create") # do NOT loop here
if r.ok:
return r.json()
err = r.json().get("error", {}) if r.content else {}
if r.status_code in (400, 401, 402, 403):
raise Permanent(f"{r.status_code} {err.get('code')}: {err.get('message')}")
wait = float(r.headers.get("Retry-After") or 0) if r.status_code == 429 else 0
time.sleep(max(wait, min(30, 2 ** n)) + random.random())
raise Transient("still failing after retries")
def finish(job_id, deadline_s=900):
end = time.monotonic() + deadline_s
while time.monotonic() < end:
j = requests.get(f"{BASE}/videos/{job_id}", headers=H, timeout=30).json()
if j["status"] == "completed":
return j["data"][0]["url"]
if j["status"] == "failed":
raise Permanent(f"job failed: {j['error']}") # read the message first
time.sleep(5)
raise TimeoutError(job_id) # slow is not failed: keep the id
A triage order that saves time
- Was it HTTP 4xx at creation? Read
code; it is your request, key or budget. - Was it 5xx? Wait and retry; nothing was billed.
- Did creation succeed and the job later fail? Read
error.message, check the image or video URL is reachable from outside your network, and retry once. - Did the file arrive but look wrong in size? That is not an error at all: an unsupported resolution or aspect ratio is ignored rather than rejected. See the duration and aspect-ratio guide.
The general retry policy that applies to any model is in the Python tutorial, and the curl equivalent is in the curl walkthrough. Create a key and set a monthly cap so the 402 you eventually see is one you chose.
Frequently asked questions
Is a Kling job that fails billed?
VideoRouter documents that a job failing because every upstream host failed is not billed. Charges are made once at creation for jobs that are accepted, so check the job status and error message before assuming either way.
Why do I get a 400 for a Kling model id?
The id is not in the catalog. Kling tiers are separate ids (kling-v3.0-std, kling-v3.0-pro, kling-v3.0-4k), and no Kling 4.0 id is available yet. GET /v1/videos/models lists valid slugs.
Which Kling errors should never be retried?
400, 401, 402 and 403. They need a change to the request, key, budget or key policy. Retry 429 after Retry-After and 5xx with backoff.
Keep reading
- Kling v3.0 Std vs Pro vs 4K — Which Tier Should You Use?
- Kling Image-to-Video API: Animate a Start Image (Code)
- Kling Motion Control API: Reference Video + Character Image
- Kling O3 (Omni) vs Kling v3: Which Model Should You Call?
VideoRouter puts it next to dozens of other video and image models behind one API key, so you can compare providers, prices and fail over automatically. Compare providers on VideoRouter →