Kling AI REST API

Kling AI REST API is a free agent skill maintained by Scopeful. It teaches an AI coding agent such as Claude Code, Cursor, Windsurf or Codex how to drive this tool correctly, so you do not have to re-explain it every session. Every published Scopeful skill is free and the install command is public, with no sign-in required. Install it with No install required. Call the Kling REST API directly from any HTTP client. Set KLING_API_KEY (or the legacy AK/SK pair) in env. See content/skills/kling-api.md for endpoints.. Scopeful also tracks hand-verified USD pricing for 39 AI creative tools at https://www.scopeful.org/tools.

'Use this skill whenever the user wants to integrate Kling AI''s video or image generation REST API into application code, backend services, CI pipelines, automation, or scripts. Triggers on "Kling API", "Kling text-to-video", "Kling image-to-video", "Kling REST", "kling-v3", "kling-v3-omni", "kling-video-o1", "migrate to Kling from Runway", "migrate to Kling from Pika", or any task that calls Kling''s HTTP endpoints directly. Do not trigger for MCP or CLI surface; use the `kling` skill for that.'

Tags: kling, kuaishou, video, image, rest-api, jwt, api-key

Install

No install required. Call the Kling REST API directly from any HTTP client. Set KLING_API_KEY (or the legacy AK/SK pair) in env. See content/skills/kling-api.md for endpoints.

Reference


name: kling-api description: 'Use this skill whenever the user wants to integrate Kling AI''s video or image generation REST API into application code, backend services, CI pipelines, automation, or scripts. Triggers on "Kling API", "Kling text-to-video", "Kling image-to-video", "Kling REST", "kling-v3", "kling-v3-omni", "kling-video-o1", "migrate to Kling from Runway", "migrate to Kling from Pika", or any task that calls Kling''s HTTP endpoints directly. Do not trigger for MCP or CLI surface; use the kling skill for that.' metadata: version: 1.0.0

Kling AI REST API Skill

This skill is the integration playbook for Kling AI's video and image generation REST API. It is syntax-first: every endpoint, every request field, every error code is documented from the live OpenAPI surface so a coding agent can write working code against it. Workflow glue (the submit, poll, download pattern, auth branches, file handling, callback schema) is layered on top.

What this skill is for. Building a production integration to Kling's REST API from any language or runtime. Generating videos in batch from a CI job, building a SaaS that resells Kling generation, automating marketing asset creation, scripting one-off clips.

What this skill is not for. End users who want to generate videos from inside an AI assistant (use the kling skill, MCP/CLI surface). Pricing comparison (use the Scopeful MCP, this skill assumes you have already picked Kling).

When To Use

Use this skill when the user needs to:

Working Style

When this skill is active:

  1. Pick the auth method first. New endpoints (Kling 3.0 Turbo, the /tasks query shape) hard-reject AK/SK. New code uses API Key.
  2. Pick the endpoint family. Legacy /v1/videos/{kind} and /v1/videos/{kind}/{id} work for models up through v3 Omni. The 3.0 Turbo model uses /image-to-video/kling-3.0-turbo and the generic /tasks query shape. Mixing them on the same task fails.
  3. Treat generation as async. POST to create a task, poll or webhook for status, then download works[].url. There is no synchronous "wait for result" wrapper.
  4. Discover model availability at runtime via who_am_i (MCP) or the CLI's kling who_am_i. Do not hardcode model names; they change.
  5. Verify pricing from the live price page before writing batch jobs. The numbers in this skill are correct as of 2026-06-30; the source of truth is https://kling.ai/document-api/pricing/base/video.

Auth: Which Method, When

Two methods are live. Pick the right one before writing any code.

Method 1: API Key (Bearer token) — recommended for all new work

Authorization: Bearer <API_KEY>

Method 2: AK/SK (legacy, HS256 JWT) — only for pre-2026 code

The legacy flow generates a short-lived JWT from an Access Key (AK) and Secret Key (SK) pair, then sends it as a Bearer token.

JWT payload (HS256):

{
  "iss": "<Access Key>",
  "exp": <unix seconds, future>,
  "nbf": <unix seconds, past or now>
}

Sign with the Secret Key using HS256. Send as Authorization: Bearer <signed JWT>.

Hard-rejection for new endpoints. Verified at POST /image-to-video/kling-3.0-turbo with no auth (probe-turbo.txt) and GET /tasks (probe-tasks.txt): both return

