# Seedance 2.5

> ByteDance's newest video model: clips up to 30 seconds with sound, from a description, frames or references.

Seedance 2.5 is the newest ByteDance video model. It films 4 to 30 second clips in 480p, 720p and 1080p with sound: from a description, from the first and last frame, from references (up to 30 photos, 10 clips and 10 audio tracks), and it edits and continues your video.

Pick «Auto» length and the model decides: the price of 30 seconds is held and the rest comes back right after the video is ready.

One model, `seedance-2.5` · ~4 min. The result follows the `input` fields (prompt, frames, references, `video_mode`, duration) — there are no separate «animate / edit» endpoints.

## Features

- `prompt` — up to **30000** characters
- `duration: "auto"` / `-1` — model picks length (on the site: **auto (-1)**)
- `video_mode`: `auto` · `reference` · `edit` · `extend`
- `watermark` — "AI generated" mark
- `output_format`: `mp4` (default) or `mov`
- `return_last_frame` — last-frame URL for chaining
- `generate_audio` — audio in the clip
- `nsfw_check` — check before charge
- `web_search` / `tools: [{"type":"web_search"}]` — fresh web grounding

## Price

| Resolution | Price per second | With your video, per second |
|---|---|---|
| 480p | $0.125 | $0.075 |
| 720p | $0.281 | $0.169 |
| 1080p | $0.501 | $0.299 |

- Price = per-second price × clip length, rounded up to $0.001. Charged when the task is created and fully refunded if it fails.
- With your video, the seconds of the source clip are added to the seconds of the result.
- On the site — the **auto (-1)** tick next to the duration (not a list item). In the API: `duration: "auto"` or `-1` lets the model choose the length, up to 30 s. On creation the price of 30 s is charged (plus your video seconds, if any). When the video is ready the price is recalculated from the real length and the difference goes straight back to your balance (history: «Refund for unused video seconds»). The final price is `costUsd` in recordInfo, the length is `duration` in `resultJson`. With `video_mode: "edit"` the result is as long as your clip.

## Media limits

| Type | Limits |
|---|---|
| Images | up to **30**; JPG / PNG / WebP (also BMP / TIFF / GIF / HEIC via public https; or `asset://…` if you already have an id); upload via [files/upload](https://docs.mixmedia.tech/en/endpoints/upload) — up to **30 MB** per file |
| Videos | up to **10**; total ≤ **30 s** (each clip 2…30 s); MP4 / MOV → MP4 on the server; upload ≤ **200 MB** |
| Audio | up to **10**; total ≤ **30 s**; MP3 / WAV; upload ≤ **200 MB**; audio-only (no photo/video) is allowed |

Photos — public https or upload; video and audio — files/upload only.

## Task types and constraints

| `video_mode` | What you need | Notes |
|---|---|---|
| `auto` | prompt; media optional | default; the model picks reference / edit / extend |
| `reference` | photo / video / audio references | character, place, motion, style |
| `edit` | `video_urls` | length and aspect ratio follow your clip; clip ≥ 4 s |
| `extend` | `video_urls` | continue the clip |

Field `omni_reference_task_type` is an alias of `video_mode` (accepted by the API).

**Tip:** with `video_urls` the site preselects `aspect_ratio: adaptive` and `duration: auto` / `-1` (**auto (-1)** tick) — the model is picky about tiny duration mismatches. You can still set other values manually.

### 480p draft → 1080p final

1. Create a task with `draft: true` (resolution forced to `480p`).
2. Wait for `success`, then `POST` with `draft_task_id` = your `taskId` (no `prompt` or media). Final is `1080p`; reference-video seconds are not billed again.
3. Only `output_format`, `watermark`, `return_last_frame` may change. Drafts last 7 days. `draft` and `draft_task_id` are mutually exclusive.

On the site: «480p draft» tick; on recent works the **1080p** button.


## Parameters (field `input`)

| Parameter | Description | Values (default in bold) |
|---|---|---|
| `prompt` | What happens in the clip: scene, motion, camera, light, style | text, up to 30000 chars |
| `first_frame_url` | First frame: a photo link (https); the video starts from it | `"https://…"` |
| `last_frame_url` | Last frame (optional, only together with the first one) | `"https://…"` |
| `image_urls` | Reference photos (https) | `["https://…"]`, up to 30 |
| `video_urls` | Your videos (MP4): upload them via files/upload first and pass the returned links | `["https://…"]`, up to 10 |
| `audio_urls` | Audio (MP3): upload via files/upload first | `["https://…"]`, up to 10 |
| `video_mode` | Task type for your video: `auto` (default) — the model works it out from the description, `reference` — reference, `edit` — edit (length and aspect ratio follow your video), `extend` — extend. Omitted = `auto` | **`auto`** (the model works it out) · `reference` (reference) · `edit` (edit) · `extend` (extend) |
| `resolution` | Video resolution | `480p` (standard) · **`720p`** (high (HD)) · `1080p` (Full HD) |
| `aspect_ratio` | Aspect ratio | **`adaptive`** (matches your upload) · `16:9` (landscape) · `9:16` (portrait) · `1:1` (square) · `4:3` (traditional) · `3:4` (vertical traditional) · `21:9` (ultra-wide) |
| `duration` | Video duration in seconds | 4 to 30 s, or `auto` / `-1` (the model picks the length) (default **`5`**) |
| `generate_audio` | Video with sound | `true` `false` (default `true`) |
| `return_last_frame` | Return the last-frame image URL (for continuing the clip) | `true` `false` (default `false`) |
| `output_format` | Result container: `mp4` (default) or `mov` | **`mp4`** `mov` |
| `draft` | Черновик 480p | `true` `false` (default `false`) |
| `web_search` | Web search: fresh information from the internet (real places, events, products) | `true` `false` (default `false`) |
| `watermark` | "AI generated" watermark on the video | `true` `false` (default `false`) |
| `nsfw_check` | Content check: the description and photos are checked first; if not allowed, no task is created and nothing is charged | `true` `false` (default `false`) |
| `seed` | Variant number: the same number with the same prompt gives a similar result; empty or -1 = random | -1…4294967295 |
| `trim_video_to` | Trim each of your clips to N seconds: the beginning stays. Shorter clips are not changed | whole number, 2 to 30 |
| `trim_to_duration` | Trim each of your clips to the chosen `duration`: the beginning stays. With `duration: "auto"` or `video_mode: "edit"` — to the model limit | `true` `false` (default `false`) |
| `auto_trim` | If your clips together are longer than 30 s, trim them instead of returning an error (the longest first) | `true` `false` (default `false`) |

## Examples

### Prompt only

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"seedance-2.5","input":{"prompt":"A drone flies over an autumn forest at sunrise, fog, cinematic","resolution":"720p","aspect_ratio":"adaptive","duration":"5"}}'
```

```python
import os, requests

