# Raindance AI

Raindance AI turns two character photos into a waterfront performance guided by a motion template. Choose your cast, check the estimate and generate your video here.

Generate a video: https://seedrouter.ai/tools/raindance-ai
Tutorial: https://seedrouter.ai/blog/how-to-make-raindance-ai-video

## Workflow

1. Pick two character images: Replace the samples with one clear image per role. Keep the seated performer in Image 1 and the singing performer in Image 2.
2. Review the Raindance AI preset: Preview the outline motion template and audio reference, then read the prompt. Retain the role assignments when adjusting appearance instructions.
3. Check the estimate and generate: Choose a Seedance model and review its live estimate. Sign in, make sure your balance covers the task, then start generation.
4. Review the picture and sound: Watch the full Raindance AI clip. Compare the cuts and gestures with the reference, then listen while checking the singing character’s mouth movements. Download once you are happy with the result.

## Scene preset

Default model: `dreamina-seedance-2-5`.
Seedance 2.5, 13 seconds, 720p, 16:9. Image 1 is the seated performer; Image 2 is the singing performer. Generation uses the original 13.13-second outline motion reference; the trend clip is displayed separately. Audio is supplied as a separate reference. Generated audio starts on. The model produces picture and sound together; exact cuts and lip sync can vary. Reset restores the preset.
Seated performer: https://file.deepytb.com/tools/hotel-lobby-ai/seedrouter/v1/left-performer.png
Singing performer: https://file.deepytb.com/tools/hotel-lobby-ai/seedrouter/v1/right-performer.png
Motion reference: https://file.deepytb.com/tools/raindance-ai/motion-reference-v1.mp4
Motion reference source: https://www.kapwing.com/resources/how-to-do-the-raindance-ai-trend-prompt-included/
Audio reference: https://file.deepytb.com/tools/raindance-ai/reference-audio-v1.mp3

## Current estimates

The preset estimate includes 13 output seconds plus 13.133333 reference-video seconds. Actual charges follow completed video usage. Failed requests are not charged.

- Seedance 2.5 (dreamina-seedance-2-5): pricing unavailable. API reference: https://seedrouter.ai/docs/seedance-2-5.md
- Seedance 2.0 (dreamina-seedance-2-0): pricing unavailable. API reference: https://seedrouter.ai/docs/seedance-2-0.md
- Seedance 2.0 Fast (dreamina-seedance-2-0-fast): pricing unavailable. API reference: https://seedrouter.ai/docs/seedance-2-0.md
- Seedance 2.0 Mini (dreamina-seedance-2-0-mini): pricing unavailable. API reference: https://seedrouter.ai/docs/seedance-2-0.md

## Prompt

Create a 13-second music performance using @Video 1 as the exact shot-by-shot timing and choreography reference. @Image 1 defines the seated performer. @Image 2 defines the singing performer. Use only their faces, hair and clothing from their own images. Follow every cut, shot duration, camera angle, framing, gesture and facial performance in @Video 1 in the same order; do not merge shots, invent new cuts, or return to the seated performer during the singing close-ups. Keep the waterfront setting. @Audio 1 provides the soundtrack reference: preserve its vocals, words, melody and beat timing, and synchronize the singing performer's visible mouth shapes with those vocals. The seated performer does not sing. Keep the visual style of the two portrait images, without adopting the reference characters' appearance or the video's black-and-white outline style. No added dialogue or captions.

## Use one character in a video

Choose a view, adjust the crop to include one character, then send it to a character slot. The other character, motion and audio stay unchanged. No video is generated automatically.
The crop is saved as a PNG. White padding keeps it within the video model’s image limits. Review the reference on the next page before generating.

