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

> ## Agent Instructions
> The Deepshi API is an OpenAI-compatible gateway. Base URL: https://api.deepshi.ai/v1. API keys start with sk-bf- and go in the Authorization: Bearer header. Prefer the official OpenAI SDKs pointed at the Deepshi base URL. Model ids are clean with no provider prefix (e.g. deepshi-3.0, claude-opus-4.8, gpt-5.5). Chat and image requests are synchronous; video and music requests are asynchronous job APIs (create, then poll). Every synchronous response carries usage.cost.total_cost in USD. Do not reference the internal /api/* admin plane or virtual keys.

# Video generation

> Generate video from a text prompt or an image (text-to-video and image-to-video) with the async video API.

The video API turns a text prompt (or an image) into a short video. Generation takes from several seconds to a few minutes, so the API is asynchronous: you create a job, poll it until it finishes, then read the result URL.

| Endpoint                      | What it does                         |
| ----------------------------- | ------------------------------------ |
| `POST /v1/videos`             | Create a video job from a prompt     |
| `GET /v1/videos/{id}`         | Poll a job for its status and result |
| `GET /v1/videos/{id}/content` | Redirect to the finished video file  |
| `DELETE /v1/videos/{id}`      | Cancel or delete a job               |

New here? The three steps below take you from a prompt to a saved `.mp4`.

## Step 1: Create a job

A request needs a **`model`** and a **`prompt`**. Browse the ids on the [video models](/models/video-models) page.

```bash curl theme={null}
curl https://api.deepshi.ai/v1/videos \
  -H "Authorization: Bearer $DEEPSHI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "grok-imagine-video",
    "prompt": "a paper boat drifting down a rain-soaked street",
    "resolution": "480p",
    "duration": 4
  }'
```

You get back a job object with `status: "queued"`. Save the `id` so you can poll it in Step 2.

```json theme={null}
{
  "id": "vid_a1b2c3...",
  "object": "video",
  "model": "grok-imagine-video",
  "status": "queued",
  "size": "480p",
  "seconds": "4"
}
```

| Field     | What it is                                                          |
| --------- | ------------------------------------------------------------------- |
| `id`      | The job id. Pass it to the poll, content, and delete endpoints.     |
| `status`  | Lifecycle state: `queued`, `in_progress`, `completed`, or `failed`. |
| `size`    | The resolution the job will bill at.                                |
| `seconds` | The clip length the job will bill at, in seconds.                   |

## Step 2: Poll for completion

Call `GET /v1/videos/{id}` on an interval (every few seconds is plenty) until `status` is `completed` or `failed`. A completed job carries the result under `videos[]`.

```json theme={null}
{
  "id": "vid_a1b2c3...",
  "object": "video",
  "model": "grok-imagine-video",
  "status": "completed",
  "size": "480p",
  "seconds": "4",
  "created_at": 1783112379,
  "videos": [
    { "type": "url", "url": "https://media.cdn-deepshi.com/videos/....mp4", "content_type": "video/mp4" }
  ]
}
```

| Field                   | What it is                                                                  |
| ----------------------- | --------------------------------------------------------------------------- |
| `videos[].url`          | A direct link to the finished `.mp4`.                                       |
| `videos[].content_type` | The media type, always `video/mp4`.                                         |
| `status: "failed"`      | Generation failed. An `error` object explains why, and you are not charged. |

## Step 3: Get the file

Use `videos[].url` directly, or call `GET /v1/videos/{id}/content` to be redirected to the same file. The full loop looks like this:

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

  BASE = "https://api.deepshi.ai/v1"
  headers = {"Authorization": "Bearer YOUR_DEEPSHI_API_KEY"}

  # 1. create
  job = requests.post(f"{BASE}/videos", headers=headers, json={
      "model": "grok-imagine-video",
      "prompt": "a paper boat drifting down a rain-soaked street",
      "resolution": "480p",
      "duration": 4,
  }).json()

  # 2. poll
  while job["status"] not in ("completed", "failed"):
      time.sleep(5)
      job = requests.get(f"{BASE}/videos/{job['id']}", headers=headers).json()

  if job["status"] == "failed":
      raise SystemExit(job.get("error"))

  # 3. download
  url = job["videos"][0]["url"]
  open("output.mp4", "wb").write(requests.get(url).content)
  print("saved output.mp4")
  ```

  ```javascript JavaScript theme={null}
  const BASE = "https://api.deepshi.ai/v1";
  const headers = {
    Authorization: "Bearer YOUR_DEEPSHI_API_KEY",
    "Content-Type": "application/json",
  };

  // 1. create
  let job = await (await fetch(`${BASE}/videos`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      model: "grok-imagine-video",
      prompt: "a paper boat drifting down a rain-soaked street",
      resolution: "480p",
      duration: 4,
    }),
  })).json();

  // 2. poll
  while (!["completed", "failed"].includes(job.status)) {
    await new Promise((r) => setTimeout(r, 5000));
    job = await (await fetch(`${BASE}/videos/${job.id}`, { headers })).json();
  }
  if (job.status === "failed") throw new Error(JSON.stringify(job.error));

  // 3. download
  const url = job.videos[0].url;
  const buf = Buffer.from(await (await fetch(url)).arrayBuffer());
  require("node:fs").writeFileSync("output.mp4", buf);
  console.log("saved output.mp4");
  ```

  ```bash curl theme={null}
  # 1. create -> capture the id
  ID=$(curl -s https://api.deepshi.ai/v1/videos \
    -H "Authorization: Bearer $DEEPSHI_API_KEY" -H "Content-Type: application/json" \
    -d '{"model":"grok-imagine-video","prompt":"a paper boat drifting down a rain-soaked street","resolution":"480p","duration":4}' \
    | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])")

  # 2. poll until it is done
  while true; do
    STATUS=$(curl -s https://api.deepshi.ai/v1/videos/$ID \
      -H "Authorization: Bearer $DEEPSHI_API_KEY" \
      | python3 -c "import sys,json;print(json.load(sys.stdin)['status'])")
    echo "status: $STATUS"; [ "$STATUS" = completed ] || [ "$STATUS" = failed ] && break
    sleep 5
  done

  # 3. follow the content redirect to the file
  curl -sL https://api.deepshi.ai/v1/videos/$ID/content \
    -H "Authorization: Bearer $DEEPSHI_API_KEY" -o output.mp4
  ```
</CodeGroup>

<Note>
  A video id is scoped to the key that created it. Another key cannot poll, download, or delete it.
  Download anything you want to keep as soon as the job completes.
</Note>

## Image to video

To animate a still image, add `input_reference` with a public image URL or a `data:` URI. The model runs in image-to-video mode. Only models whose [model card](/models/video-models) lists image-to-video support accept it.

```json theme={null}
{
  "model": "wan-2.5",
  "prompt": "the boat sails forward as the camera pulls back",
  "input_reference": "https://example.com/boat.png",
  "resolution": "480p",
  "duration": 5
}
```

## Choosing a model

Each video model supports different resolutions, durations, aspect ratios, and audio options. There are two ways to see them:

* The [video models](/models/video-models) page, with a card per model showing its resolution tiers, prompt limit, and whether it supports image-to-video and audio.
* `GET /v1/models`, the same facts as JSON, scoped to your key.

```bash theme={null}
curl https://api.deepshi.ai/v1/models \
  -H "Authorization: Bearer $DEEPSHI_API_KEY"
