
Kuaishou's Kling 3.0 Turbo generates short video clips from a plain-text prompt, and on hiapi you call it through the same unified async endpoint as every other generation model: POST /v1/tasks. This guide gives you a live-verified parameter table, a copy-paste curl request, a complete Python client with polling and download, and the production patterns (callbacks, idempotency, error handling) you'll want before wiring it into a real app.
Everything below — field names, enum values, duration limits, and error messages — was verified against the live API, not copied from a spec.
Goal: send a text prompt to kling-3.0-turbo/text-to-video, wait for the render, and download the resulting .mp4.
Prerequisites:
sk-... and go in the Authorization: Bearer header.curl or Python 3.8+ with requests. No SDK required.kling-3.0-turbo/text-to-video accepts exactly four input fields. The schema is strict: any field not in this table is rejected with a 400, so don't pass negative_prompt, cfg_scale, mode, or seed — they are not part of this model's contract.
| Field | Type | Required | Values | Notes |
|---|---|---|---|---|
prompt | string | ✅ yes | free text | Scene, subject, motion, camera language |
duration | integer | no | 3–15 | Flexible Duration: any whole second count in range |
aspect_ratio | string | no | 16:9, 9:16, 1:1 | Landscape, vertical, square |
resolution | string | no | 720p, 1080p | Output resolution |
Unlike models that lock you into fixed 5s/10s tiers, Kling 3.0 Turbo's duration is a plain integer from 3 to 15. Because billing is per second of generated video, this means you tune spend at one-second granularity: a 6-second product teaser costs literally half of a 12-second one. Ask for the shortest clip that serves the shot — you can always chain another generation.
Create the task:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-3.0-turbo/text-to-video",
"input": {
"prompt": "A golden retriever surfing a turquoise wave at sunset, cinematic slow motion, water droplets frozen mid-air",
"duration": 6,
"aspect_ratio": "16:9",
"resolution": "1080p"
}
}'
The response returns immediately with a task id:
{
"code": 200,
"data": {
"taskId": "task_..."
}
}
Poll for the result (every ~5 seconds is fine):
curl -s https://api.hiapi.ai/v1/tasks/<taskId> \
-H "Authorization: Bearer sk-<your-key>"
While rendering, data.status stays in a non-terminal state. When it flips to "success", the video URL is at data.output[0].url:
# one-liner: grab the finished mp4 URL with jq
curl -s https://api.hiapi.ai/v1/tasks/<taskId> \
-H "Authorization: Bearer sk-<your-key>" | jq -r '.data.output[0].url'
Download it immediately. Output URLs are temporary (they carry an expireAt) — persist the bytes to your own storage as soon as the task succeeds. Never hot-link the task output URL from a web page.
A full create → poll → download loop with timeout and failure handling:
import time
import requests
API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
# 1. Create the task
create = requests.post(
API_BASE,
headers={**HEADERS, "Content-Type": "application/json"},
json={
"model": "kling-3.0-turbo/text-to-video",
"input": {
"prompt": (
"A golden retriever surfing a turquoise wave at sunset, "
"cinematic slow motion, water droplets frozen mid-air"
),
"duration": 6,
"aspect_ratio": "16:9",
"resolution": "1080p",
},
},
timeout=60,
)
body = create.json()
task_id = (body.get("data") or {}).get("taskId")
if not task_id:
raise RuntimeError(f"create failed: {body}")
print(f"task created: {task_id}")
# 2. Poll until terminal state (video takes minutes, not seconds)
deadline = time.time() + 600
video_url = None
while time.time() < deadline:
task = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30).json()
data = task.get("data") or {}
status = data.get("status")
if status == "success":
video_url = data["output"][0]["url"]
break
if status == "fail":
err = data.get("error") or {}
raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
time.sleep(5)
if not video_url:
raise TimeoutError(f"task {task_id} not finished after 600s")
# 3. Download immediately — the output URL expires
clip = requests.get(video_url, timeout=120)
clip.raise_for_status()
with open("kling-clip.mp4", "wb") as f:
f.write(clip.content)
print(f"saved kling-clip.mp4 ({len(clip.content)} bytes)")
Notes on the defaults chosen here:
data.status, and treats "success" / "fail" as the only terminal states — anything else means keep waiting.data.error with a code and message; log both.For server-side workloads, skip the poll loop entirely: pass a callback object at the top level of the create request (next to model, not inside input), and hiapi will POST the terminal task state to your endpoint when the render finishes:
{
"model": "kling-3.0-turbo/text-to-video",
"input": { "prompt": "..." },
"callback": { "url": "https://your.app/hiapi-callback", "when": "final" }
}
when: "final" means you get exactly one call, on the terminal state. Rule of thumb: polling is fine for CLIs, scripts, and dev loops; callbacks are better once you're generating from a queue or handling user traffic, because you hold no open loops and pay no polling latency.
Task creation is not idempotent — if your process crashes after the POST but before persisting the taskId, retrying blindly creates (and bills) a second render. Write the taskId to your own storage the moment create returns, and on ambiguous failures check your task history before re-submitting.
These are real responses from the live API, so you can match on them:
| What you sent | Response |
|---|---|
Missing prompt | 400 INVALID_REQUEST — missing required field "prompt" |
Unknown field (e.g. negative_prompt) | 400 INVALID_REQUEST — additional properties 'negative_prompt' not allowed |
duration: 2 | 400 — duration: minimum: got 2, want 3 |
duration: 999 | 400 — duration: maximum: got 999, want 15 |
aspect_ratio: "4:3" | 400 — value must be one of '16:9', '9:16', '1:1' |
resolution: "480p" | 400 — value must be one of '720p', '1080p' |
| Bad / missing API key | 401 permission_denied |
Two operational gotchas worth coding for:
status: "fail" with data.error populated. Handle both paths.Expect minutes rather than seconds — render time grows with duration and resolution. Poll every ~5 seconds with a generous ceiling (the example above uses 10 minutes), or use a callback and don't wait at all.
Not in a single task — duration is capped at 15. For longer sequences, generate multiple clips and stitch them, or use kling-3.0-turbo/image-to-video to continue from the last frame of a previous clip for better continuity.
No. The input schema is strict and accepts only prompt, duration, aspect_ratio, and resolution. Sending anything else returns a 400 (additional properties ... not allowed). Put avoidance instructions ("no text overlays, no watermarks") directly into the prompt instead.
Billing is per second of generated video, so a 6-second clip costs half of a 12-second one at the same settings. See the pricing page for current rates.
Production API access requires a key. If you're exploring free options first, this comparison of free text-to-video APIs covers what's realistically available and the trade-offs.
Both are closed enums, and the errors tell you exactly which one you tripped: aspect_ratio must be 16:9, 9:16, or 1:1; resolution must be 720p or 1080p. There is no custom width/height for this model.