Choose the right Kling I2V endpoint, send a tested payload, and run both variants through one Python task workflow.
Choose a model, enter your prompt, and see the result.
HiAPI Blog
HiAPI
Generate it with HiAPI
This is the generic Kling AI image-to-video API Python guide for hiapi. Choose Omni or Turbo from the job requirements, keep the model-specific payload separate, and run either payload through the same create, poll, and download code.
| Choose | Use it when | Image input | Output options |
|---|---|---|---|
kling-3.0-omni/image-to-video | You need first/last-frame control, native sound, or 4K | One image for the first frame, or two images for first and last frames | 720p, 1080p, 4K; optional sound |
kling-3.0-turbo/image-to-video | A single starting frame and a smaller schema fit the job | Exactly one public image URL | 720p or 1080p; no sound field |
Both are asynchronous task models: create a task with POST /v1/tasks, poll GET /v1/tasks/{taskId}, and download data.output[0].url before it expires.
The outer task envelope is shared. The model and input fields are where the variants differ.
{
"model": "kling-3.0-omni/image-to-video",
"input": {
"image_urls": [
"https://cdn.example.com/first-frame.jpg",
"https://cdn.example.com/last-frame.jpg"
],
"prompt": "The camera arcs around the subject as the light changes from dawn to noon",
"duration": 8,
"resolution": "1080p",
"sound": true
}
}
With two images, entry 0 is the first frame and entry 1 is the last frame. Use one image when you only need a starting frame.
{
"model": "kling-3.0-turbo/image-to-video",
"input": {
"prompt": "The subject turns toward the camera while leaves move in a light breeze",
"image_urls": ["https://cdn.example.com/first-frame.jpg"],
"duration": 8,
"resolution": "1080p"
}
}
Turbo requires a prompt and exactly one image. Its accepted duration is 3-15 seconds and its resolution is 720p or 1080p. For its strict fields and failure cases, see the Turbo schema, limits, and Python errors guide.
Install requests, set HIAPI_API_KEY, then keep payload selection outside the network functions:
import os
import time
from pathlib import Path
import requests
API_BASE = "https://api.hiapi.ai/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}",
"Content-Type": "application/json",
}
def create_task(model: str, model_input: dict) -> str:
response = requests.post(
f"{API_BASE}/tasks",
headers=HEADERS,
json={"model": model, "input": model_input},
timeout=30,
)
response.raise_for_status()
return response.json()["data"]["taskId"]
def wait_for_task(task_id: str, timeout_seconds: int = 900) -> dict:
deadline = time.time() + timeout_seconds
while time.time() < deadline:
response = requests.get(
f"{API_BASE}/tasks/{task_id}", headers=HEADERS, timeout=30
)
response.raise_for_status()
task = response.json()["data"]
if task["status"] == "success":
return task
if task["status"] == "fail":
raise RuntimeError(task.get("error", "task failed"))
time.sleep(8)
raise TimeoutError(f"task {task_id} did not finish in time")
def download_output(task: dict, destination: str) -> None:
output_url = task["output"][0]["url"]
with requests.get(output_url, stream=True, timeout=180) as response:
response.raise_for_status()
with Path(destination).open("wb") as file:
for chunk in response.iter_content(1024 * 1024):
file.write(chunk)
Choose a payload and call the shared functions:
MODEL = "kling-3.0-omni/image-to-video"
MODEL_INPUT = {
"image_urls": [
"https://cdn.example.com/first-frame.jpg",
"https://cdn.example.com/last-frame.jpg",
],
"prompt": "The camera arcs around the subject as daylight grows warmer",
"duration": 8,
"resolution": "1080p",
"sound": True,
}
task_id = create_task(MODEL, MODEL_INPUT)
task = wait_for_task(task_id)
download_output(task, "kling-output.mp4")
To run Turbo, replace MODEL and MODEL_INPUT with the Turbo payload above. Do not send Omni-only fields to Turbo.
| Capability | Omni | Turbo |
|---|---|---|
prompt | Optional but recommended | Required |
image_urls | One or two URLs | Exactly one URL |
duration | Integer, 3-15 seconds | Integer, 3-15 seconds |
resolution | 720p, 1080p, 4K | 720p, 1080p |
sound | Supported | Not accepted |
Input images must be publicly reachable by the API. A localhost URL, an authenticated page, or an expired signed URL can pass basic JSON validation and still make the asynchronous task fail when the renderer fetches it.
The source image defines framing, so do not add text-to-video fields such as aspect_ratio. Unknown fields are rejected by strict schemas.
Prices below are the production rates verified for these image-to-video tiers. Check the live pricing page before budgeting a large run.
| Model and tier | Price per second | 5-second task |
|---|---|---|
| Omni 720p, no audio | $0.100 | $0.50 |
| Omni 720p, audio | $0.143 | $0.715 |
| Omni 1080p, no audio | $0.129 | $0.645 |
| Omni 1080p, audio | $0.193 | $0.965 |
| Omni 4K | $0.479 | $2.395 |
| Turbo 720p | $0.130 | $0.65 |
| Turbo 1080p | $0.160 | $0.80 |
The word “Turbo” does not mean every tier is cheaper. Select by capabilities and the price of the exact resolution and audio mode you intend to send.
Polling is appropriate for a script or notebook. A production service can send a top-level callback configuration when it creates the task, then update its job record when the terminal event arrives.
Whichever completion method you use:
success and fail as terminal outcomes.output[0].url promptly; the result includes expireAt because the pickup URL is temporary.fail passed request validation but failed during input fetching or generation. Log the task id and structured error separately from the original HTTP response.Use Omni when you need two-frame control, sound, or 4K. Use Turbo when its exact one-image, 720p/1080p schema fits the job. The create and polling code can remain shared.
Yes. Send two public URLs in image_urls; the first is the starting frame and the second is the ending frame.
No. Turbo's image-to-video schema accepts 720p or 1080p and does not accept the Omni sound field.
For these endpoints, duration is an integer from 3 through 15 seconds.
The create request passed schema validation, but the renderer may have been unable to fetch the image or complete the generation. Inspect the terminal task error and confirm the image URL is public and unexpired.