> ## Documentation Index
> Fetch the complete documentation index at: https://gccai.heqingsong.uk/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 supports 480p / 720p / 1080p; mp4 or mov output 

> [!NOTE]
>
> **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 supports **480p / 720p / 1080p** (2.0’s 4k is **not** available on 2.5).

```bash cURL theme={null}
curl --request POST \
  --url https://gccai.heqingsong.uk/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://gccai.heqingsong.uk/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://gccai.heqingsong.uk/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://gccai.heqingsong.uk/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://gccai.heqingsong.uk/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://gccai.heqingsong.uk/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;
?>
```

```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"
  }
}
```

## Authentication

**Authorization** `string` *required*

Bearer token auth. Get a key from the [API Key page](https://gccai.heqingsong.uk/keys).

```
Authorization: Bearer YOUR_API_KEY
```

## Request parameters

**model** `string` *required*

Fixed value: `seedance-2.5`

**nsfw\_check** `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

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"
  }
}
```

**draft** `boolean`

Mit `true` wird ein 480p-Entwurf erstellt. Nur `seedance-2.5` unterstützt dies. Ohne Angabe erfolgt eine normale Generierung.

* `resolution` darf nur `480p` sein; ohne Angabe gilt ebenfalls `480p`
* Nicht mit `draft_task_id` kombinierbar
* Entwürfe sind **7 Tage ab Erstellung** gültig; nach erfolgreichem Abschluss kann ein finales 1080p-Video erstellt werden

Ablauf siehe [Entwurfsmodus](#entwurfsmodus).

**draft\_task\_id** `string`

Erstellt ein finales 1080p-Video aus einem abgeschlossenen Entwurf. Verwenden Sie die GccAi-Aufgaben-ID aus der Entwurfsantwort (`data[0].task_id`), keine Video-URL.

* Nur `seedance-2.5`; dasselbe Modell wie beim Entwurf verwenden
* Der Entwurf muss dem Benutzer des aktuellen API Key gehören, erfolgreich abgeschlossen und höchstens 7 Tage alt sein
* `resolution` darf nur `1080p` sein; ohne Angabe gilt ebenfalls `1080p`
* Nicht mit `draft: true` kombinieren und geerbte Felder wie `prompt`, Referenzmedien oder Dauer nicht erneut senden

Änderbare Felder und Einschränkungen siehe [Entwurfsmodus](#entwurfsmodus).

**prompt** `string`

Für normale Generierung und Entwürfe erforderlich. Bei `draft_task_id` darf das Feld nicht gesendet werden; der Prompt wird vom Entwurf übernommen.

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"`

**resolution** `string` (default: 720p)

Resolution — **only**:

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

Unsupported values like `2k` / `4k` return a sync **400**.

Ausnahme im Entwurfsmodus: `draft: true` unterstützt ausschließlich `480p` als Standard; `draft_task_id` ausschließlich `1080p` als Standard.

**size** `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).

**duration** `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.

**generate\_audio** `boolean` (default: true)

Whether to generate audio (alias field name: `audio`).

* `true`: with audio (default)
* `false`: silent video

**watermark** `boolean` (default: false)

Add an “AI generated” watermark. Default `false`.

**seed** `integer`

Random seed. Different seeds usually yield different results for the same request; the same seed is similar but not guaranteed identical.

**output\_format** `string` (default: mp4)

Output container:

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

**omni\_reference\_task\_type** `string` (default: auto)

Sub-task type hint: `auto` / `reference` / `edit` / `extend`.

Passing `reference` / `edit` / `extend` validates that type’s constraints **at submit time**. Invalid requests return **400**, create no task, and charge nothing. See [Task types and constraints](#task-types-and-constraints).

**image\_urls** `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

**image\_with\_roles** `array\<object\>`

Images with explicit roles.

**Fields**

**url** `string` *required*

Image URL or `asset://...`

**role** `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)

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).

**video\_urls** `array\<string\>`

Reference videos (`reference_video`).

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

See [Reference video specs](#reference-video-specs).

**audio\_urls** `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).

**return\_last\_frame** `boolean` (default: false)

When `true`, the successful result also includes the last-frame image for chaining.

**tools** `array\<object\>`

Tool list for enhancements such as web search.

Example:

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

**Fields**

**type** `string` *required*

Tool type

Values:

* `web_search` — web search; generation can ground on online information

## 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`, `1080p`
* **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) |

### Turn async errors into sync errors with `omni_reference_task_type`

Passing `reference` / `edit` / `extend` declares the sub-task type so its constraints are **validated at submit time**. Invalid requests return 400 immediately: no task is created and nothing is charged — you do not have to poll until `failed`.

| Value | Meaning | Constraints checked at submit |
| - | - | - |
| `auto` (default) | Model infers the type | None (violations fail **asynchronously**) |
| `reference` | Reference-to-video | None (`size` / `duration` unrestricted) |
| `edit` | Video edit | At least one item in `video_urls`; `size` must be `adaptive` (or omitted); `duration` must be `-1` (or omitted, see below); source video 4\~30s |
| `extend` | Video extend | At least one item in `video_urls`; `size` must be `adaptive` (or omitted) |

Notes:

* **If `edit` omits `duration`, the platform sets it to `-1`.** Omitting `duration` otherwise defaults to 5 seconds, which conflicts with the upstream `edit` requirement of `-1`. The trade-off is a **30-second prepaid hold** (same as `duration: -1`); unused amount is refunded after completion. For a smaller hold, skip `edit` and use default `auto`.
* **If `edit` sends `duration` explicitly, it must be `-1`.** Any other value returns 400 (output length follows the source video).
* ⚠️ **You can still fail asynchronously with `InvalidParameter.TaskTypeMismatch`.** Upstream re-classifies the job from the prompt; a mismatch with your declared type is rejected. Keep prompt wording aligned (`edit`: “edit / delete / replace…”; `extend`: “extend forward/backward / continue…”).
* All Seedance 2.5 channels share the same validation and upstream payload; switching channels does not change this behavior.

## 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](https://gccai.heqingsong.uk/de/api-reference/videos/seedance-2-0/private-avatar.md).

### 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://gccai.heqingsong.uk/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](https://gccai.heqingsong.uk/de/api-reference/tasks/status.md) (`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.

### 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

Diese Regeln gelten für normale Generierung und Entwürfe. Für finale Videos siehe [Abrechnung im Entwurfsmodus](#abrechnung-im-entwurfsmodus).

* 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).

## Entwurfsmodus

Erstellen Sie zunächst einen 480p-Entwurf zur Sichtprüfung und anschließend mit dessen Aufgaben-ID ein finales 1080p-Video. Dieses übernimmt Prompt, Referenzmedien, Dauer, Seitenverhältnis, `seed`, Audioeinstellung und Aufgabentyp, um dem Entwurf zu entsprechen.

### Schritt 1: 480p-Entwurf erstellen

Ergänzen Sie eine normale Anfrage um `"draft": true`. Medien- und Aufgabentypbeschränkungen gelten weiterhin. Ohne `resolution` gilt `480p`; `720p` oder `1080p` ergeben 400.

```bash theme={null}
curl -X POST 'https://gccai.heqingsong.uk/v1/videos/generations' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "seedance-2.5",
    "prompt": "Ein Fuchs sitzt im Gras, die Kamera fährt langsam zurück und eine Brise bewegt sein Fell",
    "image_urls": ["https://example.com/reference.png"],
    "duration": 5,
    "size": "16:9",
    "draft": true
  }'
```

Lesen Sie die Entwurfs-ID aus `data[0].task_id`. Fragen Sie den [Aufgabenstatus](https://gccai.heqingsong.uk/de/api-reference/tasks/status.md) ab und senden Sie das finale Video erst bei `data.status=completed`.

```bash theme={null}
curl 'https://gccai.heqingsong.uk/v1/tasks/task_01KZEVX5R6DM5K9T698P1SDKCR' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

Abfrageintervall: 5–15 Sekunden. `pending` bedeutet Warteschlange, `processing` Generierung, `completed` Erfolg und `failed` Fehler. Die Video-URL steht im Array `data.result.videos[].url`; lesen Sie `url[0]`.

### Schritt 2: Finales 1080p-Video erstellen

Setzen Sie `draft_task_id` auf die ID aus Schritt 1. Ohne `resolution` gilt `1080p`; andere Auflösungen ergeben 400.

```bash theme={null}
curl -X POST 'https://gccai.heqingsong.uk/v1/videos/generations' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "seedance-2.5",
    "draft_task_id": "task_01KZEVX5R6DM5K9T698P1SDKCR",
    "output_format": "mov",
    "return_last_frame": true,
    "watermark": false
  }'
```

Das finale Video erhält eine neue Aufgaben-ID. Ergebnisabfrage über `GET /v1/tasks/{task_id}`.

Folgende Felder werden geerbt. **Nicht erneut senden: Selbst identische Werte ergeben 400**:

`prompt`, `image_urls`, `image_with_roles`, `video_urls`, `audio_urls`, `duration`, `size`, `aspect_ratio`, `seed`, `generate_audio`, `audio`, `omni_reference_task_type`, `generation_type`, `camerafixed`, `web_search`, `tools`.

Diese Ausgabeoptionen können geändert werden:

| Parameter | Beschreibung |
| - | - |
| `output_format` | `mp4` (Standard) oder `mov` |
| `return_last_frame` | Letztes Bild zurückgeben; ohne Angabe gilt der Modellstandard, nicht die Entwurfseinstellung |
| `watermark` | Wasserzeichen hinzufügen; ohne Angabe gilt der Modellstandard, nicht die Entwurfseinstellung |

> [!WARNING]
>
> `draft: true` und `draft_task_id` schließen sich aus. Weder Entwürfe noch finale Videos unterstützen `service_tier`.
>
> Entwürfe sind ab Erstellung 7 Tage gültig. Fehlende, fremde, noch nicht erfolgreiche, nicht als Entwurf erstellte oder abgelaufene Aufgaben führen bei Einreichung zu 400, ohne neue Aufgabe oder Abbuchung.

Das finale Video verwendet dieselbe Route wie der Entwurf; die Zuordnung erfolgt automatisch. Ist die Route vorübergehend nicht verfügbar, ergibt die Einreichung 400. Später mit derselben `draft_task_id` erneut versuchen.

Ein gültiger Entwurf kann mehrfach finale Videos erzeugen. Jede Generierung ist eine eigene, separat abgerechnete Aufgabe.

### Abrechnung im Entwurfsmodus

Entwürfe werden als normale 480p-Videos abgerechnet. Die Eingabedauer von Referenzvideos wird regulär berücksichtigt, insgesamt bis zu 30 Sekunden.

Finale Videos nutzen immer den **1080p-Tarif ohne Videoeingabe**. Referenzvideodauer aus dem Entwurf wird nicht erneut berechnet.

| Dauer des Entwurfs | Reservierung für Entwurf | Reservierung für finales Video |
| - | - | - |
| Explizit 4–30 Sekunden | Angegebene Dauer in 480p, Referenzvideos nach normalen Regeln | Gleiche Dauer in 1080p ohne Videoeingabe |
| `duration: -1` oder `edit` ohne Dauer, auf `-1` gesetzt | Bis zu 30 Sekunden Ausgabe, Referenzvideos nach normalen Regeln | Bis zu 30 Sekunden in 1080p |
| `duration` sonst weggelassen | 5 Sekunden Ausgabe, Referenzvideos nach normalen Regeln | 5 Sekunden in 1080p |

Nach Erfolg erfolgt die Abrechnung nach tatsächlichen Tokens mit Erstattung oder Nachbelastung der Differenz. Fehler führen zur vollständigen Erstattung. Preise und Rabatte richten sich nach dem Modellpreis.

```text theme={null}
Entwurfs-Tokens = (Eingabevideosekunden + Ausgabesekunden) × Breite × Höhe × 24 ÷ 1024
Tokens des finalen Videos = Ausgabesekunden × Breite × Höhe × 24 ÷ 1024
```

Breite und Höhe sind die Ausgabepixelmaße der jeweiligen Aufgabe. Nach Erfolg enthält `data.usage.completion_tokens` die abgerechneten Tokens.

## 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 unsupported `resolution` | Passed unsupported values such as `2k` / `4k` |
| 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)

**code** `integer`

Status code; `200` on success

**data** `array`

Submit response with `status` / `task_id`

**Array item**

**status** `string`

Initially `submitted`

**task\_id** `string`

Task ID for [status polling](https://gccai.heqingsong.uk/de/api-reference/tasks/status.md)

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

After submit, poll with [Get task status](https://gccai.heqingsong.uk/de/api-reference/tasks/status.md). 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 / 1080p** |
| 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 |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.