Перейти к содержимому
EN

Видео

Видео генерируется дольше HTTP-таймаута, поэтому работает по схеме «задача → опрос → скачивание».

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)

Ответ приходит сразу со статусом 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.

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', ''))
curl -L https://api.mixen.ai/v1/videos/video_.../content \
-H "Authorization: Bearer $MIXEN_API_KEY" \
-o result.mp4

Эндпоинт отдаёт готовый mp4.

Весь цикл целиком — одним скриптом на OpenAI SDK (тот же путь, что в сниппетах каталога /models):

import time
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",
)
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")
{
"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=0

upscale_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}
}

Рублёвая сумма считается по живому курсу — берите котировку непосредственно перед запуском, а не кешируйте.