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:
- call Kling's video or image generation REST API from code
- pick between API Key and the legacy AK/SK JWT flow
- submit a generation task and poll or webhook for the result
- wire up callbacks, multi-shot videos, element library, voice binding, or camera control
- migrate from a competitor API (Runway Gen-3, Pika, Replicate routes) onto Kling directly
- debug a Kling integration that is failing with a
code: 1xxxorcode: 5xxxerror
Working Style
When this skill is active:
- Pick the auth method first. New endpoints (Kling 3.0 Turbo, the
/tasksquery shape) hard-reject AK/SK. New code uses API Key. - 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-turboand the generic/tasksquery shape. Mixing them on the same task fails. - 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. - Discover model availability at runtime via
who_am_i(MCP) or the CLI'skling who_am_i. Do not hardcode model names; they change. - 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>
- Get a key at
https://kling.ai/dev/api-key(Kling AI console). - Works on every endpoint, including the 3.0 Turbo and
/tasksshapes. - The skill assumes this method unless the user explicitly says they have an AK/SK pair from a pre-2026 integration.
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):
task_ids(comma-separated): system task IDs.external_task_ids(comma-separated): your custom task IDs.
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:
- URL. Must be reachable from the Kling API. Host the file on a CDN or storage with permissive CORS. Kling will fetch it.
- Base64. The raw string, no
data:prefix.
Correct:
iVBORw0KGgoAAAANSUhEUgAAAAUA...
Incorrect (will fail with code 1201):
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...
Constraints:
- Formats:
.jpg,.jpeg,.png - Max 10MB
- Min 300px on each side
- Aspect ratio 1:2.5 to 2.5:1
- 3.0 Turbo: max 50MB, min 300px, same aspect range
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).
- Retention: 30 days (per the API Updates page, 2025-09-29 notice). Download promptly. The MCP/CLI guide's 24h figure is outdated and contradicted by the live API docs.
- URL is hotlink-protected. Embedding the URL in a third-party site will fail. Proxy the bytes through your own CDN.
- Cost field on the single-task query:
final_unit_deductionis the actual units charged, as a decimal string. Use it for batch cost reporting.
Multi-Shot Semantics
For multi-storyboard videos (up to 6 storyboards):
- Set
multi_shot: trueandshot_type: customize(you define each shot) orshot_type: intelligence(Kling decides). promptbecomes invalid; usemulti_prompt[]instead.- Each
multi_promptitem:{ index, prompt, duration }. - Per-item
promptmax 512 chars.durationper shot >= 1s. - 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:
video_character_elements(Video Character Elements): persistent characters for video use.multi_image_elements(Multi-Image Elements): reference objects (product, scene) for image/video use.
Constraints:
- Max 3 elements per request.
element_listandvoice_listare mutually exclusive.- For voice binding to multi-image elements, pass
element_voice_id(string) per element when creating the element.
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.
soundmust be"on"to use voice binding.voice_list[].voice_idis the ID from the Voice Customization API or a system preset voice. Not interchangeable with the Lip-Sync API voice IDs.- Tasks that reference a voice ID are billed at the higher "with specified voice" rate.
Camera Control 6-Axis
camera_control.type enum:
simple: useconfigwith 6 axes, only one non-zerodown_back,forward_up,right_turn_forward,left_turn_forward: presets,configmust beNone
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:
id(string): task IDstatus(string): one ofsubmitted,processing,succeeded,failedmessage(string): present onfailed; failure reasoncreate_time/update_time(Unix ms)outputs[]: array of generated outputs, shape varies by typebilling[]: per-call cost breakdown
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)
- Issue an API Key at
https://kling.ai/dev/api-key. - Replace the JWT generation + signing code with a static
Authorization: Bearer <API_KEY>header. - Test on a legacy endpoint first (
POST /v1/videos/text2videowith 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
modelvsmodel_name.modelis the legacy field, kept for backward compat. New code usesmodel_name. Emptymodel_nameis equivalent tokling-v1.- Status name inconsistency. Legacy endpoints return
succeed(no -ed); the new/tasksendpoint returnssucceeded(with -ed). Accept both. - 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. - 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.
- Base64 with
data:prefix fails. Strip the prefix before sending. The error code is 1201 with a specific message. - 30-day TTL, not 24h. The MCP/CLI guide's 24h is outdated. Download the result URL promptly.
element_listandvoice_listare mutually exclusive. Pick one or the other per request.- Multi-shot and prompt are mutually exclusive. When
multi_shot: true, thepromptfield is invalid; you must usemulti_prompt[]. - 4K mode is not on every model. Only
kling-v3andkling-v3-omnisupportmode: "4k". Others return 1201. - Hotlink-protected output URLs. Embedding the URL in a third-party site will fail. Proxy the bytes through your own CDN.
- 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
- API root:
https://kling.ai/document-api - Auth:
https://kling.ai/document-api/api/get-started/authentication - Error codes:
https://kling.ai/document-api/api/get-started/error-codes - Callbacks:
https://kling.ai/document-api/api/get-started/callbacks - Pricing (video):
https://kling.ai/document-api/pricing/base/video - Pricing (image):
https://kling.ai/document-api/pricing/base/image - API Updates changelog:
https://kling.ai/document-api/updates/api - Capability map:
https://kling.ai/document-api/guides/capability-map/video - API key issuance:
https://kling.ai/dev/api-key - CLI (global):
npm i -g @klingai/cli-global(v0.1.1) - CLI (China):
npm i -g @klingai/cli-cn - MCP endpoint:
https://kling.ai/mcp
For the MCP/CLI surface and end-user ergonomics, see the kling skill.