# Generate a video clip

URL: https://clippie.ai/developers/docs/generate-video

Generates a short AI video clip with an AI Studio model: Seedance and Veo from text or images, Clippie Recast and Genjutsu from a source video (referenceVideoUrl or motionTemplateId) plus photos. Spends credits, from tens to hundreds a clip depending on model, length and resolution (list_models has the prices). Run with dryRun: true (or estimate_cost) first and confirm the cost. Returns a job; clips take 1-10 minutes, so use get_job with waitSeconds.

## At a glance

| Property | Value |
| --- | --- |
| REST | `POST https://api.clippie.ai/v1/ai/videos` |
| MCP tool | `generate_video` |
| Scope | `create` |
| Cost | Spends credits or minutes, at the same prices as the app. Send `"dryRun": true` (or call [estimate_cost](https://clippie.ai/developers/docs/estimate-cost.md)) first to see the exact cost without spending anything. |
| Limit bucket | `ai_video` (see [Rate limits](https://clippie.ai/developers/docs.md#rate-limits)) |

## Parameters

| Name | In | Type | Required | Default | Description |
| --- | --- | --- | --- | --- | --- |
| `model` | body | string | Yes |  | Video model id from list_models (type video). One of `seedance-2-5`, `seedance-2-0`, `seedance-2-0-fast`, `seedance-2-0-mini`, `seedance-1-5-pro-251215`, `seedance-1-0-pro-250528`, `seedance-1-0-pro-fast-251015`, `veo-3.1-generate-preview`, `veo-3.1-fast-generate-preview`, `veo-3.0-generate-001`, `veo-3.0-fast-generate-001`, `clippie-recast`, `genjutsu-motion-transfer`, `genjutsu-object-swap`, `genjutsu-restyle`. |
| `prompt` | body | string | No | `""` | What happens in the clip. Optional for the source-video models. Up to 5000 chars. |
| `imageUrl` | body | string | No |  | First frame, or the character photo for Recast and Genjutsu: a file id from POST /v1/files (file_…) or a public https URL (copied into Clippie first, up to 100 MB). 1-2048 chars. |
| `lastFrameUrl` | body | string | No |  | Last frame (needs imageUrl): a file id from POST /v1/files (file_…) or a public https URL (copied into Clippie first, up to 100 MB). 1-2048 chars. |
| `referenceImages` | body | array of string | No |  | Reference images (Seedance 2, Veo 3.1), or more people and objects for Recast and Genjutsu. Up to 30 items. |
| `referenceVideos` | body | array of string | No |  | Seedance 2 reference videos. Up to 10 items. |
| `referenceAudios` | body | array of string | No |  | Seedance 2 reference audio. Up to 10 items. |
| `referenceVideoUrl` | body | string | No |  | Source video for Recast and Genjutsu: a file id from POST /v1/files (file_…) or a public https URL (copied into Clippie first, up to 100 MB). 1-2048 chars. |
| `motionTemplateId` | body | string | No |  | A motion template id from list_motion_templates, used as the source video instead of referenceVideoUrl. 1-120 chars. |
| `duration` | body | integer | No |  | Seconds, within the model's range (list_models). Source-video models take the source's length. 1-30. |
| `resolution` | body | string | No |  | Default: 720p when the model has it, else its first. One of `480p`, `720p`, `1080p`. |
| `aspectRatio` | body | string | No |  | Default 9:16 when the model has it. One of `auto`, `21:9`, `16:9`, `4:3`, `1:1`, `3:4`, `9:16`. |
| `withAudio` | body | boolean | No |  | Generate sound, for the models that can (Veo always does). |
| `presetId` | body | string (UUID) | No |  | Genjutsu Restyle look. |
| `seed` | body | integer | No |  |  |
| `cameraFixed` | body | boolean | No |  | Seedance 1.x: keep the camera still. |
| `dryRun` | body | boolean | No |  | Check the request and return { estimate } (credits and minutes) without spending anything. |
| `webhookUrl` | body | string (URL) | No |  | https URL that receives job.completed / job.failed, signed with your webhook secret. Up to 2048 chars. |
| `Idempotency-Key` | header | string | No |  | Retry-safe key: a retry with the same key and body within 24 hours returns the first job instead of starting (and charging) again. The same key with a different body is a 409. MCP clients pass it as the idempotencyKey argument. 1-255 chars. MCP: `idempotencyKey`. |

## Example

curl:

```bash
# 1. Dry run: the exact cost, nothing is spent.
curl https://api.clippie.ai/v1/ai/videos \
  -H "Authorization: Bearer $CLIPPIE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance-2-0-fast",
    "prompt": "A slow dolly shot down a neon-lit Tokyo street at night, light rain",
    "duration": 5,
    "aspectRatio": "9:16",
    "dryRun": true
  }'
# => { "dryRun": true, "estimate": { "credits": ..., "minutes": { ... }, "wouldSucceed": true } }

# 2. The real call. Keep the key and send the same one if you retry, so it is never charged twice.
KEY=$(uuidgen)
curl https://api.clippie.ai/v1/ai/videos \
  -H "Authorization: Bearer $CLIPPIE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $KEY" \
  -d '{
    "model": "seedance-2-0-fast",
    "prompt": "A slow dolly shot down a neon-lit Tokyo street at night, light rain",
    "duration": 5,
    "aspectRatio": "9:16"
  }'
# => 202 { "job": { "id": "job_...", "status": "queued", ... } }
```

generate-video.mjs:

```js
import { randomUUID } from 'node:crypto';

const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` };

const input = {
  "model": "seedance-2-0-fast",
  "prompt": "A slow dolly shot down a neon-lit Tokyo street at night, light rain",
  "duration": 5,
  "aspectRatio": "9:16"
};

async function call(body, headers = {}) {
  const res = await fetch('https://api.clippie.ai/v1/ai/videos', {
    method: 'POST',
    headers: { ...auth, 'Content-Type': 'application/json', ...headers },
    body: JSON.stringify(body),
  });
  const json = await res.json();
  if (!res.ok) throw new Error(`${json.error.type}: ${json.error.message}`);
  return json;
}

// 1. Dry run: the exact cost, nothing is spent. Confirm it before going on.
const { estimate } = await call({ ...input, dryRun: true });
console.log(estimate); // { credits, minutes, wouldSucceed, reason }

// 2. Create. Keep the key and reuse it on retries, so a retry never charges twice.
let { job } = await call(input, { 'Idempotency-Key': randomUUID() });

// 3. Wait. Each call holds for up to 30 seconds until the job changes.
while (!['completed', 'failed', 'canceled'].includes(job.status)) {
  const res = await fetch(`https://api.clippie.ai/v1/jobs/${job.id}?wait=30`, { headers: auth });
  ({ job } = await res.json());
}
console.log(job.status, job.output);
```

## Response

`202 { job }`: the job, straight away. Wait for it with [Get job](https://clippie.ai/developers/docs/get-job.md) (`?wait=30`) or a `webhookUrl`; see [Jobs and waiting](https://clippie.ai/developers/docs.md#jobs). With `"dryRun": true` the answer is `200 { dryRun: true, estimate }` and nothing is started.

202 Accepted:

```json
{
  "job": {
    "id": "job_abc123",
    "object": "job",
    "kind": "generate_video",
    "status": "queued",
    "step": null,
    "progress": null,
    "input": {
      "prompt": "The lost library of Alexandria"
    },
    "output": null,
    "cost": {
      "credits": 0,
      "minutes": {
        "voiceover": 1,
        "captions": 1,
        "export": 1
      }
    },
    "error": null,
    "source": "api",
    "webhookUrl": null,
    "createdAt": "2026-10-24T12:00:00.000Z",
    "startedAt": null,
    "completedAt": null
  }
}
```

Dry run (200), shape only:

```json
{
  "dryRun": true,
  "estimate": {
    "credits": 0,
    "minutes": {
      "voiceover": 0,
      "captions": 0,
      "export": 0
    },
    "wouldSucceed": true
  }
}
```

## From an MCP client

Your AI calls `generate_video` with the same fields as JSON arguments, plus `idempotencyKey` instead of the header. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one.

tools/call:

```json
{
  "name": "generate_video",
  "arguments": {
    "model": "seedance-2-0-fast",
    "prompt": "A slow dolly shot down a neon-lit Tokyo street at night, light rain",
    "duration": 5,
    "aspectRatio": "9:16",
    "idempotencyKey": "a-unique-key-per-thing-you-make"
  }
}
```