{
  "code": 1002,
  "message": "Authentication error. The current API does not support AK/SK; please go to the console (https://kling.ai/dev/api-key) to create API key.",
  "request_id": "f69e6b82-b4bc-4da6-8260-c0aade26b8a3"
}

Migration: drop the AK/SK flow, issue an API Key in the console, paste it as the Bearer token. No code changes beyond the header value.

Base URL + Region

https://api-singapore.klingai.com

This is the global region. The China region is on a separate domain (not documented in the public English docs at the time of writing); if the user is on the China site, they need @klingai/cli-cn and likely a different base URL. Ask before hardcoding.

Core Workflow: Submit -> Poll -> Download

The fundamental pattern. Every generation call follows this shape:

1. POST /v1/videos/{kind}        (or /image-to-video/kling-3.0-turbo)
   body: { model_name, prompt, ... params }
   response: { data: { task_id, task_status: "submitted" } }

2. Poll until task_status in { "succeed" or "succeeded", "failed" }
   GET /v1/videos/{kind}/{task_id}
   response: { data: { task_status, task_result: { videos: [{ url, ... }] } } }

3. GET works[].url                (the generated asset)
   Response is a 30-day signed URL. Download promptly.

Polling. No built-in poll wrapper. Recommended: poll every 5-10 seconds with exponential backoff capped at 30 seconds. Stop when task_status is terminal (succeed or succeeded, failed). Time budget for video is 1-5 minutes per the docs; image is 20-60 seconds; element creation is 30 seconds to 2 minutes.

Webhooks. Pass callback_url on the create call to get an HTTP POST instead of polling. The callback payload schema is below in the "Callbacks" section.

Terminal status names are inconsistent. The legacy endpoints return succeed (no -ed); the new /tasks endpoint returns succeeded (with -ed). Code defensively: accept both.

Endpoint Inventory

Endpoint Method Models Notes
/v1/videos/text2video POST v1 through v3-omni Legacy create. Returns task_id.
/v1/videos/text2video/{task_id} GET n/a Legacy single-task query.
/v1/videos/text2video?pageNum=&pageSize= GET n/a Legacy list query.
/v1/videos/image2video POST v1 through v3-omni Legacy image-to-video create.
/v1/videos/image2video/{task_id} GET n/a Legacy single-task query.
/v1/videos/image2video?pageNum=&pageSize= GET n/a Legacy list query.
/v1/videos/{kind}/... varies v1.5, v1.6, v2.x Per-model endpoints with extra params (motion brush, video extension, etc.).
/image-to-video/kling-3.0-turbo POST kling-3.0-turbo New endpoint shape.
/tasks?task_ids=... or ?external_task_ids=... GET all New generic single/multi query.
/tasks (POST) POST all Cursor-paginated list query with start_time, end_time, cursor, limit, filters[].

The v1/videos/{kind} legacy shape works for everything up through kling-v3 and kling-v3-omni. The 3.0 Turbo model is the first to ship on the new shape; expect future models to follow.

Endpoints: Request/Response Shapes

POST /v1/videos/text2video (legacy)

Request body:

{
  "model_name": "kling-v2-6",
  "prompt": "A cute little rabbit wearing glasses, sitting at a table, reading a newspaper, with a cup of cappuccino on the table",
  "negative_prompt": "",
  "duration": "5",
  "mode": "pro",
  "sound": "on",
  "aspect_ratio": "1:1",
  "callback_url": "",
  "external_task_id": "",
  "watermark_info": { "enabled": false }
}

model_name (optional, default kling-v1) accepts: kling-v1, kling-v1-6, kling-v2-master, kling-v2-1-master, kling-v2-5-turbo, kling-v2-6, kling-v3 (and -omni variants where the model is Omni). The backward-compat model field is accepted and equivalent to model_name="" (defaults to v1).

duration (string, default "5"): enum 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15. Per-model allowed range varies; check the Capability Map at https://kling.ai/document-api/guides/capability-map/video.

mode (string, default "std"): enum std (720P), pro (1080P), 4k (4K). 4K is only available on kling-v3 and kling-v3-omni.

sound (string, default "off"): enum on, off. With-audio generation bills at a higher per-second rate; see pricing table.

aspect_ratio (string, default "16:9"): enum 16:9, 9:16, 1:1.

cfg_scale (float, default 0.5, range [0, 1]): higher = stronger prompt adherence. kling-v2.x models do not support this parameter.

