Генерация видео (API)
Асинхронный /v1/videos — создание, опрос статуса, скачивание mp4. Оплата в рублях по факту, хранение результата 72 часа.
/v1/videos — асинхронный API генерации видео. Вы создаёте задачу, опрашиваете её статус и, когда видео готово, скачиваете mp4. Тот же Bearer-ключ sk-gw-..., что и для остального API. Цена — в рублях, списывается по факту.
Идентификатор задачи (id), polling_url и unsigned_urls указывают только на эндпоинты Hubris (https://api.hubris.pw).
Scope ключа
POST /v1/videosтребует scopevideos:write(денежно-мутирующий — создаёт задачу и резервирует средства).GET /v1/videos/{id}иGET /v1/videos/{id}/contentтребуютvideos:read.GET /v1/videos/modelsправ не требует: это тот же открытый каталог, чтоGET /v1/models?output_modalities=video.
Обычный ключ sk-gw- покрывает всё. Ключ только для чтения смотрит статус и скачивает готовые ролики, но не создаёт новые. Приложению, подключённому через OAuth, оба права выдаёт пользователь на экране согласия; подключённым до 14 октября 2026 года нужно подключиться заново.
Жизненный цикл
POST /v1/videos → 202 { id, status: "pending", polling_url }
│
▼ (опрос раз в несколько секунд)
GET /v1/videos/{id} → 200 { status: "pending" | "in_progress" | "completed" | "failed" }
│
▼ (когда status = "completed")
GET /v1/videos/{id}/content?index=0 → video/mp4 (сырые байты)Статусы наружу: pending, in_progress, completed, failed. Промежуточные внутренние состояния схлопываются в in_progress.
Создание — POST /v1/videos
https://api.hubris.pw/v1/videosОбязательны model (из каталога видео-моделей, см. GET /v1/videos/models) и prompt. Ответ — HTTP 202.
curl -s https://api.hubris.pw/v1/videos \-H "Authorization: Bearer $HUBRIS_API_KEY" \-H "Content-Type: application/json" \-d '{ "model": "kwaivgi/kling-v3.0-pro", "prompt": "Кот в скафандре катается на скейтборде по Луне", "duration": 8, "resolution": "720p", "aspect_ratio": "16:9", "generate_audio": true}'import os, requestsr = requests.post( "https://api.hubris.pw/v1/videos", headers={"Authorization": f"Bearer {os.environ['HUBRIS_API_KEY']}"}, json={ "model": "kwaivgi/kling-v3.0-pro", "prompt": "Кот в скафандре катается на скейтборде по Луне", "duration": 8, "resolution": "720p", "aspect_ratio": "16:9", "generate_audio": True, },)job = r.json()print(job["id"], job["status"])const r = await fetch("https://api.hubris.pw/v1/videos", {method: "POST",headers: { Authorization: `Bearer ${process.env.HUBRIS_API_KEY}`, "Content-Type": "application/json",},body: JSON.stringify({ model: "kwaivgi/kling-v3.0-pro", prompt: "Кот в скафандре катается на скейтборде по Луне", duration: 8, resolution: "720p", aspect_ratio: "16:9", generate_audio: true,}),});const job = await r.json();console.log(job.id, job.status);{
"id": "b0f2c1a4-3e9d-4b7a-9c2e-8f1a6d5c4b3a",
"status": "pending",
"polling_url": "https://api.hubris.pw/v1/videos/b0f2c1a4-3e9d-4b7a-9c2e-8f1a6d5c4b3a",
"created": 1752192000
}Опциональные поля: size ("1280x720", шорткат к resolution+aspect_ratio; при явном resolution и/или aspect_ratio они перекрывают size), seed (int, только если модель поддерживает детерминизм), frame_images[] — до двух опорных кадров, чтобы оживить картинку первым и/или последним кадром:
{
"frame_images": [
{
"type": "image_url",
"image_url": { "url": "https://example.com/frame.png" },
"frame_type": "first_frame"
}
]
}image_url.url принимает https://-ссылку (без логина/пароля в URL) или data:image/png;base64,... / data:image/jpeg;base64,... (до 10 МБ в base64). Поле callback_url в v1 не поддерживается — запрос с ним вернёт 400 invalid_request (не молчаливый дроп). Про input_references (исходное видео или фото для аватара) — отдельный раздел ниже.
Идемпотентность
Передайте заголовок Idempotency-Key: <ваш-ключ> — повторный POST с тем же ключом от того же аккаунта вернёт уже созданную задачу, не создавая вторую и не резервируя средства повторно. Используйте при сетевых ретраях создания.
Исходное видео и фото — input_references
Часть видео-моделей берёт на вход не только текстовый промпт, а готовое видео или фото: апскейл и монтаж существующего ролика, оживление фотографии в аватар. Передавайте исходник в input_references — массиве из 1–4 элементов:
{
"input_references": [
{ "type": "video_url", "video_url": { "url": "https://example.com/source.mp4" } }
]
}или для фото:
{
"input_references": [
{ "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } }
]
}Требования к video_url.url: ссылка только https:// с именем домена (IP-адрес не принимается), прямая ссылка на файл без перенаправлений, сервер отвечает заголовком Content-Type: video/*, размер файла — не больше 50 МБ, формат mp4, mov или webm. Одно видео на запрос.
Какой вход обязателен и какие пределы у исходника — у каждой модели свои:
| Модель | Обязателен | Длина и файл | Кадр |
|---|---|---|---|
black-forest-labs/flux-video-upscale | видео | 20 с | 2560×1440 |
black-forest-labs/flux-video-edit | видео | от 1 до 15 с | сторона кадра от 160 px |
runway/aleph-2 | видео | от 2 до 30 с, не больше 30 кадров/с, файл до 32 МБ | — |
heygen/avatar-iv | фото | 180 с речи (длина берётся из текста) | — |
У моделей без оси длительности (например, heygen/avatar-iv) поле duration передавать не нужно — модель его не принимает, запрос с duration вернёт 400 invalid_request: длина ролика определяется исходником (black-forest-labs/flux-video-edit, runway/aleph-2) либо длиной озвучиваемого текста (heygen/avatar-iv). Длина ролика аватара оценивается по тексту с запасом: цифры, сокращения и знаки вроде % считаются длиннее букв, паузы <break time="…"/> и медленная речь (voice_settings.speed) прибавляются. Если оценка выходит дольше 180 секунд, запрос вернёт 400 invalid_request до списания.
Какой вход требует конкретная модель и её пределы — также в ответе GET /v1/videos/models (required_input).
Настройки upscale_factor и creativity
У моделей с осью апскейла есть upscale_factor — во сколько раз увеличить разрешение: от 1.5 до 3 с шагом 0.5, по умолчанию 2. Другое значение вернёт 400 invalid_request. У black-forest-labs/flux-video-upscale есть ещё creativity — от 0 (точно, без додумывания деталей) до 1 (творчески: модель дорисовывает больше деталей); диапазон модели — в поле creativity того же ответа.
Настройки конкретного поставщика — provider.options
Параметры своего поставщика сейчас принимают четыре модели: heygen/avatar-iv (голос voice_id, voice_settings, движения, фон, субтитры и др.), black-forest-labs/flux-video-upscale и black-forest-labs/flux-video-edit (safety_tolerance), runway/aleph-2 (keyframes). Точный список ключей — в поле allowed_passthrough_parameters ответа GET /v1/videos/models; у остальных моделей поле пустое, и provider.options в запросе вернёт 400. Передавайте ключи через provider.options.<поставщик>.parameters, где <поставщик> — часть id модели до / (например, black-forest-labs, runway, heygen). Разрешён только один поставщик за запрос — два ключа в options вернут 400.
Например, у heygen/avatar-iv обязателен голос — без него запрос отклонит сам поставщик:
{
"model": "heygen/avatar-iv",
"prompt": "Здравствуйте! Это видео записано с помощью Hubris.",
"input_references": [{ "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } }],
"provider": {
"options": {
"heygen": {
"parameters": { "voice_id": "80441555167a467e967ab9487d844a30" }
}
}
}
}Сколько удерживается
Перед запуском задачи Hubris резервирует на балансе не меньше наибольшей возможной стоимости ролика: по длительности и разрешению, которые вы запросили, а для исходного видео или фото — по его фактическому замеру (длина, разрешение) и, для аватара, по длине озвучиваемого текста. Когда генерация завершается, списывается уже фактическая стоимость (usage.cost), а разница между удержанием и фактом возвращается на баланс. При сбое генерации (status: "failed") удержание возвращается целиком.
У runway/aleph-2 оплачивается не меньше 5 секунд ролика: исходник короче 5 секунд стоит как 5-секундный. Минимум за ролик — в ответе GET /v1/videos/models (pricing.per_unit).
Опрос статуса — GET /v1/videos/{id}
https://api.hubris.pw/v1/videos/{id}curl -s https://api.hubris.pw/v1/videos/b0f2c1a4-3e9d-4b7a-9c2e-8f1a6d5c4b3a \
-H "Authorization: Bearer $HUBRIS_API_KEY"{
"id": "b0f2c1a4-3e9d-4b7a-9c2e-8f1a6d5c4b3a",
"status": "completed",
"created": 1752192000,
"unsigned_urls": [
"https://api.hubris.pw/v1/videos/b0f2c1a4-3e9d-4b7a-9c2e-8f1a6d5c4b3a/content?index=0"
],
"usage": { "cost": 4200 }
}usage.cost— стоимость в копейках (integer), появляется только приstatus: "completed".4200= 42,00 ₽. Это итоговая сумма, списанная с баланса — не оценка.usage.estimated_cost— пока ролик в работе: сумма в копейках, удержанная с баланса перед запуском. Это верхняя оценка; после готовности вместо неё приходитusage.cost, а разница возвращается на баланс.unsigned_urlsпоявляются только приstatus: "completed"и, несмотря на имя (унаследовано от формата апстрима), требуют тот же Bearer-ключ — это эндпоинты Hubris, не публичные подписанные ссылки.- При
status: "failed"приходит полеerrorс нейтральным текстом.
Пример полного цикла (bash)
ID=$(curl -s https://api.hubris.pw/v1/videos \
-H "Authorization: Bearer $HUBRIS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"kwaivgi/kling-v3.0-pro","prompt":"Закат над океаном, дрон"}' \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])')
while true; do
STATUS=$(curl -s "https://api.hubris.pw/v1/videos/$ID" \
-H "Authorization: Bearer $HUBRIS_API_KEY" \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["status"])')
echo "status=$STATUS"
[ "$STATUS" = "completed" ] && break
[ "$STATUS" = "failed" ] && { echo "generation failed"; exit 1; }
sleep 5
done
curl -s "https://api.hubris.pw/v1/videos/$ID/content?index=0" \
-H "Authorization: Bearer $HUBRIS_API_KEY" -o result.mp4Опрашивайте не чаще раза в несколько секунд — частые повторы одного и того же GET не ускорят генерацию.
Пример с исходным видео (black-forest-labs/flux-video-edit)
ID=$(curl -s https://api.hubris.pw/v1/videos \
-H "Authorization: Bearer $HUBRIS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "black-forest-labs/flux-video-edit",
"prompt": "Сделай фон зимним, идёт снег",
"input_references": [
{ "type": "video_url", "video_url": { "url": "https://example.com/source.mp4" } }
]
}' \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])')
while true; do
STATUS=$(curl -s "https://api.hubris.pw/v1/videos/$ID" \
-H "Authorization: Bearer $HUBRIS_API_KEY" \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["status"])')
echo "status=$STATUS"
[ "$STATUS" = "completed" ] && break
[ "$STATUS" = "failed" ] && { echo "generation failed"; exit 1; }
sleep 5
done
curl -s "https://api.hubris.pw/v1/videos/$ID/content?index=0" \
-H "Authorization: Bearer $HUBRIS_API_KEY" -o result.mp4Исходник source.mp4 должен быть не длиннее 15 секунд и не меньше 160 px по стороне кадра (пределы flux-video-edit, см. «Исходное видео и фото»); duration в теле запроса не передаётся — длина результата берётся из исходника.
Скачивание — GET /v1/videos/{id}/content
https://api.hubris.pw/v1/videos/{id}/content?index=0Отдаёт сырые байты video/mp4. Требует Bearer-ключ и доступен только владельцу задачи. В v1 доступен единственный клип — index=0 (параметр можно не передавать, по умолчанию тот же); index больше нуля вернёт 400 index_out_of_range. Запрос до готовности видео или после истечения хранения — 404 video_not_ready.
Хранение результата — 72 часа
Готовый ролик хранится у Hubris 72 часа с момента создания задачи, затем удаляется без возможности восстановления. Скачайте и сохраните файл у себя в течение этого окна. Запрос /content после удаления вернёт 404 video_not_ready — так же, как если бы видео ещё не было готово (Hubris не палит разницу между «протухло» и «не готово»).
Копия исходного видео из input_references хранится у Hubris до 72 часов и удаляется вместе с результатом задачи — а также при удалении аккаунта. Фото из input_references не копируется: провайдер модели получает его по вашей ссылке.
Каталог видео-моделей — GET /v1/videos/models
https://api.hubris.pw/v1/videos/modelscurl -s https://api.hubris.pw/v1/videos/models \
-H "Authorization: Bearer $HUBRIS_API_KEY"Возвращает { "object": "list", "data": [...] } — активные видео-модели с поддерживаемыми разрешениями, соотношениями сторон, длительностями, доступностью звука/seed/опорных кадров и ценами в рублях за единицу (секунда или видео). Видео-модели скрыты из обычного GET /v1/models — получить их там тоже можно, но отдельным запросом: GET /v1/models?output_modalities=video.
Коды ошибок
| HTTP | code | type | Когда |
|---|---|---|---|
| 400 | invalid_request | invalid_request_error | Кривой запрос, неподдерживаемая ось каталога, callback_url, кривой или лишний input_references, исходник вне пределов модели, текст аватара дольше 180 секунд речи, неразрешённая настройка provider.options (включая два поставщика сразу) или отказ модели принять вход — её текст приходит в message |
| 400 | index_out_of_range | invalid_request_error | index в /content вне диапазона (v1 — только index=0) |
| 402 | insufficient_balance | invalid_request_error | Недостаточно средств для резерва под задачу — в том числе перед скачиванием исходного видео, если баланс ниже минимума |
| 404 | model_not_found | invalid_request_error | Модель с таким id не видео-модель или неактивна |
| 404 | not_found | invalid_request_error | Задачи с таким id не существует или она принадлежит другому аккаунту |
| 404 | video_not_ready | invalid_request_error | /content до completed или после 72-часового TTL |
| 413 | request_too_large | invalid_request_error | Тело запроса (обычно — frame_images в base64) превышает лимит |
| 429 | too_many_active_jobs | rate_limit_error | Достигнут лимит одновременных видео-задач аккаунта, либо уже готовится другое исходное видео этого аккаунта, либо сервер сейчас занят подготовкой — повторите запрос через минуту |
| 429 | daily_limit_exceeded, key_limit_exceeded | rate_limit_error | Удержание под ролик не помещается в остаток лимита расхода ключа или приложения, подключённого через OAuth; в тексте — удержание и остаток. Если ролик дороже всего лимита, Retry-After нет: после сброса он тоже не пройдёт. Повтор с тем же Idempotency-Key отдаёт уже созданную задачу и в лимит не упирается |
| 502 / 504 | upstream_error | api_error | Провайдер модели не принял задачу (502, включая отказ по собственным ключам Hubris — HTTP 401/403 от провайдера) или не ответил вовремя (504) |
| 503 | exchange_rate_unavailable | api_error | Курс ЦБ РФ временно недоступен — нельзя посчитать резерв под задачу |
Полный формат ошибок и стратегия retry — на странице Ошибки.
Приватность и хранение данных
Видео-генерация выполняется на инфраструктуре внешних провайдеров моделей и не покрывается режимом Zero Data Retention: провайдеры временно хранят входные данные (промпт, кадры frame_images) и результат на своей стороне на время генерации. У Hubris готовый ролик хранится 72 часа, затем удаляется.
Privacy Mode на видео не распространяется. Автоматическое маскирование персональных данных (Privacy Mode) работает только для текстовых запросов Chat Completions; промпты и кадры видео уходят провайдеру как есть — не передавайте туда персональные данные третьих лиц.
Трансграничная передача (152-ФЗ). Если вы вызываете /v1/videos от имени своих пользователей, вы отвечаете за наличие их согласия на обработку и трансграничную передачу входов и выходов (промпты, кадры, результат) за пределы РФ, а также на 72-часовое хранение результата у Hubris. Условия использования API фиксируют это в оферте.
Hubris не логирует содержимое ваших запросов: prompt, байты видео и frame_images не попадают в логи — только метаданные (модель, стоимость, статус, латентность).
Что дальше
- Ошибки — таблица всех кодов и retry-стратегий.
- Баланс и оплата — как считается
usage.cost. - Privacy Mode — что покрывает маскирование, а что нет.
- Студия видео в личном кабинете — тот же API под UI, без curl.
Обновлено: