Видео
Видео генерируется дольше HTTP-таймаута, поэтому работает по схеме «задача → опрос → скачивание».
1. Создать задачу
Заголовок раздела «1. Создать задачу»from openai import OpenAI
client = OpenAI(base_url="https://api.mixen.ai/v1", api_key=API_KEY)video = client.videos.create( model="seedance-2.5", prompt="Дрон облетает маяк на скалистом берегу, рассвет", seconds="5", size="1280x720",)print(video.id, video.status)import OpenAI from 'openai'
const client = new OpenAI({ baseURL: 'https://api.mixen.ai/v1', apiKey: process.env.MIXEN_API_KEY })const video = await client.videos.create({ model: 'seedance-2.5', prompt: 'Дрон облетает маяк на скалистом берегу, рассвет', seconds: '5', size: '1280x720',})console.log(video.id, video.status)curl https://api.mixen.ai/v1/videos \ -H "Authorization: Bearer $MIXEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "seedance-2.5", "prompt": "Дрон облетает маяк на скалистом берегу, рассвет", "seconds": "5", "size": "1280x720" }'Ответ приходит сразу со статусом queued:
{"id": "video_...", "object": "video", "model": "bytedance/seedance-2.5", "status": "queued"}| Поле | Описание |
|---|---|
model |
Модель из каталога (veo-3.1-fast по умолчанию). Псевдонимы sora-2 / sora-2-pro сняты OpenAI 24.09.2026 и перенаправляются на veo-3.1-fast с warnings в ответе — см. устаревшие модели |
prompt |
Описание сцены, обязательное. Потолок длины — поле prompt_max_chars в каталоге: вендоры режут по-разному (Kling 2500, Grok Imagine 4096, MiniMax H3 7000 символов), у остальных моделей 30 000 |
seconds |
Длительность; допустимые значения — поле seconds в каталоге |
size |
Размер WxH; допустимые — поле sizes в каталоге |
input_reference |
Image-to-video: {"image_url": "data:image/png;base64,..."} |
input_references |
Reference-to-video: от 2 картинок, потолок — поле max_references в каталоге (у Seedance 2.5 это 30, у Grok Imagine 7); только у моделей с references: true |
seed |
Сид повторяемости: целое 0…2147483647, у моделей с seed: true в каталоге. Тот же промпт с тем же сидом даёт тот же ролик; без поля апстрим берёт случайный |
Для input_reference принимаются только data:-URL — внешние ссылки не подойдут, картинку нужно закодировать в base64.
2. Опросить статус
Заголовок раздела «2. Опросить статус»curl https://api.mixen.ai/v1/videos/video_... \ -H "Authorization: Bearer $MIXEN_API_KEY"Статусы: queued → in_progress → completed либо failed. Разумный интервал опроса — 5–10 секунд; чаще смысла нет, а лимит запросов тратится.
Задача, зависшая дольше 20 минут, помечается failed при следующем опросе статуса — автопочинка срабатывает на GET /videos/{id}, а не фоновым процессом. Деньги за такую задачу не списываются.
Без опроса: webhook, ожидание, идемпотентность
Заголовок раздела «Без опроса: webhook, ожидание, идемпотентность»Три необязательных поля тела POST /v1/videos — общие для видео и музыки:
| Поле | Что делает |
|---|---|
webhook_url |
Публичный https-адрес. По статусу completed или failed мы шлём туда POST с тем же объектом задачи, что отдаёт GET. Приватные адреса и http:// отклоняются 400 invalid_webhook_url ещё до создания задачи |
wait |
true — запрос держится, пока задача не завершится, и возвращает финальный объект (ждём до wait_timeout, по умолчанию 60 с, максимум 300). По таймауту — объект как есть (in_progress), без ошибки |
idempotency_key |
До 128 символов. Повтор запроса с тем же ключом возвращает уже созданную задачу, а не новую (и не списывает второй раз); в ответе заголовок X-Mixen-Idempotent-Replay: true. Ключ уникален в пределах вашего аккаунта |
Заголовки webhook-запроса:
| Заголовок | Значение |
|---|---|
X-Mixen-Event |
video.completed или video.failed |
X-Mixen-Delivery |
id задачи |
X-Mixen-Signature |
t=<unix>,v1=<hex> — HMAC-SHA256 от строки "<t>.<сырое тело>"; секрет — sha256-hex вашего API-ключа (сам ключ мы не храним, его хэш вы считаете локально) |
Ваш эндпоинт должен ответить 2xx; иначе доставка повторится через 5, 20, 60 и 180 секунд. Гарантия — «хотя бы один раз»: обрабатывайте повторы по X-Mixen-Delivery идемпотентно. Проверка подписи:
import hashlib, hmac
def verify(raw_body: bytes, header: str, api_key: str) -> bool: secret = hashlib.sha256(api_key.encode()).hexdigest() parts = dict(kv.split('=', 1) for kv in header.split(',')) expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, parts.get('v1', ''))3. Скачать результат
Заголовок раздела «3. Скачать результат»curl -L https://api.mixen.ai/v1/videos/video_.../content \ -H "Authorization: Bearer $MIXEN_API_KEY" \ -o result.mp4Эндпоинт отдаёт готовый mp4.
Весь цикл целиком — одним скриптом на OpenAI SDK (тот же путь, что в сниппетах каталога /models):
import timefrom openai import OpenAI
client = OpenAI(base_url="https://api.mixen.ai/v1", api_key=API_KEY)video = client.videos.create( model="seedance-2.5", prompt="Дрон облетает маяк на скалистом берегу, рассвет", seconds="5", size="1280x720",)while video.status not in ("completed", "failed"): time.sleep(5) video = client.videos.retrieve(video.id)client.videos.download_content(video.id).write_to_file("video.mp4")Image-to-video
Заголовок раздела «Image-to-video»{ "model": "happyhorse-1.1", "prompt": "Камера медленно наезжает, волосы шевелит ветер", "input_reference": {"image_url": "data:image/jpeg;base64,/9j/4AAQ..."}}Важная особенность: модели этого класса наследуют соотношение сторон исходной картинки и игнорируют явный size. Хотите вертикальное видео — подавайте вертикальный образец.
Апскейл
Заголовок раздела «Апскейл»POST /v1/videos/upscale принимает multipart/form-data с готовым файлом:
curl https://api.mixen.ai/v1/videos/upscale \ -H "Authorization: Bearer $MIXEN_API_KEY" \ -F video=@clip.mp4 \ -F upscale_factor=2 \ -F creativity=0upscale_factor по умолчанию 1.5, creativity — 0: на нуле апскейлер сохраняет кадр как есть, выше — дорисовывает детали и может изменить лица. Входное видео — до 64 MB.
Апскейл — тоже асинхронная задача: ответ придёт со status: queued, дальше опрос и скачивание теми же эндпоинтами /videos/{id}.
Видео тарифицируется по секундам: per_second_rub из каталога умножается на фактическую длительность. Разрешение влияет на цену — 720p дороже 480p у одной и той же модели, поэтому сверяйтесь с каталогом перед тем, как ставить генерацию в цикл.
Цена до запуска
Заголовок раздела «Цена до запуска»POST /v1/videos/price принимает то же тело, что и POST /v1/videos, и возвращает сумму, не запуская генерацию и ничего не списывая. Считается той же формулой, что и фактическое списание (разрешение, длительность, референсы), поэтому котировка совпадает с чеком; валидация тоже общая — невалидные параметры дадут те же 400/404, что и запуск.
{ "object": "price", "model": "veo-3.1-fast", "model_id": "google/veo-3.1-fast", "price": {"rub": 107.34, "usd": 1.2}, "per_unit": {"rub": 107.34, "unit": "video"}, "quantity": 1, "params": {"seconds": 8, "size": "1280x720", "aspect_ratio": "16:9", "resolution": "720p", "input_reference": false, "input_references": 0}}Рублёвая сумма считается по живому курсу — берите котировку непосредственно перед запуском, а не кешируйте.