negative_prompt (string, max 2500 chars): negative text prompt.

prompt (string, max 2500 chars): positive text prompt.

multi_shot (bool, default false): when true, prompt is invalid and you must pass multi_prompt[] (up to 6 storyboards).

shot_type (string, enum customize | intelligence): storyboard method. Required when multi_shot: true.

multi_prompt (array, up to 6 items): each item is { index: int, prompt: string, duration: string }. Per-item prompt max 512 chars. Sum of durations must equal total duration.

element_list (array, up to 3 items): each is { element_id: long }. Reusable characters/elements from the Element Library. Mutually exclusive with voice_list.

voice_list (array, up to 2 items): each is { voice_id: string }. Bind voices for <<<voice_1>>> references in the prompt. Requires sound: "on". Mutually exclusive with element_list.

camera_control (object, optional): see "Camera Control" section.

watermark_info (object, optional): { enabled: bool }. When true, the response includes watermark_url alongside url. Custom watermarks are not currently supported.

callback_url (string, optional): if set, the server POSTs the callback payload (see Callbacks section) to this URL on status change.

external_task_id (string, optional, unique per user): your own task ID for client-side correlation. Does not replace task_id.

Response (200):

{
  "code": 0,
  "message": "string",
  "request_id": "string",
  "data": {
    "task_id": "string",
    "task_info": { "external_task_id": "string" },
    "task_status": "submitted",
    "created_at": 1722769557708,
    "updated_at": 1722769557708
  }
}

GET /v1/videos/text2video/{task_id} (legacy single query)

Response (200) when complete:

{
  "code": 0,
  "message": "string",
  "request_id": "string",
  "data": {
    "task_id": "string",
    "task_status": "succeed",
    "task_status_msg": "string",
    "task_result": {
      "videos": [
        {
          "id": "string",
          "url": "https://...",
          "watermark_url": "https://...",
          "duration": "5"
        }
      ]
    },
    "task_info": { "external_task_id": "string" },
    "watermark_info": { "enabled": false },
    "final_unit_deduction": "4.0",
    "created_at": 1722769557708,
    "updated_at": 1722769557708
  }
}

task_status enum: submitted, processing, succeed, failed.

final_unit_deduction (string, decimal): actual cost in units for this task. Use it for cost tracking in batch jobs.

task_status_msg (string): present on failed; describes why (content moderation hit, invalid input, etc.).

You can pass external_task_id instead of task_id in the path.

POST /v1/videos/image2video (legacy)

Same response shape as text2video create. Request body adds:

image (string, required if no image_tail): reference image. Either a URL (must be reachable) or Base64 with no data: prefix. Formats: .jpg, .jpeg, .png. Max 10MB. Min 300px. Aspect ratio 1:2.5 to 2.5:1.

image_tail (string, optional): end-frame reference. Same format constraints. Mutually exclusive with static_mask, dynamic_masks, and camera_control.

static_mask (string, optional): static brush mask URL or Base64. Aspect ratio must match image.

dynamic_masks (array, up to 6 groups): each group has mask (URL or Base64) and trajectories (array of {x: int, y: int} coordinates; 2-77 points for a 5s video; coordinate origin is the bottom-left of the image).

GET /v1/videos/image2video/{task_id} (legacy single query)

Same shape as text2video query.

POST /image-to-video/kling-3.0-turbo (new)

Different body shape. Uses a contents[] array and settings / options envelopes:

{
  "contents": [
    {
      "type": "prompt",
      "text": "A girl sat on the train, looking out the window..."
    },
    { "type": "first_frame", "url": "https://your-cdn.com/start-frame.jpg" }
  ],
  "settings": { "resolution": "1080p", "duration": 10 },
  "options": {
    "callback_url": "https://your-server.com/callback",
    "external_task_id": "",
    "watermark_info": { "enabled": true }
  }
}

contents[].type enum: prompt, first_frame. Place fields of the same material in the same object.

settings.resolution enum: 720p, 1080p. (No 4K on 3.0 Turbo.)

settings.duration int, enum 3 through 15. 3.0 Turbo has a max duration of 15s.

options mirrors the legacy callback_url, external_task_id, watermark_info.

Response (200):

{
  "code": 0,
  "message": "string",
  "request_id": "string",
  "data": {
    "id": "893605946402811985",
    "status": "submitted",
    "create_time": 1781080778802,
    "update_time": 1781080794151,
    "external_id": "string"
  }
}

