# Wan 3.0

> Видеомодель Alibaba до 30 секунд: по описанию, кадрам и образцам (фото, видео, звук), со звуковой дорожкой.

Wan 3.0 — видеомодель Alibaba. Ролики от 2 до 30 секунд в 480p, 720p и 1080p: по описанию, по первому и последнему кадру или по образцам (до 10 фото, 5 видео и 5 звуковых дорожек). Можно выключить звук в ролике — цена та же.

Длину можно выбрать «Авто» — модель решит сама: списывается максимум, лишнее возвращается после готовности. С вашим видео сумма секунд исходника и результата — не больше 30.

Одна модель `wan-3.0` · ~3 мин. Что получится, задаётся полями `input` (описание, кадры, образцы, `video_mode`, длительность) — отдельных эндпоинтов «оживить / изменить» нет.

## Возможности

- `prompt` — до **30000** символов
- `duration: "auto"` / `-1` — длину выбирает модель (на сайте: **auto (-1)**)
- `watermark` — метка «AI generated»
- `generate_audio` — звук в ролике
- `nsfw_check` — проверка до списания

## Цена

| Разрешение | Цена за секунду | С вашим видео, за секунду |
|---|---|---|
| 480p | $0.054 | $0.054 |
| 720p | $0.108 | $0.108 |
| 1080p | $0.215 | $0.215 |

- Цена = цена секунды × длина ролика, округляется вверх до $0.001. Списывается при создании задачи и полностью возвращается, если задача не удалась.
- С вашим видео считаются секунды исходного ролика плюс секунды результата.
- На сайте — галочка **auto (-1)** рядом с длительностью (не пункт в списке). В API: `duration: "auto"` или `-1` — длину выберет модель, до 30 сек. При создании списывается цена за 30 сек (плюс секунды вашего видео, если оно есть). Когда видео готово, цена пересчитывается по реальной длине, разница сразу возвращается на баланс (в истории — «Возврат за неиспользованные секунды видео»). Итоговая цена — `costUsd` в recordInfo, длина — `duration` в `resultJson`. При `video_mode: "edit"` длина результата — как у вашего ролика.

## Лимиты медиа