r = requests.post(
    "https://mixmedia.tech/api/v1/jobs/createTask",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    json={
      "model": "seedance-2.5",
      "input": {
        "prompt": "A drone flies over an autumn forest at sunrise, fog, cinematic",
        "resolution": "720p",
        "aspect_ratio": "adaptive",
        "duration": "5"
      }
    },
)
print(r.json())  # {"code": 200, "data": {"taskId": "..."}}
```

```javascript
const r = await fetch("https://mixmedia.tech/api/v1/jobs/createTask", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "model": "seedance-2.5",
    "input": {
      "prompt": "A drone flies over an autumn forest at sunrise, fog, cinematic",
      "resolution": "720p",
      "aspect_ratio": "adaptive",
      "duration": "5"
    }
  }),
});
console.log(await r.json()); // { code: 200, data: { taskId: "..." } }
```

### With a first frame

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"seedance-2.5","input":{"prompt":"The character slowly turns to the camera, the wind moves the hair","first_frame_url":"https://mixmedia.tech/static/img/previews/gpt-t2i.jpg","resolution":"720p","duration":"5"}}'
```

```python
import os, requests

r = requests.post(
    "https://mixmedia.tech/api/v1/jobs/createTask",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    json={
      "model": "seedance-2.5",
      "input": {
        "prompt": "The character slowly turns to the camera, the wind moves the hair",
        "first_frame_url": "https://mixmedia.tech/static/img/previews/gpt-t2i.jpg",
        "resolution": "720p",
        "duration": "5"
      }
    },
)
print(r.json())  # {"code": 200, "data": {"taskId": "..."}}
```

```javascript
const r = await fetch("https://mixmedia.tech/api/v1/jobs/createTask", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "model": "seedance-2.5",
    "input": {
      "prompt": "The character slowly turns to the camera, the wind moves the hair",
      "first_frame_url": "https://mixmedia.tech/static/img/previews/gpt-t2i.jpg",
      "resolution": "720p",
      "duration": "5"
    }
  }),
});
console.log(await r.json()); // { code: 200, data: { taskId: "..." } }
```

