curl https://api.apipod.ai/v1/videos/generations \
-H "Authorization: Bearer $APIPOD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-4.0-i2v",
"prompt": "The girl in @first_frame walks out, passes the street in @keyframe_1 and arrives at the party in @last_frame.",
"image_urls": [
"https://cdn.example.com/first.png",
"https://cdn.example.com/last.png"
],
"keyframe_urls": [
"https://cdn.example.com/street.png"
],
"duration": 10,
"resolution": "720p"
}'import os
import requests
payload = {
"model": "kling-4.0-i2v",
"prompt": "The girl in @first_frame walks out, passes the street in @keyframe_1 and arrives at the party in @last_frame.",
"image_urls": [
"https://cdn.example.com/first.png",
"https://cdn.example.com/last.png"
],
"keyframe_urls": [
"https://cdn.example.com/street.png"
],
"duration": 10,
"resolution": "720p"
}
response = requests.post(
"https://api.apipod.ai/v1/videos/generations",
headers={
"Authorization": f"Bearer {os.environ['APIPOD_API_KEY']}",
"Content-Type": "application/json",
},
json=payload,
timeout=60,
)
response.raise_for_status()
print(response.json())package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := []byte(`{
"model": "kling-4.0-i2v",
"prompt": "The girl in @first_frame walks out, passes the street in @keyframe_1 and arrives at the party in @last_frame.",
"image_urls": [
"https://cdn.example.com/first.png",
"https://cdn.example.com/last.png"
],
"keyframe_urls": [
"https://cdn.example.com/street.png"
],
"duration": 10,
"resolution": "720p"
}`)
req, err := http.NewRequest(http.MethodPost, "https://api.apipod.ai/v1/videos/generations", bytes.NewReader(body))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("APIPOD_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(out))
}use reqwest::Client;
use serde_json::json;
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let payload = json!({
"model": "kling-4.0-i2v",
"prompt": "The girl in @first_frame walks out, passes the street in @keyframe_1 and arrives at the party in @last_frame.",
"image_urls": [
"https://cdn.example.com/first.png",
"https://cdn.example.com/last.png"
],
"keyframe_urls": [
"https://cdn.example.com/street.png"
],
"duration": 10,
"resolution": "720p"
});
let response = Client::new()
.post("https://api.apipod.ai/v1/videos/generations")
.bearer_auth(env::var("APIPOD_API_KEY")?)
.json(&payload)
.send()
.await?
.error_for_status()?;
println!("{}", response.text().await?);
Ok(())
}const response = await fetch("https://api.apipod.ai/v1/videos/generations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.APIPOD_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"model": "kling-4.0-i2v",
"prompt": "The girl in @first_frame walks out, passes the street in @keyframe_1 and arrives at the party in @last_frame.",
"image_urls": [
"https://cdn.example.com/first.png",
"https://cdn.example.com/last.png"
],
"keyframe_urls": [
"https://cdn.example.com/street.png"
],
"duration": 10,
"resolution": "720p"
}),
});
if (!response.ok) {
throw new Error(`APIPod request failed: ${response.status} ${await response.text()}`);
}
console.log(await response.json());{
"code": 200,
"message": "success",
"data": {
"task_id": "vid_task_01JEXAMPLE"
}
}Kling 4.0 Image to Video
Kling 4.0 I2V is APIPod’s keyframe image-to-video mode for Kling 4.0, generating 3-30 second clips that pass through a first frame, up to 8 middle keyframes and an optional last frame.
curl https://api.apipod.ai/v1/videos/generations \
-H "Authorization: Bearer $APIPOD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-4.0-i2v",
"prompt": "The girl in @first_frame walks out, passes the street in @keyframe_1 and arrives at the party in @last_frame.",
"image_urls": [
"https://cdn.example.com/first.png",
"https://cdn.example.com/last.png"
],
"keyframe_urls": [
"https://cdn.example.com/street.png"
],
"duration": 10,
"resolution": "720p"
}'import os
import requests
payload = {
"model": "kling-4.0-i2v",
"prompt": "The girl in @first_frame walks out, passes the street in @keyframe_1 and arrives at the party in @last_frame.",
"image_urls": [
"https://cdn.example.com/first.png",
"https://cdn.example.com/last.png"
],
"keyframe_urls": [
"https://cdn.example.com/street.png"
],
"duration": 10,
"resolution": "720p"
}
response = requests.post(
"https://api.apipod.ai/v1/videos/generations",
headers={
"Authorization": f"Bearer {os.environ['APIPOD_API_KEY']}",
"Content-Type": "application/json",
},
json=payload,
timeout=60,
)
response.raise_for_status()
print(response.json())package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := []byte(`{
"model": "kling-4.0-i2v",
"prompt": "The girl in @first_frame walks out, passes the street in @keyframe_1 and arrives at the party in @last_frame.",
"image_urls": [
"https://cdn.example.com/first.png",
"https://cdn.example.com/last.png"
],
"keyframe_urls": [
"https://cdn.example.com/street.png"
],
"duration": 10,
"resolution": "720p"
}`)
req, err := http.NewRequest(http.MethodPost, "https://api.apipod.ai/v1/videos/generations", bytes.NewReader(body))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("APIPOD_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(out))
}use reqwest::Client;
use serde_json::json;
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let payload = json!({
"model": "kling-4.0-i2v",
"prompt": "The girl in @first_frame walks out, passes the street in @keyframe_1 and arrives at the party in @last_frame.",
"image_urls": [
"https://cdn.example.com/first.png",
"https://cdn.example.com/last.png"
],
"keyframe_urls": [
"https://cdn.example.com/street.png"
],
"duration": 10,
"resolution": "720p"
});
let response = Client::new()
.post("https://api.apipod.ai/v1/videos/generations")
.bearer_auth(env::var("APIPOD_API_KEY")?)
.json(&payload)
.send()
.await?
.error_for_status()?;
println!("{}", response.text().await?);
Ok(())
}const response = await fetch("https://api.apipod.ai/v1/videos/generations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.APIPOD_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"model": "kling-4.0-i2v",
"prompt": "The girl in @first_frame walks out, passes the street in @keyframe_1 and arrives at the party in @last_frame.",
"image_urls": [
"https://cdn.example.com/first.png",
"https://cdn.example.com/last.png"
],
"keyframe_urls": [
"https://cdn.example.com/street.png"
],
"duration": 10,
"resolution": "720p"
}),
});
if (!response.ok) {
throw new Error(`APIPod request failed: ${response.status} ${await response.text()}`);
}
console.log(await response.json());{
"code": 200,
"message": "success",
"data": {
"task_id": "vid_task_01JEXAMPLE"
}
}Usage notes
- Frames:
image_urls[0]is the required first frame and an optionalimage_urls[1]is the last frame. Put middle frames inkeyframe_urlsin chronological order (up to 8). The output aspect ratio follows the first frame, soaspect_ratiois not accepted. - Frame limit: the total number of frames (first + keyframes + last) is capped by
duration: 3s → 4, 4s → 5, 5s → 6, 6s → 7, 7s → 8, 8s → 9, 9s or longer → 10. Exceeding the cap returns a 400 error before a task is created. - Mentioning frames: use
@first_frame,@keyframe_1..@keyframe_Nand@last_framein the prompt to describe what happens at each frame. Only frames you provided can be mentioned. - Audio: every clip includes native audio, so there is no
generate_audioparameter — sending it is rejected. - Not supported:
negative_prompt,multi_shot/multi_prompt, reference videos,kling_elements,voices, and 4K output.
Billing
Billed per second of output video. The per-second rate depends on the resolution (720p or 1080p) and does not change with the number of frames. Current prices are shown on the model page.Task flow
The create call returnsdata.task_id. Poll Query video task until status is completed (outputs in data.result) or failed, or pass callback_url to be notified when the task reaches a terminal state.Authorizations
Use your APIPod API key as a Bearer token in the Authorization header.
Body
Public APIPod model ID.
"kling-4.0-i2v"Required, up to 8,000 characters. Mention the frames you uploaded as @first_frame, @keyframe_1..@keyframe_N and @last_frame; only provided frames can be mentioned, otherwise the request is rejected.
1 - 8000Required. image_urls[0] is the first frame; an optional image_urls[1] is the last frame. Put middle frames in keyframe_urls. The output aspect ratio follows the first frame. first_frame_image / last_frame_image are accepted as aliases.
1 - 2 elementsOptional middle frames in chronological order (up to 8), mentioned as @keyframe_1..@keyframe_N. Requires a first frame; the total frame count is capped by duration.
8Output duration in seconds (3-30). Defaults to 5. Also caps the total number of frames (first + keyframes + last): 3s → 4, 4s → 5, 5s → 6, 6s → 7, 7s → 8, 8s → 9, 9s or longer → 10.
3 <= x <= 30Output resolution. Defaults to 720p. 4K is not available for Kling 4.0.
720p, 1080p HTTPS URL that receives the task's terminal state.
Was this page helpful?