Note the field name changes: task_id -> id, task_status -> status, created_at -> create_time, updated_at -> update_time. Code defensively against both shapes if you support multiple model families.

GET /tasks?task_ids=... (new query, single or batch)

Query params (pick one, do not combine):

Max 30 IDs per request (the docs do not state a hard limit; observed behavior). Response is an array of task objects with outputs[], billing[], and the same status/create_time/update_time shape as the create response.

POST /tasks (new query, cursor-paginated list)

Request body:

{
  "start_time": "1781193600000",
  "end_time": "1781516352968",
  "cursor": "",
  "limit": 100,
  "filters": [
    { "key": "status", "values": ["succeeded"] },
    { "key": "product_type", "values": ["video"] }
  ]
}

start_time / end_time: Unix ms. start_time defaults to end_time - 30 days. cursor (non-empty) overrides both.

limit: max 500.

filters[].key enum: status (values: submitted, processing, succeeded, failed), product_type (values: video, image, try_on).

Response shape:

{
  "code": 0,
  "data": {
    "result": [ ...task objects... ],
    "count": 1,
    "next_cursor": "string",
    "has_more": true
  }
}

Models + Pricing

Per-second billing for video, per-call for audio/image. All prices are in units; 1 unit = $0.14 USD (the price page lists both). Source: https://kling.ai/document-api/pricing/base/video. Always verify against the live page before running a batch job.

Model Mode 720P 1080P 4K
kling-3.0-turbo with audio 0.8/s 1.0/s -
kling-v3 no audio 0.6/s 0.8/s 3.0/s
kling-v3 with audio, no voice 0.9/s 1.2/s 3.0/s
kling-v3 motion control 0.9/s 1.2/s -
kling-v3-omni with video input 0.6/s 0.8/s 3.0/s
kling-v3-omni no video, with audio 0.8/s 1.0/s 3.0/s
kling-v3-omni with video, no audio 0.9/s 1.2/s 3.0/s
kling-video-o1 no video input 0.6/s 0.8/s -
kling-video-o1 with video input 0.9/s 1.2/s -
kling-v2-6 no audio 0.3/s 0.5/s -
kling-v2-6 with audio, no voice - 1.0/s -
kling-v2-6 with audio, with voice - 1.2/s -
kling-v2-6 motion control 0.5/s 0.8/s -
kling-v2-5-turbo no audio 0.3/s 0.5/s -
kling-v2-1 no audio 0.4/s 0.7/s -
kling-v2-1-master no audio - 2.0/s -
kling-v2 no audio - 2.0/s -
kling-v1-6 no audio 0.4/s 0.7/s -
kling-v1-6 multi-image to video 0.4/s 0.7/s -
kling-v1-6 multi-element editing 0.6/s 1.0/s -
kling-v1-6 video extension 2.0/call 3.5/call -
kling-v1-5 no audio 0.4/s 0.7/s -
kling-v1-5 video extension 2.0/call 3.5/call -
kling-v1 no audio 0.2/s 0.7/s -
kling-v1 video extension 2.0/call 3.5/call -
avatar avatar 0.4/s 0.8/s -
avatar TTS 0.05/call
avatar lip sync 0.5 per 5s
avatar face recognition 0.05/call
audio text to audio 0.25/call
audio video to audio 0.25/call
audio custom voice 0.05/call
video image recognition 0.1/call

Per-call billing fields on the /tasks response:

"billing": [
  {
    "charge_type": "cash" | "unit",
    "amount": "0.40",
    "package_type": "video" | "image" | "audio"
  }
]

charge_type: "cash" means the cost came from your account balance. charge_type: "unit" means it came from a prepaid resource package, in which case package_type is the bundle it drew from.

Error Codes (19)

Source: https://kling.ai/document-api/api/get-started/error-codes.

HTTP Service code Meaning Fix
200 0 Request successful -
401 1000 Authentication failed Check Authorization header.
401 1001 Authorization is empty Fill in correct Authorization.
401 1002 Auth method not supported This endpoint rejects AK/SK. Use API Key from https://kling.ai/dev/api-key.
401 1003 Authorization not yet valid Check JWT nbf; wait or reissue.
401 1004 Authorization expired Check JWT exp; reissue.
429 1100 Account exception Verify account configuration.
429 1101 Account in arrears Recharge.
429 1102 Resource pack exhausted or expired Buy a pack or enable post-pay.
403 1103 Unauthorized access Verify account permissions.
400 1200 Invalid request parameters Check params.
400 1201 Invalid parameter value See message field.
404 1202 Requested method invalid Check API doc; use correct method.
404 1203 Requested resource does not exist Check task_id / model.
400 1300 Blocked by platform policy Review content.
400 1301 Content security policy Modify input content.
429 1302 Rate limit exceeded Reduce QPS, try later, or contact support.
429 1303 Concurrency or QPS exceeds pack limit Reduce request rate.
429 1304 IP whitelist policy Contact support.
500 5000 Server internal error Retry later.
503 5001 Server temporarily unavailable Retry later (maintenance).
504 5002 Server internal timeout Retry later.

