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

# Video Generation

> Veo, Kling, and Seedance behind one endpoint. Submit, poll, get an MP4.

This is what \$3 buys — generated through this endpoint's models:

<Frame caption="Image-to-video: a still keyframe animated into a spot. Failed jobs refund automatically.">
  <video autoPlay muted loop playsInline src="https://cdn.photogenius.ai/new-ai-images/mcp-landing/v1/worlds/aera.mp4" />
</Frame>

Video is **async**: submit a job, poll its status, get an MP4 URL in one to a few minutes.

```mermaid theme={null}
flowchart LR
    A["POST /api/v1/video"] --> B["request_id"]
    B --> C{"GET /video/status<br/>every 10s — free"}
    C -->|processing| C
    C -->|completed| D["video_url (MP4)"]
    C -->|failed| E["Auto-refund"]
```

## The whole flow, copy-paste ready

<CodeGroup>
  ```python Python theme={null}
  import os, time, requests

  API = "https://www.ninjachat.ai"
  HEADERS = {"Authorization": f"Bearer {os.environ['NINJACHAT_API_KEY']}"}

  job = requests.post(f"{API}/api/v1/video", headers=HEADERS, json={
      "model": "veo-3.1-fast",
      "prompt": "A timelapse of a flower blooming, macro lens, soft lighting",
      "duration": 6,
      "aspect_ratio": "16:9",
  }).json()

  while True:
      s = requests.get(f"{API}/api/v1/video/status",
          headers=HEADERS, params={"request_id": job["request_id"]}).json()
      if s["status"] == "completed":
          print(s["result"]["video_url"]); break
      if s["status"] == "failed":
          print(f"Failed: {s.get('error')} — you were not charged"); break
      time.sleep(10)
  ```

  ```javascript Node.js theme={null}
  const API = "https://www.ninjachat.ai";
  const HEADERS = {
    "Authorization": `Bearer ${process.env.NINJACHAT_API_KEY}`,
    "Content-Type": "application/json",
  };

  const job = await fetch(`${API}/api/v1/video`, {
    method: "POST", headers: HEADERS,
    body: JSON.stringify({
      model: "veo-3.1-fast",
      prompt: "A timelapse of a flower blooming, macro lens, soft lighting",
      duration: 6, aspect_ratio: "16:9",
    }),
  }).then(r => r.json());

  while (true) {
    const s = await fetch(
      `${API}/api/v1/video/status?request_id=${encodeURIComponent(job.request_id)}`,
      { headers: HEADERS }).then(r => r.json());
    if (s.status === "completed") { console.log(s.result.video_url); break; }
    if (s.status === "failed") { console.log(`Failed: ${s.error} — not charged`); break; }
    await new Promise(r => setTimeout(r, 10000));
  }
  ```
</CodeGroup>

## Parameters

| Parameter          | Default        | Description                                                               |
| ------------------ | -------------- | ------------------------------------------------------------------------- |
| `prompt`           | required       | Camera movement + subject action + style + lighting. Max 4,000 chars.     |
| `model`            | `veo-3.1-fast` | See table below.                                                          |
| `duration`         | `8`            | Seconds — allowed values depend on the model.                             |
| `aspect_ratio`     | `16:9`         | `16:9` or `9:16` (ignored by Seedance 2).                                 |
| `image_url`        | —              | Animate this still (public HTTPS) — that's how the film above was made.   |
| `reference_images` | —              | Up to 4 public HTTPS image URLs to guide subject/style (Seedance 2 only). |
| `reference_video`  | —              | Public HTTPS video URL to guide motion/POV framing (Seedance 2 only).     |
| `reference_audio`  | —              | Public HTTPS audio URL to use as background music (Seedance 2 only).      |
| `generate_audio`   | `false`        | Generate an audio track for the video (Seedance 2 only).                  |
| `watermark`        | `false`        | Overlay a watermark on the output (Seedance 2 only).                      |

Polling `GET /video/status` is free and unlimited. Status moves `processing` (with `progress`) → `completed` (with `result.video_url`) or `failed` (auto-refunded).

## Which model?

| Model        | ID                  | Cost   | Duration | Renders in | Best for                      |
| ------------ | ------------------- | ------ | -------- | ---------- | ----------------------------- |
| Veo 3.1 Fast | `veo-3.1-fast`      | \$3.00 | 4/6/8s   | \~1-2 min  | The default — speed + quality |
| Google Veo 2 | `google-veo-2`      | \$5.00 | 5/6/8s   | \~40s      | Fastest turnaround            |
| Seedance 2   | `seedance-2`        | \$3.00 | 5–15s    | \~1-2 min  | Longest clips, unrestricted   |
| Kling Video  | `kling-video`       | \$3.00 | 5–10s    | \~3-4 min  | Motion coherence              |
| Veo 3 Fast   | `google-veo-3-fast` | \$3.00 | 4/6/8s   | \~1-2 min  | Budget Veo                    |
| Veo 3.1      | `veo-3.1`           | \$5.00 | 4/6/8s   | \~3-5 min  | Best quality                  |

## Prompting for motion

| Weak        | Strong                                                                                                               |
| ----------- | -------------------------------------------------------------------------------------------------------------------- |
| `mountains` | `Drone shot slowly ascending over a misty mountain valley at golden hour, camera reveals a river below, cinematic`   |
| `ocean`     | `Underwater camera glides through a coral reef, bioluminescent creatures pulse with light, slow motion, documentary` |

The formula: **camera movement + subject action + style + lighting**.

<Tip>
  Agents can run this whole flow conversationally — "animate the second one" — via the [MCP server](/mcp/tools).
</Tip>
