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

# seedance-2.5 Video Generation

>  - Async API; returns task_id for polling
- Text-to-video / multimodal reference / edit / extend / first–last frame
- Up to 30s per job; up to 30 images + 10 videos + 10 audios as references
- Resolution 480p / 720p only; mp4 or mov output 

<Info>
  **Main changes vs 2.0**: max duration 15s → **30s**; references 9 images + 3 videos + 3 audios → **30 images + 10 videos + 10 audios**; **audio-only** reference supported; **mov** output added.\
  **Note**: resolution is **480p / 720p only** (2.0’s 1080p / 4k are **not** available on 2.5).
</Info>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.apimart.ai/v1/videos/generations \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --data '{
      "model": "seedance-2.5",
      "prompt": "A cinematic 30-second steampunk miniature landscape sequence",
      "size": "16:9",
      "resolution": "720p",
      "duration": 30
    }'
  ```

  ```python Python theme={null}
  import requests

  url = "https://api.apimart.ai/v1/videos/generations"

  payload = {
      "model": "seedance-2.5",
      "prompt": "A cinematic 30-second steampunk miniature landscape sequence",
      "size": "16:9",
      "resolution": "720p",
      "duration": 30,
  }

  headers = {
      "Authorization": "Bearer <token>",
      "Content-Type": "application/json",
  }

  response = requests.post(url, json=payload, headers=headers)

  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const url = "https://api.apimart.ai/v1/videos/generations";

  const payload = {
    model: "seedance-2.5",
    prompt: "A cinematic 30-second steampunk miniature landscape sequence",
    size: "16:9",
    resolution: "720p",
    duration: 30,
  };

  const headers = {
    Authorization: "Bearer <token>",
    "Content-Type": "application/json",
  };

  fetch(url, {
    method: "POST",
    headers: headers,
    body: JSON.stringify(payload),
  })
    .then((response) => response.json())
    .then((data) => console.log(data))
    .catch((error) => console.error("Error:", error));
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "io/ioutil"
      "net/http"
  )

  func main() {
      url := "https://api.apimart.ai/v1/videos/generations"

      payload := map[string]interface{}{
          "model":      "seedance-2.5",
          "prompt":     "A cinematic 30-second steampunk miniature landscape sequence",
          "size":       "16:9",
          "resolution": "720p",
          "duration":   30,
      }

      jsonData, _ := json.Marshal(payload)

      req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
      req.Header.Set("Authorization", "Bearer <token>")
      req.Header.Set("Content-Type", "application/json")

      client := &http.Client{}
      resp, err := client.Do(req)
      if err != nil {
          panic(err)
      }
      defer resp.Body.Close()

      body, _ := ioutil.ReadAll(resp.Body)
      fmt.Println(string(body))
  }
  ```

  ```java Java theme={null}
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.net.URI;

  public class Main {
      public static void main(String[] args) throws Exception {
          String url = "https://api.apimart.ai/v1/videos/generations";

          String payload = """
          {
            "model": "seedance-2.5",
            "prompt": "A cinematic 30-second steampunk miniature landscape sequence",
            "size": "16:9",
            "resolution": "720p",
            "duration": 30
          }
          """;

          HttpClient client = HttpClient.newHttpClient();
          HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create(url))
              .header("Authorization", "Bearer <token>")
              .header("Content-Type", "application/json")
              .POST(HttpRequest.BodyPublishers.ofString(payload))
              .build();

          HttpResponse<String> response = client.send(request,
              HttpResponse.BodyHandlers.ofString());

          System.out.println(response.body());
      }
  }
  ```

  ```php PHP theme={null}
  <?php

  $url = "https://api.apimart.ai/v1/videos/generations";

  $payload = [
      "model" => "seedance-2.5",
      "prompt" => "A cinematic 30-second steampunk miniature landscape sequence",
      "size" => "16:9",
      "resolution" => "720p",
      "duration" => 30
  ];

  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      "Authorization: Bearer <token>",
      "Content-Type: application/json"
  ]);

  $response = curl_exec($ch);
  curl_close($ch);

  echo $response;
  ?>
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "code": 200,
    "data": [
      {
        "status": "submitted",
        "task_id": "task_01KMCGF6BQGN3X28H3KSR50X5T"
      }
    ]
  }
  ```

  ```json 400 theme={null}
  {
    "error": {
      "code": 400,
      "message": "Invalid request parameters",
      "type": "invalid_request_error"
    }
  }
  ```

  ```json 401 theme={null}
  {
    "error": {
      "code": 401,
      "message": "Authentication failed. Please check your API key",
      "type": "authentication_error"
    }
  }
  ```

  ```json 402 theme={null}
  {
    "error": {
      "code": 402,
      "message": "Insufficient balance. Please top up and try again",
      "type": "payment_required"
    }
  }
  ```

  ```json 429 theme={null}
  {
    "error": {
      "code": 429,
      "message": "Too many requests. Please try again later",
      "type": "rate_limit_error"
    }
  }
  ```

  ```json 500 theme={null}
  {
    "error": {
      "code": 500,
      "message": "Internal server error. Please try again later",
      "type": "server_error"
    }
  }
  ```
</ResponseExample>

## Authentication

<ParamField header="Authorization" type="string" required>
  Bearer token auth. Get a key from the [API Key page](https://apimart.ai/keys).

  ```
  Authorization: Bearer YOUR_API_KEY
  ```
</ParamField>

## Request parameters

<ParamField body="model" type="string" required>
  Fixed value: `seedance-2.5`
</ParamField>

<ParamField body="nsfw_check" type="boolean" default="false">
  Legt fest, ob der Inhalt vor dem Absenden des Videoauftrags moderiert wird.

  * `true`: Prompts und Eingabebilder mit `omni-moderation-latest` prüfen
  * `false` oder nicht angegeben: keine Moderationsanfrage und damit keine zusätzlichen Moderationskosten oder Verzögerung (Standard)

  Geprüfte Inhalte:

  * Text: `prompt`, `negative_prompt`
  * Bilder: `image_urls`, `image_with_roles[].url`, `first_frame_image`, `last_frame_image`
  * Private Bild-Assets mit `asset://`: ursprüngliche öffentliche URL auflösen und prüfen
  * Base64-Bilder: nach der Umwandlung in eine öffentliche URL prüfen

  `video_urls`, `audio_urls` sowie private Video- und Audio-Assets werden **nicht geprüft**, da das Moderationsmodell Video und Audio nicht unterstützt.

  Unterstützte Modell-IDs: `seedance-2.0`, `seedance-2.0-fast`, `seedance-2.0-mini`, `seedance-2.0-face`, `seedance-2.0-fast-face`, `seedance-2-0` (alter Name) und `seedance-2.5`.

  Der Moderationsaufruf selbst wird dem Benutzer, der die Videoanfrage absendet, nicht berechnet.

  <Warning>
    * Bei erkannten Inhalten wird synchron HTTP 400 (`nsfw_content_detected`) zurückgegeben. Es werden weder ein Auftrag noch eine `task_id` erstellt und kein Kontingent für die Videogenerierung abgezogen
    * Ist die Moderation nicht verfügbar, läuft in ein Timeout oder liefert eine ungültige Antwort, gilt **fail-open** und die Generierung wird fortgesetzt. Diese Option ist keine absolute Garantie für Inhaltssicherheit
    * Eingaben, die nicht in eine öffentliche Bild-URL aufgelöst werden können, werden übersprungen; nicht unterstützte Modelle ignorieren `nsfw_check: true` ohne Meldung
  </Warning>

  Beispiel:

  ```json theme={null}
  {
    "model": "seedance-2.5",
    "prompt": "a cat walking on the beach",
    "image_urls": ["https://cdn.example.com/ref.png"],
    "nsfw_check": true
  }
  ```

  Antwort bei Erkennung:

  ```json theme={null}
  {
    "error": {
      "message": "Your request was rejected by content moderation (`nsfw_check` is enabled). Flagged categories: sexual, violence/graphic.",
      "type": "nsfw_content_detected",
      "param": "",
      "code": "nsfw_content_detected"
    }
  }
  ```
