Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Process multiple video generation requests efficiently with Kling AI. Use when generating batches of videos or building content pipelines. Trigger with phrases like 'klingai batch', 'kling ai bulk', 'multiple videos klingai', 'klingai parallel generation'.
.claude/skills/jeremylongshore-klingai-batch-processing/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 0% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 20% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 29% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 46% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 71% | 0% |
Generate multiple videos efficiently using controlled parallelism, rate-limit-aware submission, progress tracking, and result collection. All requests go through https://api.klingai.com/v1.
pythonimport jwt, time, os, requests BASE = "https://api.klingai.com/v1" def get_headers(): ak, sk = os.environ["KLING_ACCESS_KEY"], os.environ["KLING_SECRET_KEY"] token = jwt.encode( {"iss": ak, "exp": int(time.time()) + 1800, "nbf": int(time.time()) - 5}, sk, algorithm="HS256", headers={"alg": "HS256", "typ": "JWT"} ) return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} def submit_batch(prompts, model="kling-v2-master", duration="5", mode="standard", max_concurrent=3, delay=2.0): """Submit batch with controlled concurrency and pacing.""" tasks = [] active = [] for i, prompt in enumerate(prompts): # Wait if at concurrency limit while len(active) >= max_concurrent: active = [t for t in active if not check_complete(t["task_id"])] if len(active) >= max_concurrent: time.sleep(5) response = requests.post(f"{BASE}/videos/text2video", headers=get_headers(), json={ "model_name": model, "prompt": prompt, "duration": duration, "mode": mode, }) data = response.json()["data"] task = {"task_id": data["task_id"], "prompt": prompt, "index": i} tasks.append(task) active.append(task) print(f"[{i+1}/{len(prompts)}] Submitted: {data['task_id']}") time.sleep(delay) # pace requests return tasks def check_complete(task_id): r = requests.get(f"{BASE}/videos/text2video/{task_id}", headers=get_headers()).json() return r["data"]["task_status"] in ("succeed", "failed")
pythondef collect_results(tasks, timeout=600): """Wait for all tasks and collect results.""" results = {} start = time.monotonic() while len(results) < len(tasks) and time.monotonic() - start < timeout: for task in tasks: if task["task_id"] in results: continue r = requests.get( f"{BASE}/videos/text2video/{task['task_id']}", headers=get_headers() ).json() status = r["data"]["task_status"] if status == "succeed": results[task["task_id"]] = { "status": "succeed", "url": r["data"]["task_result"]["videos"][0]["url"], "prompt": task["prompt"], } elif status == "failed": results[task["task_id"]] = { "status": "failed", "error": r["data"].get("task_status_msg", "Unknown"), "prompt": task["prompt"], } if len(results) < len(tasks): time.sleep(15) return results
pythonimport asyncio import aiohttp async def async_batch(prompts, max_concurrent=3): """Async batch processing with semaphore-controlled concurrency.""" semaphore = asyncio.Semaphore(max_concurrent) results = {} async def generate_one(prompt, index): async with semaphore: async with aiohttp.ClientSession() as session: # Submit async with session.post( f"{BASE}/videos/text2video", headers=get_headers(), json={"model_name": "kling-v2-master", "prompt": prompt, "duration": "5", "mode": "standard"}, ) as resp: data = (await resp.json())["data"] task_id = data["task_id"] # Poll while True: await asyncio.sleep(10) async with session.get( f"{BASE}/videos/text2video/{task_id}", headers=get_headers(), ) as resp: data = (await resp.json())["data"] if data["task_status"] == "succeed": results[index] = data["task_result"]["videos"][0]["url"] return elif data["task_status"] == "failed": results[index] = f"FAILED: {data.get('task_status_msg')}" return await asyncio.gather(*[generate_one(p, i) for i, p in enumerate(prompts)]) return results
pythondef submit_batch_with_callbacks(prompts, callback_url): """Submit batch with webhook callbacks -- no polling needed.""" tasks = [] for prompt in prompts: r = requests.post(f"{BASE}/videos/text2video", headers=get_headers(), json={ "model_name": "kling-v2-master", "prompt": prompt, "duration": "5", "mode": "standard", "callback_url": callback_url, }).json() tasks.append(r["data"]["task_id"]) time.sleep(2) # rate limit pacing return tasks
pythondef estimate_batch_cost(count, duration=5, mode="standard", audio=False): credits_map = {(5, "standard"): 10, (5, "professional"): 35, (10, "standard"): 20, (10, "professional"): 70} per_video = credits_map.get((duration, mode), 10) if audio: per_video *= 5 total = count * per_video print(f"Batch: {count} videos x {per_video} credits = {total} credits") print(f"Estimated cost: ${total * 0.14:.2f}") return total # Check before submitting needed = estimate_batch_cost(50, duration=5, mode="standard")
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-14 | pass→pass | 21,569 | 19,114 | -11% | 1 | 1 | 0% | 2,936 | 4,405 | +50% | 0 | 0 | — |
case-20 | pass→pass | 11,173 | 11,625 | +4% | 1 | 1 | 0% | 2,186 | 2,953 | +35% | 0 | 0 | — |
case-21 | pass→pass | 23,481 | 12,118 | -48% | 1 | 1 | 0% | 3,803 | 4,283 | +13% | 0 | 0 | — |
case-22 | fail→fail | 16,723 | 17,168 | +3% | 1 | 1 | 0% | 3,303 | 4,114 | +25% | 0 | 0 | — |
case-01 | fail→pass | 26,842 | 18,050 | -33% | 1 | 1 | 0% | 4,614 | 4,617 | +0% | 0 | 0 | — |
case-02 | fail→pass | 35,895 | 21,984 | -39% | 1 | 1 | 0% | 4,535 | 5,455 | +20% | 0 | 0 | — |
case-03 | fail→pass | 18,363 | 14,163 | -23% | 1 | 1 | 0% | 2,708 | 3,493 | +29% | 0 | 0 | — |
case-04 | pass→pass | 42,709 | 12,116 | -72% | 1 | 1 | 0% | 2,987 | 4,032 | +35% | 0 | 0 | — |
case-05 | pass→pass | 16,979 | 14,558 | -14% | 1 | 1 | 0% | 2,448 | 3,630 | +48% | 0 | 0 | — |
case-06 | pass→pass | 19,136 | 13,893 | -27% | 1 | 1 | 0% | 2,852 | 4,670 | +64% | 0 | 0 | — |
case-07 | pass→pass | 20,137 | 11,497 | -43% | 1 | 1 | 0% | 2,317 | 3,045 | +31% | 0 | 0 | — |
case-08 | fail→pass | 20,739 | 19,564 | -6% | 1 | 1 | 0% | 3,185 | 4,648 | +46% | 0 | 0 | — |
case-09 | pass→pass | 8,286 | 12,307 | +49% | 1 | 1 | 0% | 1,488 | 2,725 | +83% | 0 | 0 | — |
case-10 | pass→pass | 8,883 | 7,988 | -10% | 1 | 1 | 0% | 1,765 | 2,915 | +65% | 0 | 0 | — |
case-11 | pass→pass | 13,296 | 9,106 | -32% | 1 | 1 | 0% | 1,720 | 2,445 | +42% | 0 | 0 | — |
case-12 | pass→pass | 21,441 | 21,216 | -1% | 1 | 1 | 0% | 3,468 | 4,182 | +21% | 0 | 0 | — |
case-13 | fail→pass | 20,597 | 16,045 | -22% | 1 | 1 | 0% | 2,856 | 4,871 | +71% | 0 | 0 | — |
case-15 | pass→pass | 17,212 | 12,731 | -26% | 1 | 1 | 0% | 2,490 | 3,348 | +34% | 0 | 0 | — |
case-16 | fail→pass | 20,387 | 21,928 | +8% | 1 | 1 | 0% | 4,060 | 5,346 | +32% | 0 | 0 | — |
case-17 | pass→pass | 18,046 | 12,804 | -29% | 1 | 1 | 0% | 2,065 | 2,831 | +37% | 0 | 0 | — |
case-18 | fail→pass | 18,778 | 12,284 | -35% | 1 | 1 | 0% | 2,545 | 3,108 | +22% | 0 | 0 | — |
case-19 | fail→pass | 10,894 | 16,530 | +52% | 1 | 1 | 0% | 2,264 | 2,977 | +31% | 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. 22 cases were attempted. The headline lift of +36 percentage points is the difference between those two pass rates over the 22 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.