> This page is for version v2 (default).
> For other versions, use one of these documentation indexes:
> - v2 (default): https://developer.ideogram.ai/v2/llms.txt
> - v1: https://developer.ideogram.ai/v1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.ideogram.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.ideogram.ai/_mcp/server.

# Generate a video with Kling 4.0

POST https://api.ideogram.ai/v2/video/generate/kling-4
Content-Type: multipart/form-data

Generate a video from a text prompt with Kling 4.0, at up to 1080p and
30 seconds. Every other input is optional and they combine freely:

* Frames: up to 10 `frames` in timeline order. The first opens the video,
  the last of two or more closes it, and the video passes through the
  rest in between.
* References: up to 10 `reference_images` and 5 reference videos the
  prompt refers to as `[Image1]`, `[Video1]`, and so on.

Upload images using `multipart/form-data`, or pass stored assets by
identifier. Returns a `generation_id`; poll
`GET /v2/generations/{generation_id}` or supply a `webhook_url`.

Reference: https://developer.ideogram.ai/api-reference/video/generate/kling-4

## Authentication

- `Api-Key` header (required) — API key for access control. Use in the header with the name \"Api-Key\"

## Request

### Query parameters

- `dry_run` (boolean, optional, default: false) — When true, the request is validated and priced but not run: nothing is generated, stored, or billed, and no safety review is performed. The response is a `PriceQuote` object instead of the usual response for this endpoint. Send exactly the request you would send to generate, so the quote reflects the same options.

### Body (multipart/form-data)

This endpoint expects a multipart form with multiple files.

- `prompt` (string, required) — A natural-language prompt describing the video to generate. Refer to references by position, as in "[Image1] walks toward the camera".
- `frames` (files, optional) — Images for the video to pass through, in timeline order, in a common format such as JPEG, PNG, or WEBP, up to 50MB each. The first opens the video and, with two or more, the last closes it. Multipart requests only. Uploaded images are used for this request only and are not stored.
- `reference_images` (files, optional) — Reference images, in prompt order. Common formats such as JPEG, PNG, and WEBP are supported, up to 50MB each. Multipart requests only. Uploaded images are used for this request only and are not stored.
- `aspect_ratio` (enum, optional)
- `resolution` (enum, optional)
- `duration` (integer, optional) — The length of the generated video in seconds.
- `webhook_url` (string, optional) — HTTPS URL that Ideogram delivers the generated result to. Ideogram sends a JSON POST to this URL once all images for the request have finished generating. The body mirrors the synchronous generate response: `request_id`, `created`, and a `data` array containing every generated image (`url`, `prompt`, `resolution`, `seed`, `is_image_safe`). Each delivery is signed with Ed25519 and verifiable against the public keys at `https://api.ideogram.ai/v1/.well-known/jwks.json`. Must be HTTPS; private and loopback hosts and the cloud metadata service are rejected.

## Response

### 200

An acknowledgement to poll with `GET /v2/generations/{generation_id}`.

- `generation_id` (string, required) — The generation ID to poll with `GET /v2/generations/{generation_id}`.
- `created` (datetime, required) — The time the request was accepted.

## Errors

### 400 Bad Request Error

Invalid input provided.

- `any`

### 401 Unauthorized Error

Unauthorized.

- `any`

### 402 Payment Required Error

Insufficient credits or quota.

- `error` (string, required) — A message describing why the generation request was rejected.
- `reject_reason` (enum, required) — The account or usage limit that rejected a generation request.
  - Allowed values: `insufficient_funds`, `subscription_required`, `daily_limit`, `priority_credit_required`, `inflight_limit`, `feature_limit`, `app_user_limit`
- `max_inflight_requests` (integer, optional) — How many generations the account may have in progress at once on the queue this request resolved to. Present when `reject_reason` is `inflight_limit`.
- `task_completion_speed` (enum, optional) — The queue this request resolved to. Present when `reject_reason` is `inflight_limit`.
  - Allowed values: `fast`, `slow`

### 403 Forbidden Error

This account does not have access to this model.

- `any`

### 404 Not Found Error

A frame or reference was not found.

- `any`

### 422 Unprocessable Entity Error

The prompt did not pass safety checks.

- `any`

### 429 Too Many Requests Error

Too many requests.

