Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Avoid common mistakes when using Kling AI API. Use when troubleshooting or learning best practices. Trigger with phrases like 'klingai pitfalls', 'kling ai mistakes', 'klingai gotchas', 'klingai best practices'.
.claude/skills/jeremylongshore-klingai-known-pitfalls/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 11% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 21% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 7% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 41% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 27% | 0% |
Documented mistakes, gotchas, and anti-patterns from real Kling AI integrations. Each pitfall includes the symptom, root cause, and tested fix.
Symptom: 400 Bad Request on valid-looking requests.
python# WRONG -- duration as integer {"duration": 5} # CORRECT -- duration as string {"duration": "5"}
The API requires duration as a string "5" or "10", not an integer.
Symptom: 401 Unauthorized even with correct AK/SK.
python# WRONG -- missing headers parameter token = jwt.encode(payload, sk, algorithm="HS256") # CORRECT -- explicit JWT headers token = jwt.encode(payload, sk, algorithm="HS256", headers={"alg": "HS256", "typ": "JWT"})
Some JWT libraries don't include typ: "JWT" by default. Kling requires it.
Symptom: Works for 30 minutes, then all requests fail with 401.
python# WRONG -- token generated once TOKEN = generate_token() # at module import headers = {"Authorization": f"Bearer {TOKEN}"} # CORRECT -- generate fresh token per request (or auto-refresh) def get_headers(): return {"Authorization": f"Bearer {generate_token()}"}
JWT tokens expire after 30 minutes. Always implement auto-refresh.
Symptom: Script hangs forever on a failed task.
python# WRONG -- infinite loop while True: result = check_status(task_id) if result["status"] == "succeed": break time.sleep(10) # CORRECT -- with timeout and failure check start = time.monotonic() while time.monotonic() - start < 600: # 10 min max result = check_status(task_id) if result["status"] == "succeed": break elif result["status"] == "failed": raise RuntimeError(result["error"]) time.sleep(10) else: raise TimeoutError("Generation timed out")
Symptom: Video URLs return 404 or 403 after a day.
Kling CDN URLs are temporary (24-72 hours). Always download and store on your own infrastructure immediately after generation completes.
python# WRONG -- storing only the Kling URL db.save(video_url=kling_cdn_url) # will expire # CORRECT -- download and rehost local_path = download_video(kling_cdn_url) permanent_url = upload_to_s3(local_path, bucket) db.save(video_url=permanent_url)
Symptom: 400 Bad Request on image-to-video with multiple features.
These are mutually exclusive for image-to-video:
camera_controldynamic_masks / static_maskimage_tailYou can only use ONE group per request.
Symptom: 400 or unexpected behavior.
python# WRONG -- kling-v2-1 is I2V-only {"model_name": "kling-v2-1", "prompt": "A sunset..."} # fails # CORRECT -- use models that support T2V {"model_name": "kling-v2-master", "prompt": "A sunset..."} {"model_name": "kling-v2-5-turbo", "prompt": "A sunset..."}
Check the model catalog: kling-v1-5 and kling-v2-1 support image-to-video only.
Symptom: Silent failures, missing videos.
python# WRONG -- only check for success if result["task_status"] == "succeed": process(result) # silently ignores failures # CORRECT -- handle all terminal states if result["task_status"] == "succeed": process(result) elif result["task_status"] == "failed": log_failure(result["task_status_msg"]) retry_or_alert(task_id)
Symptom: Credits depleted 5x faster than expected.
Native audio (v2.6, motion_has_audio: true) multiplies credit cost by 5x:
Always check motion_has_audio in cost estimates.
Symptom: Low-quality, incoherent video output.
python# WEAK -- too vague "A nice video of nature" # STRONG -- specific and descriptive "Close-up of a monarch butterfly landing on a lavender flower, " "soft bokeh background, golden hour lighting, macro lens, 4K"
Good prompts: specific subject, clear action, lighting, camera angle, style.
| Pitfall | Fix | |---------|-----| | Duration as int | Use string: "5" | | JWT headers missing | Add headers={"alg":"HS256","typ":"JWT"} | | Token not refreshed | Auto-refresh with 5-min buffer | | No poll timeout | Max 600s with failure check | | Kling URLs as permanent | Download and rehost immediately | | Mixed I2V features | One feature group per request | | Wrong model for T2V | Check model supports text-to-video | | No failure handling | Check for "failed" status | | Audio cost surprise | 5x multiplier with motion_has_audio | | Vague prompts | Specific subject, action, style, lighting |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-07 | pass→pass | 13,851 | 17,689 | +28% | 1 | 1 | 0% | 2,786 | 4,114 | +48% | 0 | 0 | — |
case-01 | fail→pass | 35,860 | 29,328 | -18% | 1 | 1 | 0% | 4,941 | 5,463 | +11% | 0 | 0 | — |
case-02 | pass→pass | 36,266 | 36,230 | -0% | 1 | 1 | 0% | 6,796 | 7,635 | +12% | 0 | 0 | — |
case-03 | fail→pass | 29,890 | 20,280 | -32% | 1 | 1 | 0% | 5,028 | 6,060 | +21% | 0 | 0 | — |
case-04 | fail→pass | 17,167 | 5,807 | -66% | 1 | 1 | 0% | 2,477 | 2,649 | +7% | 0 | 0 | — |
case-05 | pass→pass | 15,235 | 13,522 | -11% | 1 | 1 | 0% | 1,956 | 2,175 | +11% | 0 | 0 | — |
case-06 | fail→pass | 13,823 | 14,239 | +3% | 1 | 1 | 0% | 2,352 | 3,313 | +41% | 0 | 0 | — |
case-08 | fail→pass | 17,481 | 11,743 | -33% | 1 | 1 | 0% | 2,188 | 2,768 | +27% | 0 | 0 | — |
case-09 | pass→pass | 15,002 | 12,185 | -19% | 1 | 1 | 0% | 2,733 | 2,962 | +8% | 0 | 0 | — |
case-10 | fail→pass | 15,258 | 9,173 | -40% | 1 | 1 | 0% | 1,500 | 2,118 | +41% | 0 | 0 | — |
case-11 | pass→pass | 15,916 | 12,427 | -22% | 1 | 1 | 0% | 2,016 | 2,830 | +40% | 0 | 0 | — |
case-12 | fail→pass | 7,904 | 2,123 | -73% | 1 | 1 | 0% | 1,392 | 1,895 | +36% | 0 | 0 | — |
case-13 | pass→pass | 24,995 | 14,864 | -41% | 1 | 1 | 0% | 2,223 | 3,058 | +38% | 0 | 0 | — |
case-14 | pass→pass | 9,163 | 6,838 | -25% | 1 | 1 | 0% | 1,708 | 2,822 | +65% | 0 | 0 | — |
case-15 | pass→pass | 24,299 | 18,488 | -24% | 1 | 1 | 0% | 3,248 | 4,777 | +47% | 0 | 0 | — |
case-16 | pass→pass | 5,145 | 8,877 | +73% | 1 | 1 | 0% | 921 | 2,237 | +143% | 0 | 0 | — |
case-17 | fail→pass | 18,452 | 5,976 | -68% | 1 | 1 | 0% | 2,402 | 2,719 | +13% | 0 | 0 | — |
case-18 | fail→pass | 17,097 | 27,060 | +58% | 1 | 1 | 0% | 2,005 | 4,551 | +127% | 0 | 0 | — |
case-19 | fail→pass | 17,271 | 7,857 | -55% | 1 | 1 | 0% | 2,351 | 3,112 | +32% | 0 | 0 | — |
case-20 | fail→pass | 7,793 | 7,935 | +2% | 1 | 1 | 0% | 1,301 | 2,102 | +62% | 0 | 0 | — |
case-21 | pass→pass | 14,702 | 10,141 | -31% | 1 | 1 | 0% | 1,718 | 2,289 | +33% | 0 | 0 | — |
case-22 | pass→pass | 14,916 | 14,101 | -5% | 1 | 1 | 0% | 2,577 | 3,627 | +41% | 0 | 0 | — |
case-23 | fail→pass | 18,836 | 6,166 | -67% | 1 | 1 | 0% | 2,854 | 2,687 | -6% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +52 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.