> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kymaapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Video Generations

> Generate video clips. Asynchronous — returns 202 with a job_id; poll GET /v1/jobs/{id} for the result.

Async endpoint. The request returns immediately with a `job_id`; the actual generation runs in the background and is charged on success only. Billed per second of generated footage.

## Request body

<ParamField body="model" type="string" required>
  Kuaishou Kling: `kling-2.5-pro`, `kling-3-pro`, `kling-3-pro-audio`. ByteDance Seedance: `seedance-2-pro`, `seedance-2-fast`. MiniMax Hailuo: `hailuo-02-512p`, `hailuo-02-768p`, `hailuo-02-1080p`. Google Veo: `veo-3-fast`, `veo-3` (720p silent / 1080p+audio). See [Video Generation](/models/video-generation).

  <Note>
    Veo video SKUs (`veo-3-*`) also accept the same model id on `POST /v1/genmedia/jobs` — same billing, same response shape. `/v1/videos/generations` is the OpenAI-style alias.
  </Note>
</ParamField>

<ParamField body="prompt" type="string" required>
  Text prompt describing the desired video. Maximum 4000 characters.
</ParamField>

<ParamField body="duration" type="number" default="5">
  Clip length in seconds. Range 1–10. Each second is billed at the model's per-second rate.
</ParamField>

<ParamField body="image_url" type="string">
  HTTPS URL of a starting frame. When present, Kyma routes the request to the model's image-to-video variant — the image becomes the first frame and the prompt drives the motion. Optional; all five models accept it.
</ParamField>

<ParamField body="idempotency_key" type="string">
  Client-supplied key (≤128 chars) to make the request safe to retry. Repeated POSTs with the same `(api_key, idempotency_key)` pair return the same job — no duplicate charge, no duplicate generation.
</ParamField>

<ParamField body="extra" type="object">
  Pass-through model-specific parameters (e.g. `seed`, `aspect_ratio`). Forwarded to the underlying provider unchanged.
</ParamField>

## Response

`202 Accepted` for new jobs, `200 OK` if the same `idempotency_key` is replayed.

```json theme={null}
{
  "id": "v3a9c8b2e1d0a4b6c7d8e9f0",
  "object": "video.job",
  "status": "pending",
  "model": "kling-3-pro",
  "created_at": "2026-04-26T10:00:00.000Z",
  "completed_at": null,
  "output": null,
  "error": null,
  "estimated_cost": 0.7560,
  "charged_amount": null
}
```

<ResponseField name="id" type="string">Job ID. Use it to poll `GET /v1/jobs/{id}`.</ResponseField>
<ResponseField name="status" type="string">One of: `pending`, `processing`, `succeeded`, `failed`, `refunded`, `expired`.</ResponseField>
<ResponseField name="estimated_cost" type="number">Hold amount placed at submit time (USD) — `per_second_usd × duration`. Refunded if the job fails.</ResponseField>
<ResponseField name="charged_amount" type="number | null">Final billed amount once the job succeeds. `null` until then.</ResponseField>

## Poll for the result

```
GET /v1/jobs/{id}
```

Same response shape. Poll every 2–5 seconds until `status` is `succeeded`, `failed`, `refunded`, or `expired`. Most clips complete within 1–3 minutes; the runner times out at 10 minutes.

```json theme={null}
{
  "id": "v3a9c8b2e1d0a4b6c7d8e9f0",
  "object": "video.job",
  "status": "succeeded",
  "model": "kling-3-pro",
  "created_at": "2026-04-26T10:00:00.000Z",
  "completed_at": "2026-04-26T10:02:18.840Z",
  "output": {
    "url": "https://blob.vercel-storage.com/.../clip.mp4",
    "metadata": {
      "videos": [{
        "width": 1920,
        "height": 1080,
        "duration": 5,
        "content_type": "video/mp4"
      }]
    }
  },
  "error": null,
  "estimated_cost": 0.7560,
  "charged_amount": 0.7560
}
```

The `output.url` points to a Vercel Blob URL hosted by Kyma — the original upstream URL is rehosted so it remains stable after the upstream TTL expires.

## Errors

| Status | `error.code`                  | When                                      |
| ------ | ----------------------------- | ----------------------------------------- |
| `400`  | `invalid_model`               | Model is not a supported video model      |
| `400`  | `invalid_prompt`              | Missing prompt or > 4000 chars            |
| `400`  | `invalid_duration`            | `duration` is outside 1–10                |
| `401`  | `auth_error`                  | Missing or invalid API key                |
| `402`  | `insufficient_balance`        | Hold could not be placed                  |
| `413`  | `invalid_request`             | Request body > 25 MB                      |
| `429`  | `rate_limited`                | Tier limit exceeded                       |
| `500`  | `db_error` / `provider_error` | Internal failure — hold is fully refunded |

When `status` becomes `failed` or `refunded`, no charge is made and the held funds are returned to your balance via a `hold_release` ledger entry.

## Examples

### Cinematic hero clip

```bash theme={null}
curl -X POST https://kymaapi.com/v1/videos/generations \
  -H "Authorization: Bearer $KYMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kling-3-pro",
    "prompt": "an architectural fly-through of a glass-and-steel tower at sunset, cinematic anamorphic look",
    "duration": 5
  }'
```

### Talking-head with native audio

```bash theme={null}
curl -X POST https://kymaapi.com/v1/videos/generations \
  -H "Authorization: Bearer $KYMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kling-3-pro-audio",
    "prompt": "a barista pulling an espresso shot, the machine hisses, ambient cafe murmur",
    "duration": 5
  }'
```

### Image-to-video

```bash theme={null}
curl -X POST https://kymaapi.com/v1/videos/generations \
  -H "Authorization: Bearer $KYMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance-2-pro",
    "prompt": "the camera pulls back as the subject begins to walk forward",
    "image_url": "https://example.com/frame-1.jpg",
    "duration": 5
  }'
```

### Idempotent retry

```bash theme={null}
curl -X POST https://kymaapi.com/v1/videos/generations \
  -H "Authorization: Bearer $KYMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance-2-fast",
    "prompt": "a phone notification slides in from the right, soft chime, clean UI background",
    "duration": 5,
    "idempotency_key": "campaign-spring-asset-7"
  }'
```

Replaying this exact request returns the same job — no new charge.