| Тип | Ограничения |
|---|---|
| Фото | до **10**; JPG / PNG / WebP; загрузка через [files/upload](https://docs.mixmedia.tech/endpoints/upload) — до **30 МБ** на файл |
| Видео | до **5**; вместе ≤ **15 с** (каждый клип 2…15 с); MP4 / MOV → на сервере в MP4; загрузка ≤ **200 МБ** |
| Аудио | до **5**; вместе ≤ **15 с**; MP3 / WAV; загрузка ≤ **200 МБ**; можно только звук без фото/видео |

Фото — публичные https или upload; видео и звук — только через files/upload.


## Параметры (поле `input`)

| Параметр | Описание | Значения (по умолчанию — жирным) |
|---|---|---|
| `prompt` | Что происходит в ролике: сцена, движение, камера, свет, стиль | текст, до 20000 символов |
| `first_frame_url` | Первый кадр: ссылка на фото (https), с него начнётся видео | `"https://…"` |
| `last_frame_url` | Последний кадр (по желанию, только вместе с первым) | `"https://…"` |
| `image_urls` | Фото-образцы (https) | `["https://…"]`, до 10 |
| `video_urls` | Ваши видео (MP4): сначала загрузите их через files/upload и передайте полученные ссылки | `["https://…"]`, до 5 |
| `audio_urls` | Звук (MP3): сначала загрузите через files/upload | `["https://…"]`, до 5 |
| `resolution` | Разрешение видео | `480p` (стандартное) · **`720p`** (высокое (HD)) · `1080p` (Full HD) |
| `aspect_ratio` | Соотношение сторон | **`adaptive`** (под загруженное) · `16:9` (горизонтальное) · `9:16` (вертикальное) · `1:1` (квадратное) · `4:3` (традиционное) · `3:4` (вертикальное традиционное) |
| `duration` | Длительность видео в секундах | от 2 до 30 сек, либо `auto` / `-1` (длину выберет модель) (по умолчанию **`5`**) |
| `generate_audio` | Видео со звуком | `true` `false` (по умолчанию `true`) |
| `watermark` | Водяной знак «AI generated» на видео | `true` `false` (по умолчанию `false`) |
| `nsfw_check` | Проверка содержимого: сначала проверить описание и фото на запрещённое; если нельзя — задача не создаётся и ничего не списывается | `true` `false` (по умолчанию `false`) |
| `seed` | Номер варианта: то же число с тем же описанием даёт похожий результат; пусто или -1 — случайный | -1…4294967295 |
| `trim_video_to` | Обрезать каждое ваше видео до N секунд: остаётся начало ролика. Видео короче — без изменений | целое число от 2 до 15 |
| `trim_to_duration` | Обрезать каждое ваше видео до выбранной длины `duration`: остаётся начало ролика. При `duration: "auto"` или `video_mode: "edit"` — до лимита модели | `true` `false` (по умолчанию `false`) |
| `auto_trim` | Если видео вместе длиннее 15 сек — обрезать их самим, а не выдавать ошибку (сначала самое длинное) | `true` `false` (по умолчанию `false`) |

## Примеры

### Только описание

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"wan-3.0","input":{"prompt":"Дрон пролетает над осенним лесом на рассвете, туман, кинематографично","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": "wan-3.0",
      "input": {
        "prompt": "Дрон пролетает над осенним лесом на рассвете, туман, кинематографично",
        "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": "wan-3.0",
    "input": {
      "prompt": "Дрон пролетает над осенним лесом на рассвете, туман, кинематографично",
      "resolution": "720p",
      "aspect_ratio": "adaptive",
      "duration": "5"
    }
  }),
});
console.log(await r.json()); // { code: 200, data: { taskId: "..." } }
```

### С первым кадром

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"wan-3.0","input":{"prompt":"Герой медленно поворачивается к камере, ветер развевает волосы","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": "wan-3.0",
      "input": {
        "prompt": "Герой медленно поворачивается к камере, ветер развевает волосы",
        "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": "wan-3.0",
    "input": {
      "prompt": "Герой медленно поворачивается к камере, ветер развевает волосы",
      "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: "..." } }
```