- `error` (string, required) — A message describing why the generation request was rejected.
- `reject_reason` (enum, required) — The account or usage limit that rejected a generation request.
  - Allowed values: `insufficient_funds`, `subscription_required`, `daily_limit`, `priority_credit_required`, `inflight_limit`, `feature_limit`, `app_user_limit`
- `max_inflight_requests` (integer, optional) — How many generations the account may have in progress at once on the queue this request resolved to. Present when `reject_reason` is `inflight_limit`.
- `task_completion_speed` (enum, optional) — The queue this request resolved to. Present when `reject_reason` is `inflight_limit`.
  - Allowed values: `fast`, `slow`

### 500 Internal Server Error

Internal server error.

- `any`

### 503 Service Unavailable Error

The endpoint is temporarily unavailable.

- `any`

## Examples

**Request**

```json
{
  "frames": [],
  "prompt": "A red fox running through a snowy forest at dawn.",
  "reference_images": []
}
```

**Response**

```json
{
  "generation_id": "generation_id",
  "created": "2024-01-15T09:30:00Z"
}
```

**SDK Code**

```python
import requests

url = "https://api.ideogram.ai/v2/video/generate/kling-4"

payload = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA red fox running through a snowy forest at dawn.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"webhook_url\"\r\n\r\n\r\n-----011000010111000001101001--\r\n"
headers = {
    "Api-Key": "<apiKey>",
    "Content-Type": "multipart/form-data; boundary=---011000010111000001101001"
}

response = requests.post(url, data=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.ideogram.ai/v2/video/generate/kling-4';
const form = new FormData();
form.append('aspect_ratio', '');
form.append('duration', '');
form.append('prompt', 'A red fox running through a snowy forest at dawn.');
form.append('resolution', '');
form.append('webhook_url', '');

const options = {method: 'POST', headers: {'Api-Key': '<apiKey>'}};

options.body = form;

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ideogram.ai/v2/video/generate/kling-4"

	payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA red fox running through a snowy forest at dawn.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"webhook_url\"\r\n\r\n\r\n-----011000010111000001101001--\r\n")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Api-Key", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.ideogram.ai/v2/video/generate/kling-4")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Api-Key"] = '<apiKey>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA red fox running through a snowy forest at dawn.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"webhook_url\"\r\n\r\n\r\n-----011000010111000001101001--\r\n"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.ideogram.ai/v2/video/generate/kling-4")
  .header("Api-Key", "<apiKey>")
  .body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA red fox running through a snowy forest at dawn.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"webhook_url\"\r\n\r\n\r\n-----011000010111000001101001--\r\n")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.ideogram.ai/v2/video/generate/kling-4', [
  'multipart' => [
    [
        'name' => 'prompt',
        'contents' => 'A red fox running through a snowy forest at dawn.'
    ]
  ]
  'headers' => [
    'Api-Key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.ideogram.ai/v2/video/generate/kling-4");
var request = new RestRequest(Method.POST);
request.AddHeader("Api-Key", "<apiKey>");
request.AddParameter("undefined", "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA red fox running through a snowy forest at dawn.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"webhook_url\"\r\n\r\n\r\n-----011000010111000001101001--\r\n", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Api-Key": "<apiKey>"]
let parameters = [
  [
    "name": "aspect_ratio",
    "value": 
  ],
  [
    "name": "duration",
    "value": 
  ],
  [
    "name": "prompt",
    "value": "A red fox running through a snowy forest at dawn."
  ],
  [
    "name": "resolution",
    "value": 
  ],
  [
    "name": "webhook_url",
    "value": 
  ]
]

let boundary = "---011000010111000001101001"

var body = ""
var error: NSError? = nil
for param in parameters {
  let paramName = param["name"]!
  body += "--\(boundary)\r\n"
  body += "Content-Disposition:form-data; name=\"\(paramName)\""
  if let filename = param["fileName"] {
    let contentType = param["content-type"]!
    let fileContent = String(contentsOfFile: filename, encoding: String.Encoding.utf8)
    if (error != nil) {
      print(error as Any)
    }
    body += "; filename=\"\(filename)\"\r\n"
    body += "Content-Type: \(contentType)\r\n\r\n"
    body += fileContent
  } else if let paramValue = param["value"] {
    body += "\r\n\r\n\(paramValue)"
  }
}

let request = NSMutableURLRequest(url: NSURL(string: "https://api.ideogram.ai/v2/video/generate/kling-4")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```