> This page is for Gateway.

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

# Video generation

> Generate video on Gateway through the async POST /v1/videos job flow: submit a prompt with optional media, poll status, download the MP4, and read per-second billing.

Video generation runs as an async job: submit a prompt and any media, poll until the job completes, then download the MP4.

## 1. Submit

**`cURL`**

```bash title="cURL"
curl https://api-gateway.merge.dev/v1/videos \
  -H "Authorization: Bearer $MERGE_GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax/minimax-h3",
    "prompt": "a red ball rolling on grass",
    "seconds": 4,
    "size": "768P"
  }'
```

**`Python`**

```python title="Python"
import os, requests

job = requests.post(
    "https://api-gateway.merge.dev/v1/videos",
    headers={"Authorization": f"Bearer {os.environ['MERGE_GATEWAY_API_KEY']}"},
    json={"model": "minimax/minimax-h3", "prompt": "a red ball rolling on grass", "seconds": 4, "size": "768P"},
).json()
video_id = job["id"]  # "vid_..."
```

The response is `202 Accepted` with a job whose `status` is `queued`.

To start from media, add public URLs in the fields the route expects; `capabilities.input` in the [model catalog](/merge-gateway/models/catalog) shows what it accepts. Gateway forwards extra fields unchanged, so field names, file limits, and rejections come from the vendor.

```json
{
  "model": "bytedance/seedance-2.5-reference-to-video",
  "prompt": "@Image1 walks along the beach in the style of @Video1, scored by @Audio1",
  "image_urls": ["https://cdn.acme.com/mascot.png"],
  "video_urls": ["https://cdn.acme.com/style-ref.mp4"],
  "audio_urls": ["https://cdn.acme.com/theme.mp3"],
  "seconds": 8
}
```

## 2. Poll

**`cURL`**

```bash title="cURL"
curl https://api-gateway.merge.dev/v1/videos/vid_XXXX \
  -H "Authorization: Bearer $MERGE_GATEWAY_API_KEY"
```

Poll until `status` is `completed`, usually one to a few minutes. The completed job carries the settled `cost`; a `failed` job carries the vendor `error` and bills nothing.

## 3. Download

**`cURL`**

```bash title="cURL"
curl https://api-gateway.merge.dev/v1/videos/vid_XXXX/content \
  -H "Authorization: Bearer $MERGE_GATEWAY_API_KEY" \
  -o video.mp4
```

The body is the raw MP4, or a JSON error if the job hasn't completed.

> **Warning**
>
> Gateway keeps a job record for 24 hours after its last update, after which the job ID returns `404 video_job_not_found`. Download the video soon after it completes.

## List and delete

**`cURL`**

```bash title="cURL"
curl "https://api-gateway.merge.dev/v1/videos?limit=20" \
  -H "Authorization: Bearer $MERGE_GATEWAY_API_KEY"

curl -X DELETE https://api-gateway.merge.dev/v1/videos/vid_XXXX \
  -H "Authorization: Bearer $MERGE_GATEWAY_API_KEY"
```

`GET /v1/videos` lists jobs from the last 24 hours, newest first. Pass the previous page's `last_id` as `after` for the next page, and `order=asc` for oldest first. Entries show each job's status from its last poll. `DELETE /v1/videos/{video_id}` removes a finished job; one still rendering returns `409 video_not_deletable`. Usage and billing history are kept. The OpenAI SDK's `client.videos.list()` and `client.videos.delete()` work against these endpoints; remix isn't supported.

## Reference

| Field      | Notes                                                                                                                           |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `model`    | Required. A canonical model ID, since `@alias/...` returns `400 alias_not_supported`.                                           |
| `prompt`   | Required                                                                                                                        |
| `seconds`  | Clip length, within the model's limits                                                                                          |
| `size`     | Output resolution. Use a key from `output_per_second_by_resolution`, such as `768P` on `minimax/minimax-h3`, or `WIDTHxHEIGHT`. |
| `vendor`   | Pin the execution vendor                                                                                                        |
| `customer` | Customer UUID to scope the key, budget, and usage to                                                                            |

The job object has `id`, `object`, `status`, `model`, `seconds`, `size`, `created_at`, `progress`, `error`, and `cost`. Any key in your organization can read a job.

Video bills per output second at the served resolution's rate in `output_per_second_by_resolution`, settled on the first poll or download that sees the job completed. Input media doesn't change the rate. `generate_audio` isn't forwarded, and the route bills at its with-audio rate. Customer budgets are checked at submit against the requested duration and tier.

## Next steps

#### [Image generation](/merge-gateway/capabilities/multimodal/image-generation)

Dedicated endpoint or inline in chat

#### [Multimodal overview](/merge-gateway/capabilities/multimodal/overview)

Every modality, endpoint, and surface