The 1302/1303 rate limit codes do not document a specific number (non-subscriber 5 QPS, single concurrent video task for non-subs, per the MCP/CLI guide). Treat any 130x as a backoff signal.

File Inputs

Two ways to provide a reference image:

  1. URL. Must be reachable from the Kling API. Host the file on a CDN or storage with permissive CORS. Kling will fetch it.
  2. Base64. The raw string, no data: prefix.

Correct:

iVBORw0KGgoAAAANSUhEUgAAAAUA...

Incorrect (will fail with code 1201):

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...

Constraints:

If the input fails validation, the API returns code: 1201 with a specific reason in the message field.

Output Handling

Successful generation returns works[].url (and works[].url_without_watermark in the CLI/README; the legacy REST task_result.videos[].url is the same thing, the dual naming is for parity with the MCP works[] shape).

Multi-Shot Semantics

For multi-storyboard videos (up to 6 storyboards):

  1. Set multi_shot: true and shot_type: customize (you define each shot) or shot_type: intelligence (Kling decides).
  2. prompt becomes invalid; use multi_prompt[] instead.
  3. Each multi_prompt item: { index, prompt, duration }.
  4. Per-item prompt max 512 chars. duration per shot >= 1s.
  5. Sum of shot durations must equal the top-level duration.

On Omni models, you can also pass first_frame/last_frame references per shot (3.0 Omni update, 2026-05-07).

Element Library

Reusable characters or scene elements, created once and referenced across many generations via element_list[]. Each element is identified by a numeric element_id.

Two types, with different scopes:

Constraints:

Element creation itself is an async API on a separate endpoint (advanced-custom-elements). Pre-built elements live at https://kling.ai/dev/model-release/....

Voice Binding

Two voices max per request. Reference them in the prompt with the <<<voice_n>>> placeholder:

The man <<<voice_1>>> said, "Hello." <<<voice_2>>> laughed.

Camera Control 6-Axis

camera_control.type enum:

Axes (range [-10, 10]):

Axis Meaning Negative Positive
horizontal x-translation left right
vertical y-translation down up
pan rotation around y left right
tilt rotation around x down up
roll rotation around z counterclockwise clockwise
zoom focal length change longer (narrower FOV) shorter (wider FOV)

Mutually exclusive with image_tail and the brush masks.

Callbacks (Webhook)

When callback_url is set on create, the server POSTs a JSON payload to that URL on status change. The schema is the same as the single-task query response, with two key fields:

outputs[].type enum and the per-type fields:

Type Fields
video id, url, watermark_url, duration (seconds)
image url, watermark_url, group_id (only on grouped images)
audio id, mp3_url, wav_url, mp3_duration, wav_duration
voice id, name, url, owned_by, status
element id, name, description, element_type, references[], owned_by, status, tags[]

Validate with an HMAC if you need to confirm the callback came from Kling (the docs do not describe a signature scheme at the time of writing, so treat any URL-based delivery as best-effort and use a secret path in the URL).

Migration Paths

AK/SK to API Key (mandatory for new endpoints)

  1. Issue an API Key at https://kling.ai/dev/api-key.
  2. Replace the JWT generation + signing code with a static Authorization: Bearer <API_KEY> header.
  3. Test on a legacy endpoint first (POST /v1/videos/text2video with a known-good prompt) before flipping traffic.

Runway Gen-3 to Kling v3

Runway concept Kling equivalent
duration (4s or 8s) duration (3-15s, per-model)
aspect ratio string aspect_ratio enum 16:9 / 9:16 / 1:1
motion_amount slider not a direct match; use camera_control
seed for reproducibility not exposed in the public API
sync wait-for-result async only; poll or callback_url
image reference (URL) image (URL or Base64, no data: prefix)

