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

# Video Generation

Generate video from text or an image with Veo 3.1 and Seedance using Nebula Block's asynchronous video API.

Nebula Block hosts text-to-video and image-to-video models — Google Veo 3.1 and BytePlus Seedance — behind an OpenAI-compatible endpoint.

Video generation takes anywhere from seconds to minutes, so the API is **asynchronous**: you submit a job, get a generation ID back immediately, and poll (or stream) until it is done.

## Models available

See the [Model Catalog](/products/serverless-inference/model-catalog.md#video-generation) for the current list. Models whose ID ends in `-i2v` are image-to-video and **require** an input image.

| Family                                          | Capabilities                                        |
| ----------------------------------------------- | --------------------------------------------------- |
| `Google/veo-3.1`, `Google/veo-3.1-fast`         | Text-to-video, optional generated audio             |
| `Google/veo-3.1-i2v`, `Google/veo-3.1-fast-i2v` | Image-to-video                                      |
| `Byteplus/seedance-2-0-*`                       | Text-, image-, and video-to-video with native audio |
| `Byteplus/seedance-1-0-*`                       | Text- and image-to-video                            |

## Generate a video

### 1. Submit the job

```bash
curl -X POST "https://inference.nebulablock.com/v1/videos/generations" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $NEBULA_API_KEY" \
    --data-raw '{
        "model": "Google/veo-3.1-fast",
        "prompt": "A time-lapse of the Montreal skyline at sunset, cinematic, 35mm",
        "n_seconds": 5,
        "resolution": "720p",
        "aspect_ratio": "16:9",
        "include_audio": false
    }'
```

The response comes back immediately with a status of `pending`:

```json
{
  "id": "b3f1c0e2-6d1a-4e2b-9f77-6a1a2b3c4d5e",
  "object": "video.generation",
  "created_at": 1756944000,
  "status": "pending",
  "model": "Google/veo-3.1-fast",
  "estimated_cost": 0.6
}
```

### 2. Poll for the result

```bash
curl "https://inference.nebulablock.com/v1/videos/generations/$GENERATION_ID" \
    -H "Authorization: Bearer $NEBULA_API_KEY"
```

When `status` becomes `completed`, the response carries a `url` you can download the video from:

```json
{
  "id": "b3f1c0e2-6d1a-4e2b-9f77-6a1a2b3c4d5e",
  "object": "video.generation",
  "created_at": 1756944000,
  "status": "completed",
  "model": "Google/veo-3.1-fast",
  "url": "https://..."
}
```

A failed job returns `status: "failed"` and an `error` object instead.

### Python

```python
import os
import time

import requests

BASE = "https://inference.nebulablock.com/v1"
headers = {"Authorization": f"Bearer {os.environ['NEBULA_API_KEY']}"}

job = requests.post(
    f"{BASE}/videos/generations",
    headers=headers,
    json={
        "model": "Google/veo-3.1-fast",
        "prompt": "A time-lapse of the Montreal skyline at sunset, cinematic, 35mm",
        "n_seconds": 5,
        "resolution": "720p",
    },
).json()

while True:
    result = requests.get(f"{BASE}/videos/generations/{job['id']}", headers=headers).json()
    if result["status"] in ("completed", "failed"):
        break
    time.sleep(5)

print(result.get("url") or result.get("error"))
```

## Image-to-video

Pass a publicly reachable image as `image_url` and use an `-i2v` model:

```bash
curl -X POST "https://inference.nebulablock.com/v1/videos/generations" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $NEBULA_API_KEY" \
    --data-raw '{
        "model": "Google/veo-3.1-fast-i2v",
        "prompt": "Slow push-in, leaves drifting across the frame",
        "image_url": "https://example.com/still.jpg",
        "n_seconds": 5
    }'
```

Omitting `image_url` on an `-i2v` model returns `400` with code `missing_parameter`.

## Cost and credit

Video jobs are priced before they run, and the estimate is returned as `estimated_cost` on submission. If your balance cannot cover the estimate, the request is rejected with `402` and code `insufficient_credits` — nothing is queued and nothing is charged.

For Veo, generated audio and higher resolutions increase the cost. See the [pricing page](https://www.nebulablock.com/pricing/serverless-ai) for current rates.

## See also

* [Videos API reference](/api-reference/inference-api/videos.md) — every parameter and response field
* [Model Catalog](/products/serverless-inference/model-catalog.md)
* [Image Generation](/products/serverless-inference/image-generation.md)