</ParamField>

<ParamField body="prompt" type="string" required>
  Prompt. Reference media with `@图片1` / `@视频1` / `@音频1` (1-based index matching array order). English aliases in prompts may also be used depending on model behavior; keep indices aligned with arrays.

  Example: `"Use @视频1 for first-person framing throughout, @音频1 as BGM, first frame is @图片1"`
</ParamField>

<ParamField body="resolution" type="string" default="720p">
  Resolution — **only**:

  * `480p`
  * `720p` (default)

  Values like `1080p` / `2k` / `4k` return a sync **400**.
</ParamField>

<ParamField body="size" type="string" default="adaptive">
  Aspect ratio (field name `aspect_ratio` is also accepted).

  Values: `16:9`, `4:3`, `1:1`, `3:4`, `9:16`, `21:9`, `adaptive` (default)

  <Warning>
    Edit, extend, and first/last-frame jobs have hard `size` constraints — see [Task types and constraints](#task-types-and-constraints).
  </Warning>
</ParamField>

<ParamField body="duration" type="integer" default="5">
  Duration in seconds:

  * `4` \~ `30`
  * `-1`: model picks duration (pre-charge at the **30s** cap; settle to actual length after completion)

  If omitted: generate and bill **5** seconds.
</ParamField>

<ParamField body="generate_audio" type="boolean" default="true">
  Whether to generate audio (alias field name: `audio`).

  * `true`: with audio (default)
  * `false`: silent video
</ParamField>

<ParamField body="watermark" type="boolean" default="false">
  Add an “AI generated” watermark. Default `false`.
</ParamField>

<ParamField body="seed" type="integer">
  Random seed. Different seeds usually yield different results for the same request; the same seed is similar but not guaranteed identical.
</ParamField>

<ParamField body="output_format" type="string" default="mp4">
  Output container:

  * `mp4` (default)
  * `mov`: higher color precision — **recommended** for edit / extend workflows
</ParamField>

<ParamField body="image_urls" type="array<string>">
  Reference image URLs, all treated as `reference_image`.

  Supports:

  * Public URL: `https://example.com/pic.jpg`
  * Private asset: `asset://cm9xxxxxxxx`

  For first/last frames use `image_with_roles`.

  <Warning>
    * Max **30** images
    * Prefer `image_with_roles` for first/last-frame roles
  </Warning>
</ParamField>

<ParamField body="image_with_roles" type="array<object>">
  Images with explicit roles.

  <Expandable title="Fields">
    <ParamField body="url" type="string" required>
      Image URL or `asset://...`
    </ParamField>

    <ParamField body="role" type="string" required>
      * `first_frame`: first frame (1 image)
      * `last_frame`: last frame (1 image, usually with first frame)
      * `reference_image`: reference image (up to 30 total)
    </ParamField>
  </Expandable>

  Example:

  ```json theme={null}
  [
    {"url": "https://example.com/first.jpg", "role": "first_frame"},
    {"url": "https://example.com/last.jpg", "role": "last_frame"}
  ]
  ```

  <Note>
    If `video_urls` / `audio_urls` are present, `first_frame` / `last_frame` are auto-converted to `reference_image` (multimodal reference job).
  </Note>
</ParamField>

<ParamField body="video_urls" type="array<string>">
  Reference videos (`reference_video`).

  **Input**: video URL or asset ID (`asset://...`).

  See [Reference video specs](#reference-video-specs).
</ParamField>

<ParamField body="audio_urls" type="array<string>">
  Reference audio URLs (`reference_audio`). Public URLs or `asset://...`.

  Max **10**; total duration ≤ **30s** (each clip 2\~30s).

  <Note>
    2.5 supports **audio-only** reference (no image/video required).
  </Note>
</ParamField>

<ParamField body="return_last_frame" type="boolean" default="false">
  When `true`, the successful result also includes the last-frame image for chaining.
</ParamField>

<ParamField body="tools" type="array<object>">
  Tool list for enhancements such as web search.

  Example:

  ```json theme={null}
  "tools": [{"type": "web_search"}]
  ```

  <Expandable title="Fields">
    <ParamField body="type" type="string" required>
      Tool type

      Values:

      * `web_search` — web search; generation can ground on online information
    </ParamField>
  </Expandable>
</ParamField>

## Media limits

| Media  | Limits                                                                                                                     |
| ------ | -------------------------------------------------------------------------------------------------------------------------- |
| Images | ≤30; jpeg / png / webp / bmp / tiff / gif / heic / heif; aspect ratio \[0.4, 2.5]; side length \[300, 6000]px; each \<30MB |
| Videos | See [Reference video specs](#reference-video-specs)                                                                        |
| Audio  | ≤10; wav / mp3; each \[2, 30]s and **total ≤30s**; each ≤15MB                                                              |

### Reference video specs

* **Input**: video URL or asset ID (`asset://...`)
* **Container**: `mp4`, `mov` — codecs in the table below
* **Resolution**: `480p`, `720p`
* **Duration**: each clip \[2, 30] s; up to **10** reference videos; **total duration of all videos ≤ 30s**
* **Per-video dimensions**:
  * Aspect ratio (width/height): \[0.4, 2.5]
  * Side length (px): \[300, 6000]
  * Total pixels: \[640×640=409600, 3326×2494=8295044], i.e. width × height must fall in \[409600, 8295044]
* **Size**: each video ≤ **200 MB**
* **Frame rate (FPS)**: \[24, 60]

#### Supported codecs

| Container | Video codec   | Audio codec |
| --------- | ------------- | ----------- |
| `mp4`     | H.264 / H.265 | AAC / MP3   |
| `mov`     | H.264 / H.265 | AAC / MP3   |

## Task types and constraints

The service infers task type from references and **prompt intent**. The last three types hard-constrain `size` / `duration`; violations fail **asynchronously after the job starts** (e.g. `InvalidParameter.TaskTypeConstraint`):

| Task type                | Trigger                                                                       | Constraints                                                                                                         |
| ------------------------ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Text-to-video            | Text only                                                                     | None                                                                                                                |
| Reference-to-video       | References + normal descriptive prompt                                        | None. **Avoid** words like “edit / extend / continue / delete / replace” in the prompt to prevent misclassification |
| Video edit               | References + prompt with “edit video / add / delete / modify / replace”, etc. | `size` must be `adaptive`, `duration` must be `-1`; source video 4\~30s                                             |
| Video extend             | References + prompt with “extend forward/backward / continue / sequel”        | `size` must be `adaptive`                                                                                           |
| First / first–last frame | `image_with_roles` uses `first_frame` / `last_frame`                          | `size` must be `adaptive` (also checked at submit time)                                                             |

## Asset library

You can pass public URLs as references, or upload into the library first and use `asset://`. **Prefer the library** when:

1. **Real human faces must use the library** — raw URLs are blocked by content moderation; only approved library assets can be used
2. **Assets are reused often** — upload once, skip repeated moderation on later jobs, faster submits
3. **URLs are signed temporary links** — the platform stores a durable copy on ingest so later use does not depend on the original URL staying alive
4. Library assets are **synced to all available channels** so multi-channel routing can use them wherever the job lands

The library is **shared** with the 2.0 family; approved `asset://` IDs work in both 2.0 and 2.5 generation requests. Full submit fields: also see [Private avatar assets](/de/api-reference/videos/seedance-2-0/private-avatar).

### Upload assets

```bash theme={null}
POST /v1/seedance2/private-avatar/assets
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

```bash theme={null}
curl --request POST \
  --url https://api.apimart.ai/v1/seedance2/private-avatar/assets \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
    "model": "seedance-2.5",
    "group": { "name": "my-assets" },
    "asset_type": "Image",
    "assets": [
      { "url": "https://example.com/a.png", "name": "a" },
      { "url": "https://example.com/b.png", "name": "b" }
    ]
  }'
```

| Field           | Required | Description                                                                                                   |
| --------------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| `model`         | No       | Model the assets will be used with (defaults to 2.0). **Sets the duration tier**; for 2.5 pass `seedance-2.5` |
| `group`         | No       | Asset group; created automatically if omitted                                                                 |
| `asset_type`    | Yes      | `Image` / `Video` / `Audio`                                                                                   |
| `assets[].url`  | Yes      | Public media URL                                                                                              |
| `assets[].name` | No       | Asset name                                                                                                    |

The response includes a local task `id`. Poll with [Get task status](/de/api-reference/tasks/status) (`GET /v1/tasks/{id}`). After approval, list assets to obtain the `asset://` ID.

### Media limits (validated at submit)

Violations return **400 immediately** (which asset index and which rule), without starting moderation or consuming moderation quota:

| Media | Limits                                                                                                                |
| ----- | --------------------------------------------------------------------------------------------------------------------- |
| Image | jpeg / png / webp / bmp / tiff / gif / heic / heif; side length \[300, 6000]px; aspect ratio \[0.4, 2.5]; each \<30MB |
| Video | mp4 / mov; each ≤200MB; duration **by model**: 2.0 family \[2, 15]s, **2.5 \[2, 30]s**                                |
| Audio | wav / mp3; each ≤15MB; duration tiers same as video                                                                   |

Error example:

```json theme={null}
{
  "error": {
    "code": "invalid_asset_material",
    "message": "video #1: duration must be between 2s and 15s for seedance-2.0 (got 30s)"
  }
}
```

<Note>
  For 30s video assets, submit with `"model": "seedance-2.5"` (2.0 cannot use assets longer than 15s).\
  If the platform cannot probe the media (e.g. network blip), it may pass through and leave the decision to moderation.
</Note>

### Use in generation requests

Reference approved assets with `asset://` in `image_urls` / `image_with_roles` / `video_urls` / `audio_urls`:

```json theme={null}
{
  "model": "seedance-2.5",
  "prompt": "The person in @图片1 walks along the beach",
  "image_urls": ["asset://cm9xxxxxxxx"],
  "duration": 8
}
```

Multi-channel: after ingest, assets sync to all available channels so any routed channel can use them. If a channel’s copy is missing, the platform may re-upload from the original URL as a fallback (if that URL is already expired, that channel is skipped and another takes over).

### Management APIs (quick reference)

| Method                              | Path                                             | Purpose                                |
| ----------------------------------- | ------------------------------------------------ | -------------------------------------- |
| `GET`                               | `/v1/seedance2/private-avatar/assets`            | List assets                            |
| `GET`                               | `/v1/seedance2/private-avatar/assets/{asset_id}` | Asset detail (incl. moderation status) |
| `PATCH`                             | `/v1/seedance2/private-avatar/assets/{asset_id}` | Update asset metadata                  |
| `DELETE`                            | `/v1/seedance2/private-avatar/assets/{asset_id}` | Delete asset                           |
| `POST` / `GET` / `PATCH` / `DELETE` | `/v1/seedance2/private-avatar/groups[/{id}]`     | Asset group management                 |

Asset APIs are **free of charge** (auth + rate limits only); they do not create billing records.

### FAQ

**Q: How long does moderation take?**\
Images usually seconds; video and real-person assets may take minutes. Poll the task `id` until a terminal status.

**Q: Moderation failed with no clear reason?**\
Some failures omit a detailed reason (often a transient fetch failure). The platform already retries once; if it still fails, change the URL (ensure it is publicly downloadable) and resubmit.

**Q: Same asset for both 2.0 and 2.5 — upload twice?**\
No. Upload once; `asset://` works for both generations. Cross-channel / cross-model sync is handled by the platform.

**Q: Can I pass a real-person face as a raw URL?**\
Real-person assets must go through the library first.

## Billing

* Billed by **seconds × resolution tier**.
* **With reference video input**: billable seconds = total input video duration (≤30s) + output duration, at the input-reference rate tier.
* `duration = -1` (auto): pre-charge at the **30s** cap; settle to actual output after completion.
* Omit `duration`: generate and bill **5** seconds.
* Failed jobs or content moderation blocks: **full refund** (charge only on successful output).

## Request examples

### Text-to-video (30s)

```json theme={null}
{
  "model": "seedance-2.5",
  "prompt": "A cinematic 30-second steampunk miniature landscape sequence",
  "size": "16:9",
  "resolution": "720p",
  "duration": 30
}
```

### Multimodal reference (image + video + audio)

```json theme={null}
{
  "model": "seedance-2.5",
  "prompt": "Use @视频1 for first-person framing, @音频1 as BGM, first frame is @图片1",
  "image_urls": [
    "https://example.com/pic1.jpg",
    "https://example.com/pic2.jpg"
  ],
  "video_urls": ["https://example.com/ref.mp4"],
  "audio_urls": ["https://example.com/bgm.mp3"],
  "size": "16:9",
  "duration": 11
}
```

### Video edit

```json theme={null}
{
  "model": "seedance-2.5",
  "prompt": "Edit the video: remove all passers-by in @视频1, keep only the main character",
  "video_urls": ["https://example.com/input.mp4"],
  "size": "adaptive",
  "duration": -1,
  "output_format": "mov"
}
```

### First–last frame

```json theme={null}
{
  "model": "seedance-2.5",
  "prompt": "The girl in the image says \"cheese\" to the camera, 360-degree orbit shot",
  "image_with_roles": [
    {"url": "https://example.com/first.jpg", "role": "first_frame"},
    {"url": "https://example.com/last.jpg", "role": "last_frame"}
  ],
  "size": "adaptive",
  "duration": 5
}
```

### Private asset

```json theme={null}
{
  "model": "seedance-2.5",
  "prompt": "A person walking naturally on a city street in sunlight",
  "image_urls": ["asset://cm9xxxxxxxx"],
  "duration": 5,
  "resolution": "720p"
}
```

### Web search

```json theme={null}
{
  "model": "seedance-2.5",
  "prompt": "Generate a short tech-news video intro based on the latest information",
  "size": "16:9",
  "resolution": "720p",
  "duration": 8,
  "tools": [{"type": "web_search"}]
}
```

## Common errors

| Symptom                                                        | Cause                                                                              |
| -------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| 400 `This model only supports 480p or 720p resolution`         | Passed 1080p / 2k / 4k, etc.                                                       |
| 400 `This model only supports duration 4-30 seconds, or -1`    | `duration` out of range                                                            |
| 400 `first_frame/last_frame tasks only support ratio=adaptive` | First/last-frame job with a fixed aspect ratio                                     |
| Async failure (edit / extend style message)                    | Prompt classified as edit/extend but `size` / `duration` violate that type’s rules |
| Async failure `SensitiveContentDetected`                       | Media or output failed moderation                                                  |

## Response (submit)

<ResponseField name="code" type="integer">
  Status code; `200` on success
</ResponseField>

<ResponseField name="data" type="array">
  Submit response with `status` / `task_id`

  <Expandable title="Array item">
    <ResponseField name="status" type="string">
      Initially `submitted`
    </ResponseField>

    <ResponseField name="task_id" type="string">
      Task ID for [status polling](/de/api-reference/tasks/status)
    </ResponseField>
  </Expandable>
</ResponseField>

## Completed task (`GET /v1/tasks/{task_id}`)

After submit, poll with [Get task status](/de/api-reference/tasks/status). When `status` is `completed`, the payload looks like this.

### Completed response example

```json theme={null}
{
  "code": 200,
  "data": {
    "id": "task_01KZEWTMH6TG3Z1X2QRP2B9ZG9",
    "status": "completed",
    "progress": 100,
    "created": 1786132648,
    "completed": 1786132748,
    "actual_time": 100,
    "estimated_time": 300,
    "cost": 0.3104,
    "credits_cost": 3.104,
    "usage": {
      "completion_tokens": 38800
    },
    "result": {
      "videos": [
        {
          "url": [
            "https://cdn.example.com/video/…-video_task_01KZEWTMH6TG3Z1X2QRP2B9ZG9.mp4"
          ],
          "expires_at": 1786219148
        }
      ]
    }
  }
}
```

### Completed fields

| Field                                 | Type          | Description                                                                                                     |
| ------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------- |
| `data.id`                             | string        | Task ID                                                                                                         |
| `data.status`                         | string        | Always `completed` when done                                                                                    |
| `data.progress`                       | int           | Always `100` when done                                                                                          |
| `data.created` / `data.completed`     | int           | Submit / finish time (Unix seconds)                                                                             |
| `data.actual_time`                    | int           | Elapsed seconds = completed − created                                                                           |
| `data.estimated_time`                 | int           | Platform ETA in seconds (reference only)                                                                        |
| `data.cost`                           | float         | **Actual charge in USD** for this job (includes discounts; matches billing logs)                                |
| `data.credits_cost`                   | float         | Credits charged = `cost × 10`                                                                                   |
| `data.usage.completion_tokens`        | int           | Tokens consumed. Settlement: `cost = completion_tokens ÷ 10⁶ × token unit price × discount`                     |
| `data.result.videos[].url`            | **string\[]** | Video URL(s). **This is an array** — use `url[0]`. Values are **long-lived platform CDN URLs** after re-hosting |
| `data.result.videos[].expires_at`     | int           | Expiry timestamp (kept for compatibility; re-hosted URLs are long-lived)                                        |
| `data.result.videos[].last_frame_url` | string        | Last-frame image (only if submit used `return_last_frame=true`)                                                 |

### Notes

* Failed tasks (`status=failed`): `cost` is always `0` (pre-charge fully refunded); reason in `data.error.message`
* `usage` may be missing for a few seconds after completion (settlement lag); query again shortly

## Differences from 2.0

| Feature            | 2.0                      | 2.5                                   |
| ------------------ | ------------------------ | ------------------------------------- |
| Model              | `seedance-2.0`, etc.     | `seedance-2.5`                        |
| Duration           | \~4–15s                  | **4–30s**, or `-1` auto               |
| Resolution         | 480p / 720p / 1080p / 4k | **480p / 720p only**                  |
| Reference images   | ≤9                       | **≤30**                               |
| Reference videos   | ≤3, total \~\<15s        | **≤10, total ≤30s**                   |
| Reference audio    | ≤3, total ≤15s           | **≤10, total ≤30s**; audio-only OK    |
| Output format      | Mainly mp4               | **mp4 / mov**                         |
| Watermark          | —                        | `watermark`                           |
| Private `asset://` | Supported                | **Shared** with the same private APIs |