Cost: Kling v3 pro 1080P with audio is 1.2 units/s = $0.168/s. Runway Gen-3 Standard 10s is $0.50; Kling at 10s is $1.68. Cheaper for short clips, more expensive for long.

Pika to Kling

Pika concept Kling equivalent
motion (0-4) no direct match; use prompt + camera control
aspect_ratio free-form enum only
sync wait async only
seed not exposed
prompt-only generation POST /v1/videos/text2video

Kling has a richer mode matrix (std / pro / 4k) and explicit audio control via sound, which Pika lacks.

Common Pitfalls

  1. model vs model_name. model is the legacy field, kept for backward compat. New code uses model_name. Empty model_name is equivalent to kling-v1.
  2. Status name inconsistency. Legacy endpoints return succeed (no -ed); the new /tasks endpoint returns succeeded (with -ed). Accept both.
  3. Field name drift between endpoint shapes. Legacy (task_id / task_status / created_at) vs new (id / status / create_time). Code with both shapes or pick one model family.
  4. 1002 error means "wrong auth method". Not "invalid credentials". If you see 1002 on a new endpoint, the answer is not to fix the JWT; it's to drop AK/SK entirely and use an API Key.
  5. Base64 with data: prefix fails. Strip the prefix before sending. The error code is 1201 with a specific message.
  6. 30-day TTL, not 24h. The MCP/CLI guide's 24h is outdated. Download the result URL promptly.
  7. element_list and voice_list are mutually exclusive. Pick one or the other per request.
  8. Multi-shot and prompt are mutually exclusive. When multi_shot: true, the prompt field is invalid; you must use multi_prompt[].
  9. 4K mode is not on every model. Only kling-v3 and kling-v3-omni support mode: "4k". Others return 1201.
  10. Hotlink-protected output URLs. Embedding the URL in a third-party site will fail. Proxy the bytes through your own CDN.
  11. No cancellation. The MCP/CLI guide states generation tasks cannot be canceled once submitted. Build your queue to absorb this; do not promise a cancel button to users.

Code Examples

Curl (text-to-video, legacy endpoint, API Key)

curl --request POST \
  --url https://api-singapore.klingai.com/v1/videos/text2video \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "model_name": "kling-v2-6",
    "prompt": "A cinematic drone shot over a snowy mountain peak at sunrise",
    "duration": "5",
    "mode": "pro",
    "sound": "on",
    "aspect_ratio": "16:9",
    "callback_url": "https://your-server.com/kling-callback"
  }'

Curl (poll a task)

curl --request GET \
  --url https://api-singapore.klingai.com/v1/videos/text2video/$TASK_ID \
  --header 'Authorization: Bearer YOUR_API_KEY'

Curl (3.0 Turbo, new endpoint)

curl --location 'https://api-singapore.klingai.com/image-to-video/kling-3.0-turbo' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data-raw '{
    "contents": [
      { "type": "prompt", "text": "A girl sat on the train, looking out the window with a melancholic expression" },
      { "type": "first_frame", "url": "https://your-cdn.com/first-frame.jpg" }
    ],
    "settings": { "resolution": "1080p", "duration": 10 },
    "options": { "callback_url": "https://your-server.com/callback" }
  }'

Python (pseudocode, polling loop)

import time
import requests

API_KEY = "YOUR_API_KEY"
BASE = "https://api-singapore.klingai.com"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

def submit(prompt, model="kling-v2-6", duration="5", mode="pro"):
    r = requests.post(f"{BASE}/v1/videos/text2video", headers=HEADERS, json={
        "model_name": model, "prompt": prompt, "duration": duration, "mode": mode,
    })
    r.raise_for_status()
    return r.json()["data"]["task_id"]

def poll(task_id, max_seconds=300):
    deadline = time.time() + max_seconds
    delay = 5
    while time.time() < deadline:
        r = requests.get(f"{BASE}/v1/videos/text2video/{task_id}", headers=HEADERS)
        r.raise_for_status()
        d = r.json()["data"]
        status = d["task_status"]
        if status in ("succeed", "succeeded"):
            return d["task_result"]["videos"][0]["url"], d.get("final_unit_deduction")
        if status == "failed":
            raise RuntimeError(f"Kling task failed: {d.get('task_status_msg')}")
        time.sleep(delay)
        delay = min(delay * 1.5, 30)
    raise TimeoutError("Kling task did not complete in time")

Resources

For the MCP/CLI surface and end-user ergonomics, see the kling skill.