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

# Video generation

> Submit a text-to-video job and poll for the rendered MP4.

Unlike chat, image, and audio endpoints, video generation is **asynchronous**. `POST /v1/videos/generations` returns a job id immediately and queues the render; you poll `GET /v1/videos/generations/{id}` until `status` is `completed`, at which point the MP4 is in `data[0].b64_json`.

A 2-second clip typically takes 50–90 seconds end-to-end. Jobs are retained for 24 hours after completion. Find video-capable models with `supports` containing `video_generation` — see [model discovery](/inference-api/guides/model-discovery#video-generation).

## Submit a job

<CodeGroup>
  ```bash cURL theme={null}
  curl https://tokens.flex.ai/v1/videos/generations \
    -H "Authorization: Bearer $FLEXAI_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "Wan2.2-T2V-A14B-Diffusers",
      "prompt": "a paper boat sailing across a puddle, slow motion",
      "duration_seconds": 2
    }'
  ```

  ```python Python theme={null}
  import os, requests

  resp = requests.post(
      "https://tokens.flex.ai/v1/videos/generations",
      headers={"Authorization": f"Bearer {os.environ['FLEXAI_API_KEY']}"},
      json={
          "model": "Wan2.2-T2V-A14B-Diffusers",
          "prompt": "a paper boat sailing across a puddle, slow motion",
          "duration_seconds": 2,
      },
  )
  resp.raise_for_status()
  job_id = resp.json()["id"]
  ```

  ```typescript TypeScript theme={null}
  const resp = await fetch("https://tokens.flex.ai/v1/videos/generations", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.FLEXAI_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "Wan2.2-T2V-A14B-Diffusers",
      prompt: "a paper boat sailing across a puddle, slow motion",
      duration_seconds: 2,
    }),
  });
  const { id: jobId } = await resp.json();
  ```
</CodeGroup>

The response is `202 Accepted` with `{ "id": "...", "status": "queued", "created": 1748902000 }`.

## Poll for the result

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

  while True:
      poll = requests.get(
          f"https://tokens.flex.ai/v1/videos/generations/{job_id}",
          headers={"Authorization": f"Bearer {os.environ['FLEXAI_API_KEY']}"},
      ).json()
      if poll["status"] == "completed":
          with open("out.mp4", "wb") as f:
              f.write(base64.b64decode(poll["data"][0]["b64_json"]))
          break
      if poll["status"] == "failed":
          raise RuntimeError(poll.get("error"))
      time.sleep(5)
  ```

  ```typescript TypeScript theme={null}
  import fs from "node:fs";

  while (true) {
    const poll = await fetch(`https://tokens.flex.ai/v1/videos/generations/${jobId}`, {
      headers: { Authorization: `Bearer ${process.env.FLEXAI_API_KEY}` },
    }).then((r) => r.json());

    if (poll.status === "completed") {
      fs.writeFileSync("out.mp4", Buffer.from(poll.data[0].b64_json, "base64"));
      break;
    }
    if (poll.status === "failed") throw new Error(JSON.stringify(poll.error));
    await new Promise((r) => setTimeout(r, 5000));
  }
  ```

  ```bash cURL theme={null}
  # Poll every 5 seconds; pipe to base64 once status is "completed".
  JOB_ID=...
  while true; do
    STATUS=$(curl -s -H "Authorization: Bearer $FLEXAI_API_KEY" \
      "https://tokens.flex.ai/v1/videos/generations/$JOB_ID" | jq -r '.status')
    [ "$STATUS" = "completed" ] && break
    [ "$STATUS" = "failed" ] && { echo "job failed"; exit 1; }
    sleep 5
  done

  curl -s -H "Authorization: Bearer $FLEXAI_API_KEY" \
    "https://tokens.flex.ai/v1/videos/generations/$JOB_ID" \
    | jq -r '.data[0].b64_json' | base64 -d > out.mp4
  ```
</CodeGroup>

## Status values

| `status`     | Meaning                                 |
| ------------ | --------------------------------------- |
| `queued`     | Accepted but not yet on a GPU.          |
| `processing` | Render in progress.                     |
| `completed`  | `data[0].b64_json` holds the MP4 bytes. |
| `failed`     | `error` is populated with details.      |

## Request fields

| Field              | Required | Notes                                                           |
| ------------------ | -------- | --------------------------------------------------------------- |
| `model`            | Yes      | Must have `video_generation` in `supports`.                     |
| `prompt`           | Yes      | 1–2000 characters; must not be blank.                           |
| `duration_seconds` | No       | 1–5, default 2. Longer clips proportionally extend render time. |

## Polling cadence

Poll every 5–10 seconds; faster polling won't speed up the render and counts against your rate limit. The poll endpoint is cheap (no GPU cost) but still rate-limited per key.

## Billing

Video models bill per output second — see `pricing[]` on `GET /api/models` for the active rate, or the [billing reference](/inference-api/reference/billing).