### With references

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"seedance-2.5","input":{"prompt":"A drone flies over an autumn forest at sunrise, fog, cinematic","image_urls":["https://mixmedia.tech/static/img/previews/gpt-t2i.jpg"],"resolution":"720p","aspect_ratio":"adaptive","duration":"5"}}'
```

```python
import os, requests

r = requests.post(
    "https://mixmedia.tech/api/v1/jobs/createTask",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    json={
      "model": "seedance-2.5",
      "input": {
        "prompt": "A drone flies over an autumn forest at sunrise, fog, cinematic",
        "image_urls": [
          "https://mixmedia.tech/static/img/previews/gpt-t2i.jpg"
        ],
        "resolution": "720p",
        "aspect_ratio": "adaptive",
        "duration": "5"
      }
    },
)
print(r.json())  # {"code": 200, "data": {"taskId": "..."}}
```

```javascript
const r = await fetch("https://mixmedia.tech/api/v1/jobs/createTask", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "model": "seedance-2.5",
    "input": {
      "prompt": "A drone flies over an autumn forest at sunrise, fog, cinematic",
      "image_urls": [
        "https://mixmedia.tech/static/img/previews/gpt-t2i.jpg"
      ],
      "resolution": "720p",
      "aspect_ratio": "adaptive",
      "duration": "5"
    }
  }),
});
console.log(await r.json()); // { code: 200, data: { taskId: "..." } }
```

### With your video

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"seedance-2.5","input":{"prompt":"Make the clip anime style, keep everything else","video_urls":["https://mixmedia.tech/files/<id>.mp4"],"video_mode":"edit","resolution":"720p","duration":"5"}}'
```

```python
import os, requests

r = requests.post(
    "https://mixmedia.tech/api/v1/jobs/createTask",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    json={
      "model": "seedance-2.5",
      "input": {
        "prompt": "Make the clip anime style, keep everything else",
        "video_urls": [
          "https://mixmedia.tech/files/<id>.mp4"
        ],
        "video_mode": "edit",
        "resolution": "720p",
        "duration": "5"
      }
    },
)
print(r.json())  # {"code": 200, "data": {"taskId": "..."}}
```

```javascript
const r = await fetch("https://mixmedia.tech/api/v1/jobs/createTask", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "model": "seedance-2.5",
    "input": {
      "prompt": "Make the clip anime style, keep everything else",
      "video_urls": [
        "https://mixmedia.tech/files/<id>.mp4"
      ],
      "video_mode": "edit",
      "resolution": "720p",
      "duration": "5"
    }
  }),
});
console.log(await r.json()); // { code: 200, data: { taskId: "..." } }
```

## Trim your video

Your clips together: up to 30 s. No need to cut a long clip beforehand:

- `trim_to_duration: true` — trim each clip to the chosen `duration`;
- `trim_video_to: N` — keep N seconds from the start;
- `auto_trim: true` — if the clips together are over the limit, cut them exactly to it instead of an error.

Trimming is free, and the price uses the trimmed seconds — fewer seconds cost less.

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"seedance-2.5","input":{"prompt":"Make the clip anime style, keep everything else","video_urls":["https://mixmedia.tech/files/<id>.mp4"],"video_mode":"edit","resolution":"720p","duration":"5","trim_to_duration":true,"auto_trim":true}}'
```

```python
import os, requests

r = requests.post(
    "https://mixmedia.tech/api/v1/jobs/createTask",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    json={
      "model": "seedance-2.5",
      "input": {
        "prompt": "Make the clip anime style, keep everything else",
        "video_urls": [
          "https://mixmedia.tech/files/<id>.mp4"
        ],
        "video_mode": "edit",
        "resolution": "720p",
        "duration": "5",
        "trim_to_duration": True,
        "auto_trim": True
      }
    },
)
print(r.json())  # {"code": 200, "data": {"taskId": "..."}}
```

```javascript
const r = await fetch("https://mixmedia.tech/api/v1/jobs/createTask", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "model": "seedance-2.5",
    "input": {
      "prompt": "Make the clip anime style, keep everything else",
      "video_urls": [
        "https://mixmedia.tech/files/<id>.mp4"
      ],
      "video_mode": "edit",
      "resolution": "720p",
      "duration": "5",
      "trim_to_duration": true,
      "auto_trim": true
    }
  }),
});
console.log(await r.json()); // { code: 200, data: { taskId: "..." } }
```

One uploaded file: up to 30 s. Without these options, clips that are too long return a `400` error with a hint.

## Create and result

1. `POST /api/v1/jobs/createTask` with `model` and `input`.
2. Poll `GET /api/v1/jobs/recordInfo?taskId=…` every 3–5 s (or a [callback](https://docs.mixmedia.tech/en/callbacks)).

### Successful createTask (HTTP 200)

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "task_...",
    "task_id": "task_...",
    "status": "submitted"
  }
}
```

