Kling 4.0 Image-to-Video animates one first-frame image into a 3–30 second clip with synchronized audio. This guide gives you the exact request that works on hiapi, the fields the API accepts and rejects, and what a real run looked like. You need an API key from the hiapi dashboard; the model id is kling-4.0/image-to-video, and the model page lists the current spec.
If you are still on Kling 3.0, the Kling 4.0 API overview was written before launch and describes it as announced; this page is the working reference now.
Minimal request (curl)
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-4.0/image-to-video",
"input": {
"prompt": "She turns her head slightly toward the camera, hair moving in the wind, camera slowly pushes in, ambient wave sound",
"image_urls": ["https://example.com/first-frame.jpg"],
"duration": 3,
"resolution": "720p"
}
}'
The response returns a task id immediately:
{"code":200,"data":{"taskId":"tk-hiapi-..."},"message":"success"}
Poll GET /v1/tasks/<taskId> until data.status is success, then read data.output[0].url.
Python: submit and poll
import os, time, requests
BASE = "https://api.hiapi.ai"
HEADERS = {"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"}
def kling4_i2v(image_url, prompt, duration=5, resolution="720p"):
r = requests.post(f"{BASE}/v1/tasks", headers=HEADERS, json={
"model": "kling-4.0/image-to-video",
"input": {
"prompt": prompt,
"image_urls": [image_url], # exactly one first frame
"duration": duration, # integer, 3-30
"resolution": resolution, # "720p" or "1080p"
},
})
body = r.json()
if body["code"] != 200:
raise RuntimeError(body["message"])
task_id = body["data"]["taskId"]
while True:
d = requests.get(f"{BASE}/v1/tasks/{task_id}", headers=HEADERS).json()["data"]
if d["status"] == "success":
return d["output"][0]["url"]
if d["status"] == "fail":
raise RuntimeError(d)
time.sleep(10)
Download the returned MP4 straight away. The output URL carries an expireAt timestamp (about seven days out in our run) and is hosted on a temporary domain, so copy it into your own storage rather than hot-linking it.
Fields the API accepts
We probed the endpoint with deliberately invalid values (these return HTTP 400 INVALID_REQUEST and do not create a task):
| Field | Rule observed |
|---|---|
image_urls | Required array, exactly one item (two items → maxItems: got 2, want 1) |
duration | Integer, minimum 3, maximum 30. A string like "5" is rejected (got string, want integer) |
resolution | 720p or 1080p |
aspect_ratio | Optional: 16:9, 9:16, 1:1, 21:9. The model page says framing follows the source image when omitted |
prompt | Motion description; we sent requests without one in the validator probe only, so treat it as recommended |
Two things that trip people up when porting code from other video models:
- The field is
image_urls, notimage_url. Sendingimage_urlfails with both "missing required fieldimage_urls" and "additional propertiesimage_urlnot allowed". - There is no audio switch.
audio,generate_audioandsoundare each rejected as additional properties. Audio is always generated with the picture; steer it in the prompt (the example above asks for ambient wave sound).
Unknown fields are rejected rather than ignored, so a typo surfaces as a 400 instead of a silently ignored setting.
Production notes
- Prefer a callback over polling. Clips take minutes, so pass
callbackwithurlandwhen: "final"on task creation and handle the result in your webhook; keep polling only as a fallback. - Validate before you pay. Because invalid input returns 400 with no task created, check duration and resolution in your own code first; a bad request costs nothing but a good one bills for every second you ask for.
- Billing is per second and depends on resolution. Longer clips and 1080p cost more; see pricing for the current rates rather than relying on a number in an article.
- Save the task id. If a poll fails on a transient network error, re-poll the same id instead of resubmitting, which would create and bill a second clip.
Tested run
We submitted the request above: one portrait first frame, duration: 3, resolution: "720p", no aspect_ratio.
- Status sequence seen while polling every ~10 s:
handling→archiving→success. - Total wall time: about 274 seconds for a 3-second clip.
handlingaccounted for roughly 255 s; plan for minutes, not seconds, and design around a callback. - Output file (checked with
ffprobe): H.264 video at 1280×720, 3.04 s, plus an AAC audio track of the same length. So the audio is real, embedded in the MP4, and the framing followed the 16:9 source.
What we did not test: 1080p, durations above 3 seconds, and non-default aspect_ratio values. Those limits come from the API validation messages and the model page, not from generated output.
Related pages
- Kling 4.0 Image-to-Video model page
- Kling 4.0 Text-to-Video model page, for when you have no source image
- Kling 3.0 Image-to-Video model page
- How to use Kling 3.0 Turbo image-to-video
- hiapi pricing
- hiapi docs
FAQ
Can I turn off audio on Kling 4.0 image-to-video?
Not through a request field. audio, generate_audio and sound were all rejected by the validator. Describe the sound you want (or none) in the prompt.
How many input images can I send?
One. image_urls must contain exactly one first-frame image.
How long does a request take?
Our 3-second 720p clip took about 274 seconds end to end. Use a callback for production workloads.
What is the longest clip?
The API accepts duration up to 30 (integer seconds) and rejects higher values; we only generated a 3-second clip ourselves.