Start with a [character reference sheet](https://seedrouter.ai/tools/ai-character-reference-sheet-generator). Sign in to save the crop, then choose character slot 1 or 2 in [Hotel Lobby AI](https://seedrouter.ai/tools/hotel-lobby-ai) or [Raindance AI](https://seedrouter.ai/tools/raindance-ai). This is a browser workflow, not an additional model API parameter.

## Questions

### Is Raindance AI free?

Previewing the template does not start a paid task. Generation uses your SeedRouter balance at the chosen model's rate, shown before submission. There is no separate Raindance AI subscription.

### Which photos should I use for Raindance AI?

Use one clear character per slot, with face and outfit visible. Similar visual styles help describe the scene. Images remain subject to model checks; acceptance and exact likeness are not guaranteed.

### Where is the Raindance AI prompt?

The prompt is prefilled and editable in the Playground. It assigns the seated and singing roles and follows the motion reference's shot sequence. Reset restores the scene prompt, sample inputs and starting settings.

### Does Raindance AI generate sound and lip sync?

The preset includes an outline video and a separate audio reference, with generated audio enabled. The prompt asks the singing performer to follow the vocals. The model generates picture and sound together; exact lyrics, musical timing and lip sync still need review.

### Can I change the Raindance AI model or format?

Yes. Choose Seedance 2.5, 2.0, Fast or Mini. The preset is 13 seconds at 720p in 16:9; available settings and the estimate follow your selected model.

### Why does my Raindance AI result look different?

Images guide appearance; the reference guides performance. Conflicting styles, obscured faces or complicated instructions can confuse the result. Simplify one input at a time and compare both characters throughout the clip.

## Model integration

This tool uses presets over the models below; the tool name is not an API model ID. The references describe each model’s full contract. Use the scene prompt and inputs above for this workflow; the reference examples illustrate general API usage. Browser-only steps are described separately above.
Use an API key from https://seedrouter.ai/apikeys as Authorization: Bearer $SEEDROUTER_API_KEY. Store the key on your server.

## Current model rates

Rates use the same source and one-minute refresh interval as the model pages. All amounts are in USD. Unavailable rates must not be interpreted as free usage.

### seedance-2-5

Model IDs: `dreamina-seedance-2-5`. [Model page](https://seedrouter.ai/models/seedance-2-5).

Current rates are temporarily unavailable. No price estimate is provided; unavailable rates must not be interpreted as free usage.

### seedance-2-0

Model IDs: `dreamina-seedance-2-0`. [Model page](https://seedrouter.ai/models/seedance-2-0).

Current rates are temporarily unavailable. No price estimate is provided; unavailable rates must not be interpreted as free usage.

### seedance-2-0-fast

Model IDs: `dreamina-seedance-2-0-fast`. [Model page](https://seedrouter.ai/models/seedance-2-0-fast).

Current rates are temporarily unavailable. No price estimate is provided; unavailable rates must not be interpreted as free usage.

### seedance-2-0-mini

Model IDs: `dreamina-seedance-2-0-mini`. [Model page](https://seedrouter.ai/models/seedance-2-0-mini).

Current rates are temporarily unavailable. No price estimate is provided; unavailable rates must not be interpreted as free usage.

## API reference: seedance-2-5

- [Markdown](https://seedrouter.ai/docs/seedance-2-5.md)
- [OpenAPI request and response schemas](https://seedrouter.ai/docs/seedance-2-5.openapi.json)

Seedance 2.5 is ByteDance's newest video generation model (Dreamina Seedance 2.5). Send the official ModelArk task body, keep the returned task ID, and read the finished video from the task. Images, videos and audio go in `content` as URLs.

## Model IDs

| Model ID                | Resolutions       | Length                     |
| ----------------------- | ----------------- | -------------------------- |
| `dreamina-seedance-2-5` | 480p, 720p, 1080p | 4–30 seconds, or automatic |

See the [model page](https://seedrouter.ai/models/seedance-2-5#pricing) for current prices.

## Quick example

```bash
curl https://api.seedrouter.ai/v1/contents/generations/tasks \
  -H "Authorization: Bearer $SEEDROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "dreamina-seedance-2-5",
    "content": [{"type": "text", "text": "A red paper boat drifts across a calm pond at sunrise, slow dolly-in"}],
    "resolution": "720p",
    "ratio": "16:9",
    "duration": 5,
    "generate_audio": true
  }'
```

```python
import os
import requests

response = requests.post(
    "https://api.seedrouter.ai/v1/contents/generations/tasks",
    headers={"Authorization": f"Bearer {os.environ['SEEDROUTER_API_KEY']}"},
    json={
        "model": "dreamina-seedance-2-5",
        "content": [{"type": "text", "text": "A red paper boat drifts across a calm pond at sunrise, slow dolly-in"}],
        "resolution": "720p",
        "ratio": "16:9",
        "duration": 5,
        "generate_audio": True,
    },
    timeout=60,
)
response.raise_for_status()
task_id = response.json()["id"]
```

```javascript
const response = await fetch('https://api.seedrouter.ai/v1/contents/generations/tasks', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SEEDROUTER_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'dreamina-seedance-2-5',
    content: [{ type: 'text', text: 'A red paper boat drifts across a calm pond at sunrise, slow dolly-in' }],
    resolution: '720p',
    ratio: '16:9',
    duration: 5,
    generate_audio: true,
  }),
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const { id: taskId } = await response.json();
```

```go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "os"
    "strings"
    "time"
)

func main() {
    body := strings.NewReader(`{
      "model": "dreamina-seedance-2-5",
      "content": [{"type": "text", "text": "A red paper boat drifts across a calm pond at sunrise, slow dolly-in"}],
      "resolution": "720p",
      "ratio": "16:9",
      "duration": 5,
      "generate_audio": true
    }`)
    req, err := http.NewRequest("POST", "https://api.seedrouter.ai/v1/contents/generations/tasks", body)
    if err != nil { panic(err) }
    req.Header.Set("Authorization", "Bearer " + os.Getenv("SEEDROUTER_API_KEY"))
    req.Header.Set("Content-Type", "application/json")
    client := &http.Client{Timeout: 60 * time.Second}
    res, err := client.Do(req)
    if err != nil { panic(err) }
    defer res.Body.Close()
    if res.StatusCode != http.StatusOK { panic(res.Status) }
    var task struct { ID string `json:"id"` }
    if err := json.NewDecoder(res.Body).Decode(&task); err != nil { panic(err) }
    fmt.Println(task.ID)
}
```

## Endpoint

```text
POST https://api.seedrouter.ai/v1/contents/generations/tasks
```

| Header        | Value                 |
| ------------- | --------------------- |
| Authorization | `Bearer YOUR_API_KEY` |
| Content-Type  | `application/json`    |

The body is the official ModelArk "create a video generation task" request. If you already call ModelArk, change only the base URL to `https://api.seedrouter.ai/v1` and the API key. The response is `{"id": "task_..."}`, not the finished video. Keep API keys in server-side code.

## Parameters

| Name                       | Type      | Required | Default    | Notes                                                                                                                                     |
| -------------------------- | --------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `model`                    | string    | Yes      | —          | `dreamina-seedance-2-5`.                                                                                                                  |
| `content`                  | object\[] | Yes      | —          | The prompt and media; see below.                                                                                                          |
| `resolution`               | enum      | No       | `720p`     | `480p`, `720p`, `1080p`.                                                                                                                  |
| `ratio`                    | enum      | No       | `adaptive` | `16:9`, `4:3`, `1:1`, `3:4`, `9:16`, `21:9`, `adaptive`. Must be `adaptive` (or omitted) with a first frame, and for `edit` and `extend`. |
| `duration`                 | integer   | No       | `-1`       | 4–30 seconds, or `-1` to let the model choose. Must be `-1` for `edit`.                                                                   |
| `generate_audio`           | boolean   | No       | `true`     | Generate sound with the video.                                                                                                            |
| `watermark`                | boolean   | No       | `false`    | Add a watermark.                                                                                                                          |
| `return_last_frame`        | boolean   | No       | `false`    | Also return the final frame as an image URL.                                                                                              |
| `output_format`            | enum      | No       | `mp4`      | `mp4` or `mov`.                                                                                                                           |
| `omni_reference_task_type` | enum      | No       | `auto`     | `auto`, `reference`, `edit`, `extend`. `edit` and `extend` need a reference video.                                                        |
| `execution_expires_after`  | integer   | No       | `172800`   | 3600–259200 seconds. A task still unfinished after this becomes `expired` and is not charged.                                             |
| `priority`                 | integer   | No       | `0`        | 0–9.                                                                                                                                      |
| `safety_identifier`        | string    | No       | —          | 1–64 characters identifying your end user. A hash is fine.                                                                                |
| `service_tier`             | enum      | No       | `default`  | Only `default`.                                                                                                                           |
| `content_filter`           | boolean   | No       | `true`     | SeedRouter extension. `false` turns off content filtering on this request.                                                                |

### `content` items

| Item  | Shape                                                                                   | Role                                           | Limit                      |
| ----- | --------------------------------------------------------------------------------------- | ---------------------------------------------- | -------------------------- |
| Text  | `{"type": "text", "text": "..."}`                                                       | —                                              | One.                       |
| Image | `{"type": "image_url", "image_url": {"url": "https://..."}, "role": "..."}`             | `first_frame`, `last_frame`, `reference_image` | Up to 30 reference images. |
| Video | `{"type": "video_url", "video_url": {"url": "https://..."}, "role": "reference_video"}` | `reference_video`                              | Up to 10.                  |
| Audio | `{"type": "audio_url", "audio_url": {"url": "https://..."}, "role": "reference_audio"}` | `reference_audio`                              | Up to 10.                  |

Unknown fields are rejected. Not supported: `seed`, `callback_url` (poll the task instead), `draft` and `draft_task`, `tools`, and the 1.x-only `frames` and `camera_fixed`. Tasks cannot be cancelled or deleted.

## Modes

The mode follows from the `content` items; there is no mode parameter.

| Mode                 | `content`                                                                          |
| -------------------- | ---------------------------------------------------------------------------------- |
| Text to video        | one text item                                                                      |
| First frame          | text (optional) + one image with role `first_frame`, or one image with no role     |
| First and last frame | text (optional) + one `first_frame` image + one `last_frame` image                 |
| Multimodal reference | text + any mix of `reference_image`, `reference_video` and `reference_audio` items |
| Edit a video         | text + one `reference_video`, with `omni_reference_task_type: "edit"`              |
| Extend a video       | text + one `reference_video`, with `omni_reference_task_type: "extend"`            |

First-frame modes cannot be combined with reference items. With several images or any other media, every image needs a `role`.

### Reference example

```bash
curl https://api.seedrouter.ai/v1/contents/generations/tasks \
  -H "Authorization: Bearer $SEEDROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "dreamina-seedance-2-5",
    "content": [
      {"type": "text", "text": "The character from the image walks through the market in the video, same camera move"},
      {"type": "image_url", "image_url": {"url": "https://example.com/character.png"}, "role": "reference_image"},
      {"type": "video_url", "video_url": {"url": "https://example.com/market.mp4"}, "role": "reference_video"}
    ],
    "ratio": "adaptive",
    "duration": 8
  }'
```

Replace the example URLs with your own accessible files.

## Media inputs

This API accepts URL references only. Base64, `data:` URLs, `asset://` IDs and multipart uploads are not accepted. The Playground uploads selected files to storage before submitting their URLs.

Media must be public HTTP(S) URLs and meet the model's official limits:

| Media | Formats                                     | Limits                                                                                                                                                                                                       |
| ----- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Image | JPEG, PNG, WebP, BMP, TIFF, GIF, HEIC, HEIF | Smaller than 30 MB; width and height 300–6000 px; aspect ratio (width / height) 0.4–2.5; 1–30 reference images                                                                                               |
| Video | MP4, MOV (H.264 or H.265)                   | 2–30 seconds each (4–30 seconds for `edit`), up to 10, at most 30 seconds in total; at most 200 MB; 24–60 FPS; width and height 300–6000 px; aspect ratio 0.4–2.5; 407,696–8,295,044 pixels (width × height) |
| Audio | WAV, MP3                                    | 2–30 seconds each, up to 10, at most 30 seconds in total; at most 15 MB                                                                                                                                      |

Reference images and videos that contain real human faces are not supported by the model.

Media is checked when the task starts, before any generation. A task whose media breaks one of these limits ends as `failed` with `invalid_request_error` and a message naming the rule, for example `The request was rejected: content reference videos must total at most 15 seconds.`, and is not charged. A file that cannot be read at that point is passed to the model, which accepts or rejects it; a failed task is not charged either way.

## Pricing dimensions

Check the [model pricing section](https://seedrouter.ai/models/seedance-2-5#pricing) for current rates. Seedance 2.5 bills video tokens, the official unit:

```text
video tokens = (output seconds + reference video seconds) × width × height × 24 / 1024
```

The rate per million tokens depends on the output resolution and on whether the request includes a reference video; a request with a reference video uses a lower rate for all of its tokens. Text, image and audio inputs are not billed. At 16:9, one second is 9,607.5 tokens at 480p (854×480), 21,600 at 720p, 48,600 at 1080p.

The charge follows the tokens the finished video reports (`usage.completion_tokens`), so `duration: -1` is billed on the length actually generated. Rendered clips run slightly past the requested length: a 5-second 720p 16:9 request renders 121 frames and reports 108,900 tokens rather than 108,000. View final charges in your account usage history. Failed and expired tasks are not charged.

## Output schema

Submission returns the task ID:

```json
{"id": "task_..."}
```

### Get the task

```bash
curl https://api.seedrouter.ai/v1/contents/generations/tasks/YOUR_TASK_ID \
  -H "Authorization: Bearer $SEEDROUTER_API_KEY"
```

Poll every 10–20 seconds until `status` is `succeeded`, `failed` or `expired`. A network timeout while polling does not mean generation failed: keep the task ID and resume checking it. Do not create another task to check progress.

### Complete polling example

Run this after the Python submission example above.

```python
import time

deadline = time.monotonic() + 1800
while time.monotonic() < deadline:
    result = requests.get(
        f"https://api.seedrouter.ai/v1/contents/generations/tasks/{task_id}",
        headers={"Authorization": f"Bearer {os.environ['SEEDROUTER_API_KEY']}"},
        timeout=30,
    )
    result.raise_for_status()
    task = result.json()
    if task["status"] == "succeeded":
        print(task["content"]["video_url"])
        break
    if task["status"] in ("failed", "expired"):
        raise RuntimeError(task["error"]["message"])
    time.sleep(15)
else:
    raise TimeoutError(f"Still waiting. Resume polling task {task_id}.")
```

### Succeeded task

```json
{
  "id": "task_...",
  "model": "dreamina-seedance-2-5",
  "status": "succeeded",
  "content": {
    "video_url": "https://static.seedrouter.ai/media/tasks/task_example/0.mp4",
    "last_frame_url": "https://static.seedrouter.ai/media/tasks/task_example/last_frame/0.jpg"
  },
  "usage": {"completion_tokens": 108900, "total_tokens": 108900},
  "seed": 42,
  "resolution": "720p",
  "ratio": "16:9",
  "duration": 5,
  "framespersecond": 24,
  "generate_audio": true,
  "draft": false,
  "output_format": "mp4",
  "service_tier": "default",
  "execution_expires_after": 172800,
  "priority": 0,
  "created_at": 1790321515,
  "updated_at": 1790321652
}
```

| Field                                                        | Meaning                                                         |
| ------------------------------------------------------------ | --------------------------------------------------------------- |
| `id`                                                         | Keep this ID for later queries.                                 |
| `status`                                                     | `queued`, `running`, `succeeded`, `failed` or `expired`.        |
| `content.video_url`                                          | The generated video.                                            |
| `content.last_frame_url`                                     | The final frame, when `return_last_frame` is `true`.            |
| `usage.completion_tokens`                                    | Video tokens of the finished video; the billed quantity.        |
| `duration`, `resolution`, `ratio`, `framespersecond`, `seed` | What was actually rendered; `seed` is the one the model picked. |
| `created_at`, `updated_at`                                   | Unix timestamps in seconds.                                     |
| `error`                                                      | `{"code", "message"}` on a failed or expired task.              |

Video URLs are hosted on our storage. Save the file to your own storage when you need a durable copy.

### List tasks

```bash
curl "https://api.seedrouter.ai/v1/contents/generations/tasks?page_num=1&page_size=20&filter.status=succeeded" \
  -H "Authorization: Bearer $SEEDROUTER_API_KEY"
```

Returns `{"total": N, "items": [...]}` with task objects from the last 7 days, newest first. `page_num` and `page_size` are 1–500 (defaults 1 and 20). Filters: `filter.status`, `filter.model`, `filter.task_ids` (repeatable) and `filter.service_tier`.

## Errors

Requests rejected before a task is created return an HTTP error with an `error` object and are not charged. A task that fails after acceptance returns HTTP 200 when queried, with `status: "failed"` (or `"expired"`) and an `error` object. Output withheld by content filtering fails with `content_policy_violation`; a task that runs past `execution_expires_after` ends as `expired` with `task_expired`.

See the [shared error catalog](https://seedrouter.ai/docs/api/errors) for codes, HTTP statuses, and retry guidance.

```json
{
  "id": "task_...",
  "model": "dreamina-seedance-2-5",
  "status": "failed",
  "error": {
    "code": 60001,
    "message": "The request was rejected by the content policy. Please revise the prompt or input images."
  }
}
```

If submission itself times out, check your task list before submitting again: the first request may have been accepted.

## Tips

* Describe the subject, the action, the camera move, and the lighting in full sentences.
* Draft at 480p with a short `duration`, then render the keeper at a higher resolution.
* Chain shots with `return_last_frame`: use the returned frame as the next task's `first_frame`.
* To edit footage, set `omni_reference_task_type` to `edit` and describe only what should change.

## Related

* [Seedance 2.5 Playground and pricing](https://seedrouter.ai/models/seedance-2-5)
* [Download OpenAPI](https://seedrouter.ai/docs/seedance-2-5.openapi.json)
* [Copyable Markdown](https://seedrouter.ai/docs/seedance-2-5.md)
## API reference: seedance-2-0

- [Markdown](https://seedrouter.ai/docs/seedance-2-0.md)
- [OpenAPI request and response schemas](https://seedrouter.ai/docs/seedance-2-0.openapi.json)

Seedance 2.0 is ByteDance's video generation model (Dreamina Seedance 2.0). Send the official ModelArk task body, keep the returned task ID, and read the finished video from the task. Images, videos and audio go in `content` as URLs.

## Model IDs

| Model ID                     | Resolutions           | Notes                   |
| ---------------------------- | --------------------- | ----------------------- |
| `dreamina-seedance-2-0`      | 480p, 720p, 1080p, 4K | Full model              |
| `dreamina-seedance-2-0-fast` | 480p, 720p            | Lower price per second  |
| `dreamina-seedance-2-0-mini` | 480p, 720p            | Lowest price per second |

All three IDs accept the same parameters. See the [model page](https://seedrouter.ai/models/seedance-2-0#pricing) for current prices.

## Quick example

```bash
curl https://api.seedrouter.ai/v1/contents/generations/tasks \
  -H "Authorization: Bearer $SEEDROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "dreamina-seedance-2-0",
    "content": [{"type": "text", "text": "A red paper boat drifts across a calm pond at sunrise, slow dolly-in"}],
    "resolution": "720p",
    "ratio": "16:9",
    "duration": 5,
    "generate_audio": true
  }'
```

```python
import os
import requests

response = requests.post(
    "https://api.seedrouter.ai/v1/contents/generations/tasks",
    headers={"Authorization": f"Bearer {os.environ['SEEDROUTER_API_KEY']}"},
    json={
        "model": "dreamina-seedance-2-0",
        "content": [{"type": "text", "text": "A red paper boat drifts across a calm pond at sunrise, slow dolly-in"}],
        "resolution": "720p",
        "ratio": "16:9",
        "duration": 5,
        "generate_audio": True,
    },
    timeout=60,
)
response.raise_for_status()
task_id = response.json()["id"]
```

```javascript
const response = await fetch('https://api.seedrouter.ai/v1/contents/generations/tasks', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SEEDROUTER_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'dreamina-seedance-2-0',
    content: [{ type: 'text', text: 'A red paper boat drifts across a calm pond at sunrise, slow dolly-in' }],
    resolution: '720p',
    ratio: '16:9',
    duration: 5,
    generate_audio: true,
  }),
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const { id: taskId } = await response.json();
```

```go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "os"
    "strings"
    "time"
)

func main() {
    body := strings.NewReader(`{
      "model": "dreamina-seedance-2-0",
      "content": [{"type": "text", "text": "A red paper boat drifts across a calm pond at sunrise, slow dolly-in"}],
      "resolution": "720p",
      "ratio": "16:9",
      "duration": 5,
      "generate_audio": true
    }`)
    req, err := http.NewRequest("POST", "https://api.seedrouter.ai/v1/contents/generations/tasks", body)
    if err != nil { panic(err) }
    req.Header.Set("Authorization", "Bearer " + os.Getenv("SEEDROUTER_API_KEY"))
    req.Header.Set("Content-Type", "application/json")
    client := &http.Client{Timeout: 60 * time.Second}
    res, err := client.Do(req)
    if err != nil { panic(err) }
    defer res.Body.Close()
    if res.StatusCode != http.StatusOK { panic(res.Status) }
    var task struct { ID string `json:"id"` }
    if err := json.NewDecoder(res.Body).Decode(&task); err != nil { panic(err) }
    fmt.Println(task.ID)
}
```

## Endpoint

```text
POST https://api.seedrouter.ai/v1/contents/generations/tasks
```

| Header        | Value                 |
| ------------- | --------------------- |
| Authorization | `Bearer YOUR_API_KEY` |
| Content-Type  | `application/json`    |

The body is the official ModelArk "create a video generation task" request. If you already call ModelArk, change only the base URL to `https://api.seedrouter.ai/v1` and the API key. The response is `{"id": "task_..."}`, not the finished video. Keep API keys in server-side code.

## Parameters

| Name                      | Type      | Required | Default    | Notes                                                                                         |
| ------------------------- | --------- | -------- | ---------- | --------------------------------------------------------------------------------------------- |
| `model`                   | string    | Yes      | —          | One of the three model IDs above.                                                             |
| `content`                 | object\[] | Yes      | —          | The prompt and media; see below.                                                              |
| `resolution`              | enum      | No       | `720p`     | `480p`, `720p`, `1080p`, `4k`; the Fast and Mini IDs accept `480p` and `720p` only.           |
| `ratio`                   | enum      | No       | `adaptive` | `16:9`, `4:3`, `1:1`, `3:4`, `9:16`, `21:9`, `adaptive`.                                      |
| `duration`                | integer   | No       | `5`        | 4–15 seconds, or `-1` to let the model choose.                                                |
| `generate_audio`          | boolean   | No       | `true`     | Generate sound with the video.                                                                |
| `watermark`               | boolean   | No       | `false`    | Add a watermark.                                                                              |
| `return_last_frame`       | boolean   | No       | `false`    | Also return the final frame as an image URL.                                                  |
| `execution_expires_after` | integer   | No       | `172800`   | 3600–259200 seconds. A task still unfinished after this becomes `expired` and is not charged. |
| `priority`                | integer   | No       | `0`        | 0–9.                                                                                          |
| `safety_identifier`       | string    | No       | —          | 1–64 characters identifying your end user. A hash is fine.                                    |
| `service_tier`            | enum      | No       | `default`  | Only `default`.                                                                               |
| `content_filter`          | boolean   | No       | `true`     | SeedRouter extension. `false` turns off content filtering on this request.                    |

### `content` items

| Item  | Shape                                                                                   | Role                                           | Limit                                      |
| ----- | --------------------------------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------ |
| Text  | `{"type": "text", "text": "..."}`                                                       | —                                              | One.                                       |
| Image | `{"type": "image_url", "image_url": {"url": "https://..."}, "role": "..."}`             | `first_frame`, `last_frame`, `reference_image` | Up to 9 reference images.                  |
| Video | `{"type": "video_url", "video_url": {"url": "https://..."}, "role": "reference_video"}` | `reference_video`                              | Up to 3.                                   |
| Audio | `{"type": "audio_url", "audio_url": {"url": "https://..."}, "role": "reference_audio"}` | `reference_audio`                              | Up to 3. Needs a reference image or video. |

Unknown fields are rejected. Not supported: `seed`, `callback_url` (poll the task instead), `draft` and `draft_task`, `tools`, and the 1.x-only `frames` and `camera_fixed`, as well as `output_format` and `omni_reference_task_type` (Seedance 2.5 only). Tasks cannot be cancelled or deleted.

## Modes

The mode follows from the `content` items; there is no mode parameter.

| Mode                 | `content`                                                                          |
| -------------------- | ---------------------------------------------------------------------------------- |
| Text to video        | one text item                                                                      |
| First frame          | text (optional) + one image with role `first_frame`, or one image with no role     |
| First and last frame | text (optional) + one `first_frame` image + one `last_frame` image                 |
| Multimodal reference | text + any mix of `reference_image`, `reference_video` and `reference_audio` items |

First-frame modes cannot be combined with reference items. With several images or any other media, every image needs a `role`.

### Reference example

```bash
curl https://api.seedrouter.ai/v1/contents/generations/tasks \
  -H "Authorization: Bearer $SEEDROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "dreamina-seedance-2-0",
    "content": [
      {"type": "text", "text": "The character from the image walks through the market in the video, same camera move"},
      {"type": "image_url", "image_url": {"url": "https://example.com/character.png"}, "role": "reference_image"},
      {"type": "video_url", "video_url": {"url": "https://example.com/market.mp4"}, "role": "reference_video"}
    ],
    "ratio": "adaptive",
    "duration": 8
  }'
```

Replace the example URLs with your own accessible files.

## Media inputs

This API accepts URL references only. Base64, `data:` URLs, `asset://` IDs and multipart uploads are not accepted. The Playground uploads selected files to storage before submitting their URLs.

Media must be public HTTP(S) URLs and meet the model's official limits:

| Media | Formats                                     | Limits                                                                                                                                                                            |
| ----- | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Image | JPEG, PNG, WebP, BMP, TIFF, GIF, HEIC, HEIF | Smaller than 30 MB; width and height 300–6000 px; aspect ratio (width / height) 0.4–2.5; 1–9 reference images                                                                     |
| Video | MP4, MOV (H.264 or H.265)                   | 2–15 seconds each, up to 3, at most 15 seconds in total; at most 200 MB; 24–60 FPS; width and height 300–6000 px; aspect ratio 0.4–2.5; 407,696–8,295,044 pixels (width × height) |
| Audio | WAV, MP3                                    | 2–15 seconds each, up to 3, at most 15 seconds in total; needs a reference image or video; at most 15 MB                                                                          |

Reference images and videos that contain real human faces are not supported by the model.

Media is checked when the task starts, before any generation. A task whose media breaks one of these limits ends as `failed` with `invalid_request_error` and a message naming the rule, for example `The request was rejected: content reference videos must total at most 15 seconds.`, and is not charged. A file that cannot be read at that point is passed to the model, which accepts or rejects it; a failed task is not charged either way.

## Pricing dimensions

Check the [model pricing section](https://seedrouter.ai/models/seedance-2-0#pricing) for current rates. Seedance 2.0 bills video tokens, the official unit:

```text
video tokens = (output seconds + reference video seconds) × width × height × 24 / 1024
```

The rate per million tokens depends on the output resolution and on whether the request includes a reference video; a request with a reference video uses a lower rate for all of its tokens. Text, image and audio inputs are not billed. At 16:9, one second is 10,044 tokens at 480p (864×496), 21,600 at 720p, 48,600 at 1080p, and 194,400 at 4K.

The charge follows the tokens the finished video reports (`usage.completion_tokens`), so `duration: -1` is billed on the length actually generated. Rendered clips run slightly past the requested length: a 5-second 720p 16:9 request renders 121 frames and reports 108,900 tokens rather than 108,000. View final charges in your account usage history. Failed and expired tasks are not charged.

## Output schema

Submission returns the task ID:

```json
{"id": "task_..."}
```

### Get the task

```bash
curl https://api.seedrouter.ai/v1/contents/generations/tasks/YOUR_TASK_ID \
  -H "Authorization: Bearer $SEEDROUTER_API_KEY"
```

Poll every 10–20 seconds until `status` is `succeeded`, `failed` or `expired`. A network timeout while polling does not mean generation failed: keep the task ID and resume checking it. Do not create another task to check progress.

### Complete polling example

Run this after the Python submission example above.

```python
import time

deadline = time.monotonic() + 1800
while time.monotonic() < deadline:
    result = requests.get(
        f"https://api.seedrouter.ai/v1/contents/generations/tasks/{task_id}",
        headers={"Authorization": f"Bearer {os.environ['SEEDROUTER_API_KEY']}"},
        timeout=30,
    )
    result.raise_for_status()
    task = result.json()
    if task["status"] == "succeeded":
        print(task["content"]["video_url"])
        break
    if task["status"] in ("failed", "expired"):
        raise RuntimeError(task["error"]["message"])
    time.sleep(15)
else:
    raise TimeoutError(f"Still waiting. Resume polling task {task_id}.")
```

### Succeeded task

```json
{
  "id": "task_...",
  "model": "dreamina-seedance-2-0",
  "status": "succeeded",
  "content": {
    "video_url": "https://static.seedrouter.ai/media/tasks/task_example/0.mp4",
    "last_frame_url": "https://static.seedrouter.ai/media/tasks/task_example/last_frame/0.jpg"
  },
  "usage": {"completion_tokens": 108900, "total_tokens": 108900},
  "seed": 42,
  "resolution": "720p",
  "ratio": "16:9",
  "duration": 5,
  "framespersecond": 24,
  "generate_audio": true,
  "draft": false,
  "output_format": "mp4",
  "service_tier": "default",
  "execution_expires_after": 172800,
  "priority": 0,
  "created_at": 1790321515,
  "updated_at": 1790321652
}
```

| Field                                                        | Meaning                                                         |
| ------------------------------------------------------------ | --------------------------------------------------------------- |
| `id`                                                         | Keep this ID for later queries.                                 |
| `status`                                                     | `queued`, `running`, `succeeded`, `failed` or `expired`.        |
| `content.video_url`                                          | The generated video.                                            |
| `content.last_frame_url`                                     | The final frame, when `return_last_frame` is `true`.            |
| `usage.completion_tokens`                                    | Video tokens of the finished video; the billed quantity.        |
| `duration`, `resolution`, `ratio`, `framespersecond`, `seed` | What was actually rendered; `seed` is the one the model picked. |
| `created_at`, `updated_at`                                   | Unix timestamps in seconds.                                     |
| `error`                                                      | `{"code", "message"}` on a failed or expired task.              |

Video URLs are hosted on our storage. Save the file to your own storage when you need a durable copy.

### List tasks

```bash
curl "https://api.seedrouter.ai/v1/contents/generations/tasks?page_num=1&page_size=20&filter.status=succeeded" \
  -H "Authorization: Bearer $SEEDROUTER_API_KEY"
```

Returns `{"total": N, "items": [...]}` with task objects from the last 7 days, newest first. `page_num` and `page_size` are 1–500 (defaults 1 and 20). Filters: `filter.status`, `filter.model`, `filter.task_ids` (repeatable) and `filter.service_tier`.

## Errors

Requests rejected before a task is created return an HTTP error with an `error` object and are not charged. A task that fails after acceptance returns HTTP 200 when queried, with `status: "failed"` (or `"expired"`) and an `error` object. Output withheld by content filtering fails with `content_policy_violation`; a task that runs past `execution_expires_after` ends as `expired` with `task_expired`.

See the [shared error catalog](https://seedrouter.ai/docs/api/errors) for codes, HTTP statuses, and retry guidance.

```json
{
  "id": "task_...",
  "model": "dreamina-seedance-2-0",
  "status": "failed",
  "error": {
    "code": 60001,
    "message": "The request was rejected by the content policy. Please revise the prompt or input images."
  }
}
```

If submission itself times out, check your task list before submitting again: the first request may have been accepted.

## Tips

* Describe the subject, the action, the camera move, and the lighting in full sentences.
* Draft at 480p with a short `duration`, then render the keeper at a higher resolution.
* Chain shots with `return_last_frame`: use the returned frame as the next task's `first_frame`.

## Related

* [Seedance 2.0 Playground and pricing](https://seedrouter.ai/models/seedance-2-0)
* [Download OpenAPI](https://seedrouter.ai/docs/seedance-2-0.openapi.json)
* [Copyable Markdown](https://seedrouter.ai/docs/seedance-2-0.md)

## More resources

- [Task lifecycle](https://seedrouter.ai/docs/api/tasks.md)
- [Error catalog and retry guidance](https://seedrouter.ai/docs/api/errors.md)
- [Account usage](https://seedrouter.ai/usage)
- [All tools and models](https://seedrouter.ai/llms.txt)
- [Documentation index](https://seedrouter.ai/docs/llms.txt)
