A version-specific reference for Turbo's single-image schema, 3-15 second limits, 720p/1080p tiers, and failure modes.

kling-3.0-turbo/image-to-video has a small, strict schema: one public image URL, a required motion prompt, an integer duration from 3 to 15 seconds, and either 720p or 1080p output. This page is the version-specific reference for validating that payload and separating HTTP 400 errors from asynchronous TASK_FAILED results.
For the shared create, polling, callback, and download workflow, use the Kling Python task runner and Omni/Turbo chooser.
| Field | Type | Required | Accepted value |
|---|---|---|---|
prompt | string | Yes | Describe motion and camera behavior |
image_urls | array of strings | Yes | Exactly one publicly reachable image URL |
duration | integer | No | 3 through 15 seconds |
resolution | string | No | 720p or 1080p |
Turbo does not accept sound, aspect_ratio, negative_prompt, seed, mode, or a second image URL. A strict schema rejects extra properties instead of silently ignoring them.
The input image determines the frame shape. Upload it to a public bucket or CDN before creating the task; localhost, private dashboards, and expired signed URLs cannot be fetched by the renderer.
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-3.0-turbo/image-to-video",
"input": {
"prompt": "The runner accelerates as the camera tracks from the side",
"image_urls": ["https://cdn.example.com/first-frame.jpg"],
"duration": 6,
"resolution": "1080p"
}
}'
A successful response contains data.taskId. HTTP 200 confirms creation, not completion; the video render continues asynchronously.
Validate the constraints before the network call so obvious mistakes never create a task:
from urllib.parse import urlparse
import requests
def turbo_input(prompt: str, image_url: str, duration: int, resolution: str) -> dict:
if not prompt.strip():
raise ValueError("prompt is required")
if type(duration) is not int or not 3 <= duration <= 15:
raise ValueError("duration must be an integer from 3 to 15")
if resolution not in {"720p", "1080p"}:
raise ValueError("resolution must be 720p or 1080p")
parsed = urlparse(image_url)
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
raise ValueError("image_url must be a public HTTP(S) URL")
return {
"prompt": prompt,
"image_urls": [image_url],
"duration": duration,
"resolution": resolution,
}
payload = {
"model": "kling-3.0-turbo/image-to-video",
"input": turbo_input(
prompt="The runner accelerates as the camera tracks from the side",
image_url="https://cdn.example.com/first-frame.jpg",
duration=6,
resolution="1080p",
),
}
response = requests.post(
"https://api.hiapi.ai/v1/tasks",
headers={"Authorization": "Bearer sk-your-key"},
json=payload,
timeout=30,
)
response.raise_for_status()
task_id = response.json()["data"]["taskId"]
print(task_id)
This local check catches value shape, but it cannot prove that the remote image is publicly downloadable. The renderer performs that fetch after creation.
An HTTP 400 response is synchronous schema validation. Typical causes are:
| Invalid input | Why it fails | Fix |
|---|---|---|
"duration": "5" | A string was sent instead of an integer | Send 5 |
"duration": 20 | The maximum is 15 | Choose 3-15 |
Two image_urls | Turbo accepts exactly one | Keep only the starting frame |
"resolution": "4K" | Turbo supports 720p and 1080p | Use 720p or 1080p, or choose Omni |
"sound": true | sound is not in the Turbo schema | Remove it or choose Omni |
Missing prompt | Turbo requires a prompt | Add a non-empty motion prompt |
Because validation failed before creation, there is no task id to poll. Log the response body and request id, correct the payload, and submit a new request.
If POST /v1/tasks returned HTTP 200 and a task id, later failure belongs to the asynchronous task lifecycle. Treat it separately from a 400.
Common causes include:
Record the task id, terminal error code and message, original input URL, and request time. Before retrying, verify that an unauthenticated server can fetch the image with a 2xx response and an image content type. Blindly retrying the same inaccessible URL only creates another failed task.
The generic Python guide contains the shared terminal-status loop and output download code; keeping it there avoids duplicating lifecycle logic on this version reference.
Current production rates are:
| Turbo tier | Price per second | 5-second task |
|---|---|---|
| 720p | $0.130 | $0.65 |
| 1080p | $0.160 | $0.80 |
Check the live pricing page before a large batch. Turbo's name describes the model variant; it is not a promise that every resolution is cheaper or that a task will meet a fixed render-time SLA.
Choose Omni instead when you need a last frame, audio, or 4K. For a single input frame at 720p or 1080p, benchmark both variants with your own motion prompts and judge output quality, queue time, and total cost.
Exactly one public image URL. A second image is a schema error; use Omni for first-and-last-frame control.
Send an integer from 3 through 15. A quoted number such as "5" is a string and fails validation.
No. This endpoint accepts 720p or 1080p and has no sound field. Use Omni when those capabilities are required.
HTTP 400 means no task was created because the request violated the schema. TASK_FAILED occurs after a valid create response, during input fetching or generation.
The URL may depend on cookies, have an expired signature, block server fetches, or return HTML instead of image bytes. Test it without browser authentication before retrying.