```

Each video entry lists the `parameters` it accepts (with `type`, allowed `values`, and `default`), plus `prompt_max_length` and its modalities:

```json Example video model entry theme={null}
{
  "id": "veo3.1",
  "object": "model",
  "output_modalities": ["video"],
  "input_modalities": ["text", "image"],
  "prompt_max_length": 20000,
  "parameters": {
    "resolution":     { "type": "enum",    "values": ["720p", "1080p", "4k"], "default": "720p" },
    "duration":       { "type": "enum",    "values": [4, 6, 8], "default": 8, "unit": "seconds" },
    "aspect_ratio":   { "type": "enum",    "values": ["16:9", "9:16"], "default": "16:9" },
    "generate_audio": { "type": "boolean", "default": true }
  }
}
```

### Common parameters

These apply across video models. A model ignores any field it does not support, so don't carry one model's parameters over to another.

<ParamField body="model" type="string" required>
  The video model id. Take it from `GET /v1/models`.
</ParamField>

<ParamField body="prompt" type="string" required>
  What to generate. Each model caps the length; over its `prompt_max_length` returns `400`.
</ParamField>

<ParamField body="input_reference" type="string">
  A public image URL or `data:` URI. Switches the model to image-to-video. Only models whose card lists image-to-video support accept it.
</ParamField>

<ParamField body="resolution" type="string">
  Resolution tier, such as `480p`, `720p`, `1080p`, or `4k`. Allowed values are per model. Sets the billed price on resolution-priced models.
</ParamField>

<ParamField body="duration" type="integer">
  Clip length in seconds. An enum for some models (for example `4`, `6`, `8`), a range for others. The billed length.
</ParamField>

<ParamField body="aspect_ratio" type="string">
  Aspect ratio such as `16:9` or `9:16`. Some models add `auto` in image-to-video mode.
</ParamField>

<ParamField body="generate_audio" type="boolean">
  Generate a soundtrack, on models that support audio.
</ParamField>

<ParamField body="negative_prompt" type="string">
  What to avoid in the output, on models that support it.
</ParamField>

<ParamField body="seed" type="integer">
  Seed for reproducible output, on models that support it. Omit for a random seed.
</ParamField>

## Pricing

Video is priced **per second** of output. The final cost is the model's per-second rate times the clip `duration`. The rate can depend on the `resolution` and whether audio is on. Per-model rates are on [deepshi.ai](https://deepshi.ai/).

You are billed only for **delivered output**. A job is charged once, when it completes. A job that fails, or that you cancel before it finishes, is not charged.

Unlike image responses, video job objects do not carry an inline `usage.cost`. Check per-model rates and your balance on [deepshi.ai](https://deepshi.ai/).

## Canceling a job

`DELETE /v1/videos/{id}` cancels a job that is still running and stops it from billing, or removes a job you no longer need. It returns a confirmation:

```json theme={null}
{ "id": "vid_a1b2c3...", "deleted": true, "object": "video.deleted" }
```

Canceling before the video is delivered means you are not charged. A job that already completed has already billed, so canceling it does not refund it.

## Next steps

<CardGroup cols={2}>
  <Card title="Video models" icon="clapperboard" href="/models/video-models">
    Browse video models, their resolution tiers, and audio support.
  </Card>

  <Card title="Credits & billing" icon="coins" href="/get-started/pricing-credits">
    How billing and your prepaid balance work.
  </Card>
</CardGroup>
