> For the complete documentation index, see [llms.txt](https://lanbridge.gitbook.io/lanbridge-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lanbridge.gitbook.io/lanbridge-docs/api/video-generation.md).

# Video Generation

Generate clips from text, a first/last frame, or reference assets. This is **not** Chat Completions. Video takes seconds to minutes, so LanBridge tracks each request as a job.

**Base URL:** <https://www.lanbridge.net/api/v1\\>
**Auth:** Authorization: Bearer $LANBRIDGE\_API\_KEY

### Flow

1. POST /api/v1/videos — submit, get a job id
2. GET /api/v1/videos/{jobId} — poll until completed
3. GET /api/v1/videos/{jobId}/content — download the MP4

### Discover models

Capabilities differ by slug. Always read this list before you submit.

Bash

```
curl https://www.lanbridge.net/api/v1/videos/models \
  -H "Authorization: Bearer $LANBRIDGE_API_KEY"
```

Each item includes id, supported\_durations, supported\_resolutions, supported\_aspect\_ratios, supported\_frame\_images, generate\_audio, allowed\_passthrough\_parameters, and pricing\_skus.

Video models are **not** on GET /api/v1/models. Use /videos/models.

Suggested slugs (confirm on the catalog):

* google/veo-3.1
* google/veo-3.1-lite
* bytedance/seedance-2.5
* bytedance/seedance-2.0
* alibaba/wan-2.7
* openai/sora-2-pro
* x-ai/grok-imagine-video

### Submit a job

Bash

```
curl -X POST https://www.lanbridge.net/api/v1/videos \
  -H "Authorization: Bearer $LANBRIDGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/veo-3.1",
    "prompt": "A cinematic 8-second shot of a glass greenhouse at sunrise, soft mist, slow dolly-in",
    "duration": 8,
    "resolution": "720p",
    "aspect_ratio": "16:9",
    "generate_audio": true
  }'
```

**202 response**

JSON

```
{
  "id": "job-abc123",
  "generation_id": "gen-xyz789",
  "polling_url": "/api/v1/videos/job-abc123",
  "status": "pending"
}
```

If polling\_url is a path, resolve it against <https://www.lanbridge.net>.

### Request fields

| Field             | Type    | Required | Notes                                                      |
| ----------------- | ------- | -------- | ---------------------------------------------------------- |
| model             | string  | Yes      | e.g. google/veo-3.1                                        |
| prompt            | string  | Usually  | Required except on image-only models                       |
| duration          | integer | No       | Seconds; must be in supported\_durations                   |
| resolution        | string  | No       | 480p, 720p, 768p, 1080p, 1K, 2K, 4K                        |
| aspect\_ratio     | string  | No       | 16:9, 9:16, 1:1, 4:3, 3:4, 3:2, 2:3, 21:9, 9:21            |
| size              | string  | No       | 1280x720 — interchangeable with resolution + aspect\_ratio |
| generate\_audio   | boolean | No       | Default true when the model supports audio                 |
| frame\_images     | array   | No       | Exact first/last frame → image-to-video                    |
| input\_references | array   | No       | Style / character / setting → reference-to-video           |
| seed              | integer | No       | Best-effort; not guaranteed upstream                       |
| callback\_url     | uri     | No       | HTTPS webhook on terminal status                           |
| provider          | object  | No       | Passthrough for model-specific knobs                       |
| in\_painting      | string  | No       | Continue a prior gen-vid-… id                              |
| session\_id       | string  | No       | Group related jobs, max 256 chars                          |
| user              | string  | No       | End-user id, max 256 chars                                 |

If both frame\_images and input\_references are sent, the request is treated as image-to-video.

### Image-to-video

JSON

```
{
  "model": "alibaba/wan-2.7",
  "prompt": "The camera slowly pushes in as the subject turns toward warm window light",
  "duration": 4,
  "resolution": "720p",
  "aspect_ratio": "16:9",
  "frame_images": [
    {
      "type": "image_url",
      "image_url": { "url": "https://example.com/first-frame.png" },
      "frame_type": "first_frame"
    }
  ]
}
```

frame\_type is first\_frame or last\_frame. image\_url.url can be HTTPS or a data URI.

### Reference-to-video

JSON

```
{
  "model": "x-ai/grok-imagine-video",
  "prompt": "The same character walks across a rainy neon street, cinematic, 24fps",
  "duration": 8,
  "resolution": "720p",
  "input_references": [
    {
      "type": "image_url",
      "image_url": { "url": "https://example.com/character.png" }
    }
  ]
}
```

Providers cannot fetch URLs behind logins, bot checks, or multi-hop redirects.

### Provider passthrough

Only keys listed in that model’s allowed\_passthrough\_parameters.

JSON

```
{
  "model": "google/veo-3.1",
  "prompt": "A time-lapse of a flower blooming",
  "provider": {
    "options": {
      "google-vertex": {
        "parameters": {
          "personGeneration": "allow",
          "negativePrompt": "blurry, low quality"
        }
      }
    }
  }
}
```

### Poll

Bash

```
curl https://www.lanbridge.net/api/v1/videos/job-abc123 \
  -H "Authorization: Bearer $LANBRIDGE_API_KEY"
```

| status       | Meaning                                  |
| ------------ | ---------------------------------------- |
| pending      | Queued                                   |
| in\_progress | Rendering                                |
| completed    | Ready to download                        |
| failed       | Read error. Stop.                        |
| cancelled    | Stop                                     |
| expired      | TTL exceeded. Submit a new job if needed |

Poll every 10–30 seconds. Persist id so a restart does not bill a second generation.

**Completed body**

JSON

```
{
  "id": "job-abc123",
  "generation_id": "gen-1234567890-abcdef",
  "polling_url": "https://www.lanbridge.net/api/v1/videos/job-abc123",
  "status": "completed",
  "unsigned_urls": [
    "https://www.lanbridge.net/api/v1/videos/job-abc123/content?index=0"
  ],
  "usage": { "cost": 0.25, "is_byok": false }
}
```

### Download

unsigned\_urls are **not** presigned. Send the Bearer token when the URL points at LanBridge. index defaults to 0.

Bash

```
curl "https://www.lanbridge.net/api/v1/videos/job-abc123/content?index=0" \
  -H "Authorization: Bearer $LANBRIDGE_API_KEY" \
  --output video.mp4
```

Response body is raw bytes, typically Content-Type: video/mp4.

### Python loop

Python

```
import os, time, requests

BASE = "https://www.lanbridge.net/api/v1"
HEADERS = {
    "Authorization": f"Bearer {os.environ['LANBRIDGE_API_KEY']}",
    "Content-Type": "application/json",
}

job = requests.post(f"{BASE}/videos", headers=HEADERS, json={
    "model": "google/veo-3.1-lite",
    "prompt": "A glass greenhouse at sunrise, soft mist, slow dolly-in",
    "duration": 4,
    "resolution": "720p",
    "aspect_ratio": "16:9",
    "generate_audio": False,
}).json()

poll_url = job["polling_url"]
if poll_url.startswith("/"):
    poll_url = "https://www.lanbridge.net" + poll_url

while True:
    time.sleep(20)
    status = requests.get(poll_url, headers=HEADERS).json()
    state = status["status"]
    if state == "completed":
        url = (status.get("unsigned_urls") or [None])[0] or f"{BASE}/videos/{job['id']}/content?index=0"
        open("output.mp4", "wb").write(requests.get(url, headers=HEADERS).content)
        break
    if state in {"failed", "cancelled", "expired"}:
        raise SystemExit(status.get("error") or state)
```

### Webhooks

Pass callback\_url (HTTPS) on submit instead of polling from the client.

JSON

```
{
  "type": "video.generation.completed",
  "created_at": "2026-09-20T12:00:00.000Z",
  "data": {
    "id": "job-abc123",
    "status": "completed",
    "generation_id": "gen-xyz789",
    "model": "google/veo-3.1",
    "unsigned_urls": [
      "https://www.lanbridge.net/api/v1/videos/job-abc123/content?index=0"
    ],
    "usage": { "cost": 0.25, "is_byok": false }
  }
}
```

Failed jobs use type: "video.generation.failed" and include data.error. Deduplicate retries with X-OpenRouter-Idempotency-Key when present.

### Errors

| Signal | When                                           |
| ------ | ---------------------------------------------- |
| 400    | Bad combo, duration/aspect not supported       |
| 401    | Missing or invalid key                         |
| 402    | Balance or spend cap exhausted                 |
| 404    | Unknown model or job id                        |
| 429    | Back off. Do not tight-loop the same prompt    |
| failed | Upstream or policy. Read error before retrying |

A submitted job can bill even if you never download the file.

### Chat vs video

| Task                     | Endpoint                                                     |
| ------------------------ | ------------------------------------------------------------ |
| Generate a clip          | POST /api/v1/videos                                          |
| Analyze an existing clip | POST /api/v1/chat/completions with a video\_url content part |
| Text, tools, stills      | POST /api/v1/chat/completions                                |

Chat video input formats: video/mp4, video/mpeg, video/mov, video/webm.