### С образцами

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"wan-3.0","input":{"prompt":"Дрон пролетает над осенним лесом на рассвете, туман, кинематографично","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": "wan-3.0",
      "input": {
        "prompt": "Дрон пролетает над осенним лесом на рассвете, туман, кинематографично",
        "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": "wan-3.0",
    "input": {
      "prompt": "Дрон пролетает над осенним лесом на рассвете, туман, кинематографично",
      "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: "..." } }
```

### С вашим видео

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"wan-3.0","input":{"prompt":"Сделай ролик в стиле аниме, остальное не меняй","video_urls":["https://mixmedia.tech/files/<id>.mp4"],"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": "wan-3.0",
      "input": {
        "prompt": "Сделай ролик в стиле аниме, остальное не меняй",
        "video_urls": [
          "https://mixmedia.tech/files/<id>.mp4"
        ],
        "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": "wan-3.0",
    "input": {
      "prompt": "Сделай ролик в стиле аниме, остальное не меняй",
      "video_urls": [
        "https://mixmedia.tech/files/<id>.mp4"
      ],
      "resolution": "720p",
      "duration": "5"
    }
  }),
});
console.log(await r.json()); // { code: 200, data: { taskId: "..." } }
```

## Обрезать своё видео

Ваши видео вместе — до 15 сек. Слишком длинный ролик не нужно резать заранее:

- `trim_to_duration: true` — обрезать каждое видео до выбранной длины `duration`;
- `trim_video_to: N` — оставить N секунд от начала;
- `auto_trim: true` — если видео вместе длиннее лимита, обрезать их ровно до лимита, а не выдавать ошибку.

Обрезка бесплатная, а цена считается уже по обрезанным секундам — меньше секунд, дешевле.

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"wan-3.0","input":{"prompt":"Сделай ролик в стиле аниме, остальное не меняй","video_urls":["https://mixmedia.tech/files/<id>.mp4"],"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": "wan-3.0",
      "input": {
        "prompt": "Сделай ролик в стиле аниме, остальное не меняй",
        "video_urls": [
          "https://mixmedia.tech/files/<id>.mp4"
        ],
        "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": "wan-3.0",
    "input": {
      "prompt": "Сделай ролик в стиле аниме, остальное не меняй",
      "video_urls": [
        "https://mixmedia.tech/files/<id>.mp4"
      ],
      "resolution": "720p",
      "duration": "5",
      "trim_to_duration": true,
      "auto_trim": true
    }
  }),
});
console.log(await r.json()); // { code: 200, data: { taskId: "..." } }
```

Один файл при загрузке — до 30 сек. Без этих параметров слишком длинные видео дают ошибку `400` с подсказкой.

## Создание и результат

1. `POST /api/v1/jobs/createTask` с `model` и `input`.
2. Поллите `GET /api/v1/jobs/recordInfo?taskId=…` каждые 3–5 с (или [callback](https://docs.mixmedia.tech/callbacks)).

### Успешный createTask (HTTP 200)

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

### Готовая задача (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
    }
  }
}
```

Алиасы: `task_id` = `taskId`; `status`: `pending` / `processing` / `completed` / `failed` рядом с `state`. Ссылка на ролик — в `resultJson`. `size` — синоним `aspect_ratio` в `input`.


Фото можно передать любыми публичными https-ссылками или загрузить через [files/upload](https://docs.mixmedia.tech/endpoints/upload). Видео и звук — только через files/upload.

## Обработка ошибок

Ответ API — JSON `{"code", "msg", "data"}`; при ошибке есть строковое поле `error` (тип). **На `/api/v1/*` HTTP-статус совпадает с `code` в теле.**

### Примеры тел ответа

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

### Коды при createTask / upload

| code | error | Что значит | Что делать |
|---|---|---|---|
| 400 | `invalid_request_error` | Неверный параметр или тело | Смотрите `msg` |
| 400 | `unsupported_model` | Нет такой модели | [Список моделей](https://docs.mixmedia.tech/models) |
| 400 | `nsfw_content_detected` | Отклонено проверкой содержимого (`nsfw_check`) | Измените описание или файлы |
| 401 | `authentication_error` | Нет ключа или он неверный | Проверьте ключ |
| 402 | `payment_required` | Не хватает денег | Пополните баланс |
| 403 | `permission_error` | Нет доступа | Напишите в поддержку |
| 429 | `rate_limit_error` | Слишком много запросов | Подождите `Retry-After` |
| 505 | `model_disabled` | Модель временно отключена | Выберите другую |
| 500 | `server_error` | Сбой у нас | Повторите позже |

Примеры `msg`: `Authentication failed. Please check your API key` (401); `Insufficient balance. Please top up and try again…` (402).

### Ошибки задачи (recordInfo)

Если задача уже создана и завершилась неудачей: `state: "fail"` (алиас `status: "failed"`), `failCode` / `failMsg`, объект `error: { code, message, type }`. Деньги возвращаются (`refunded: true`).

| failCode | error.type | Что значит |
|---|---|---|
| 400 | `nsfw_content_detected` / `invalid_request_error` | Модерация / правила безопасности |
| 422 | `invalid_request_error` | Модель не приняла параметры (длина, режим, разрешение, файл) |
| 501 | `server_error` | Модель не смогла сгенерировать (`failMsg`) |
| 504 | `timeout_error` | Слишком долго — повторите |
| 500 / 503 | `server_error` | Сервис временно недоступен |

### Типичные отказы для этой модели

- `duration` вне диапазона, или `auto` / `-1` если авто-длина не поддерживается (только Seedance 2.5)
- `resolution` / `aspect_ratio` (`size`) / `video_mode` не из списка выше
- `video_mode` = `edit` или `extend` без `video_urls`; для `edit` — ролик короче 4 с
- Слишком длинные образцы видео/звука без `auto_trim` / `trim_*` → `400`
- `nsfw_check: true` — при отказе **сразу HTTP 400** (`nsfw_content_detected`), задача не создаётся и деньги не списываются

### Лимиты запросов

| Что | Лимит |
|---|---|
| createTask и upload | 20 за 10 с на ключ |
| recordInfo / list / balance | 100 за 10 с на ключ |

Сверх лимита — HTTP 429 и `Retry-After`.