### Completed task (recordInfo)

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "task_...",
    "task_id": "task_...",
    "state": "success",
    "status": "completed",
    "costUsd": "0.384",
    "refunded": false,
    "resultJson": {
      "videos": [
        {
          "url": "https://…/out.mp4"
        }
      ],
      "duration": 8
    },
    "lastFrameUrl": "https://…/last.jpg"
  }
}
```

Aliases: `task_id` = `taskId`; `status`: `pending` / `processing` / `completed` / `failed` alongside `state`. Clip URL is in `resultJson`. `size` is an alias of `aspect_ratio` in `input`.


Photos can be any public https links or uploads via [files/upload](https://docs.mixmedia.tech/en/endpoints/upload). Videos and audio — via files/upload only.

## Error handling

API responses are JSON `{"code", "msg", "data"}`; errors also have a string `error` field (type). **On `/api/v1/*` the HTTP status matches body `code`.**

### Example response bodies

**400**

```json
{
  "code": 400,
  "msg": "input.duration: must be between 4 and 30, or \"auto\" / -1",
  "data": null,
  "error": "invalid_request_error"
}
```

**401**

```json
{
  "code": 401,
  "msg": "Authentication failed. Please check your API key",
  "data": null,
  "error": "authentication_error"
}
```

**402**

```json
{
  "code": 402,
  "msg": "Insufficient balance. Please top up and try again. Available: $0.01, required: $0.015. Top up at https://ai.aimixmedia.site/cabinet/billing",
  "data": {
    "balance": "0.0100",
    "required": "0.0150",
    "currency": "USD",
    "topUpUrl": "https://mixmedia.tech/cabinet/billing"
  },
  "error": "payment_required"
}
```

**429**

```json
{
  "code": 429,
  "msg": "Rate limit exceeded: 10 createTask/upload requests per 10 s per key. Retry after 8s",
  "data": null,
  "error": "rate_limit_error"
}
```

**500**

```json
{
  "code": 500,
  "msg": "Internal server error",
  "data": null,
  "error": "server_error"
}
```

### Codes on createTask / upload

| code | error | Meaning | What to do |
|---|---|---|---|
| 400 | `invalid_request_error` | Invalid parameter or body | See `msg` |
| 400 | `unsupported_model` | Unknown model | [Model list](https://docs.mixmedia.tech/en/models) |
| 400 | `nsfw_content_detected` | Rejected by content check (`nsfw_check`) | Change the prompt or files |
| 401 | `authentication_error` | Missing or invalid key | Check the key |
| 402 | `payment_required` | Not enough balance | Top up |
| 403 | `permission_error` | No access | Contact support |
| 429 | `rate_limit_error` | Too many requests | Wait for `Retry-After` |
| 505 | `model_disabled` | Model temporarily disabled | Pick another |
| 500 | `server_error` | Our failure | Retry later |

Example `msg` values: `Authentication failed. Please check your API key` (401); `Insufficient balance. Please top up and try again…` (402).

### Task errors (recordInfo)

If the task was created and then failed: `state: "fail"` (alias `status: "failed"`), `failCode` / `failMsg`, and `error: { code, message, type }`. Money is refunded (`refunded: true`).

| failCode | error.type | Meaning |
|---|---|---|
| 400 | `nsfw_content_detected` / `invalid_request_error` | Moderation / safety rules |
| 422 | `invalid_request_error` | The model rejected the parameters (length, mode, resolution, file) |
| 501 | `server_error` | The model could not generate (`failMsg`) |
| 504 | `timeout_error` | Timed out — retry |
| 500 / 503 | `server_error` | Service temporarily unavailable |

### Common refusals for this model

- `duration` out of range, or `auto` / `-1` when auto length is not supported (Seedance 2.5 only)
- `resolution` / `aspect_ratio` (`size`) / `video_mode` not in the list above
- `video_mode` = `edit` or `extend` without `video_urls`; for `edit` — clip shorter than 4 s
- Reference video/audio too long without `auto_trim` / `trim_*` → `400`
- `nsfw_check: true` — on reject **immediate HTTP 400** (`nsfw_content_detected`); no task, no charge

### Rate limits

| What | Limit |
|---|---|
| createTask and upload | 20 per 10 s per key |
| recordInfo / list / balance | 100 per 10 s per key |

Over the limit — HTTP 429 and `Retry-After`.
