Генерация видео

POST/v1/videos
Тот же обработчик отвечает и на: /v1/video/generations · /v1/videos/extend · /v1/videos/storyboard

Ставит генерацию видео в очередь: 202 с id джобы, либо готовый результат сразу при wait: true.

Запрос

modelstringbodyобязательно
имя видео-модели из каталога (GET /cabinet/api/public/media-models)
promptstringbodyобязательно
сцена для генерации; обязателен у каждого видео-движка
durationnumberbody
длительность в секундах; значение по умолчанию и набор задаёт движок модели
secondsnumberbody
алиас duration; если переданы оба, duration побеждает
aspect_ratiostringbody= 16:9
кадр готового ролика. Один и тот же набор у всех видео-движков
16:9 · 9:16 · 1:1
imagestring|arraybody
входная картинка: URL, data-URL, base64 или uuid картинки у провайдера; массив — несколько
videostring|arraybody
входное видео для video-to-video: те же формы, что у image
waitbooleanbody= false
true — держать соединение и вернуть результат, но не дольше 90 с
timeoutnumberbody= 90
сколько секунд ждать; наличие поля включает ожидание даже без wait
ref_video_job_idstringbody
id ТВОЕЙ прежней видео-джобы — провайдеру уходит её uuid как исходник
scenesstring|arraybody
сцены сториборд-движка; шлюз их не валидирует, другим движкам поле не уходит

Ответы

wait: true

{
  "id": "vid_9f1c4a2b7e0d4f5a8c3b6d1e2f0a7b4c",
  "status": "completed",
  "created": 1757500800,
  "duration": 5.0,
  "cost_usd": "0.95",
  "data": [
    { "url": "https://api.teamtoken.store/v1/videos/vid_9f1c4a2b7e0d4f5a8c3b6d1e2f0a7b4c/content" }
  ]
}

Accepted

{
  "id": "vid_9f1c4a2b7e0d4f5a8c3b6d1e2f0a7b4c",
  "status": "processing",
  "model": "seedance-2-fast-720p",
  "created": 1757500800,
  "estimated_cost_usd": "0.95"
}

не проходит нашу проверку до провайдера; причина — в error.message

{ "error": { "message": "...", "type": "...", "code": "PROVIDER_CODE" } }

PROVIDER_CODE — провайдер отказал на сабмите: его 4xx доезжает как есть, джоба failed, резерв снят

{ "error": { "message": "...", "type": "...", "code": "PROVIDER_CODE" } }

ключа нет в заголовке или он не наш

{ "error": { "message": "...", "type": "...", "code": "PROVIDER_CODE" } }

остатка не хватает на худшую цену этого запроса

{ "error": { "message": "...", "type": "...", "code": "PROVIDER_CODE" } }

ref_video_job_id указывает на джобу, которой нет или которая не твоя

{ "error": { "message": "...", "type": "...", "code": "PROVIDER_CODE" } }

генерация провалилась

{
  "error": {
    "message": "No valid characters detected in the image",
    "type": "generation_error",
    "code": "KLING_GENERATION_FAILED"
  },
  "cost_usd": "0"
}

5xx или сеть на сабмите: заявка могла быть принята — unknown_submit, резерв держится

{ "error": { "message": "...", "type": "...", "code": "PROVIDER_CODE" } }

PROVIDER_CODE — при wait: true генерация провалилась: резерв снят, в теле cost_usd: "0"

{ "error": { "message": "...", "type": "...", "code": "PROVIDER_CODE" } }

резерв не удалось зафиксировать: джоба провалена, деньги не заняты

{ "error": { "message": "...", "type": "...", "code": "PROVIDER_CODE" } }

провайдер ответил 200, но без id джобы: джоба провалена, резерв снят

{ "error": { "message": "...", "type": "...", "code": "PROVIDER_CODE" } }
Подробности

Нужен ключ в заголовке Authorization: Bearer или x-api-key.

Генерация асинхронная. По умолчанию ручка отвечает 202 с id джобы и суммой резерва (estimated_cost_usd), а результат забирается через GET /v1/videos/{job_id}. С wait: true (и с любым переданным timeout) шлюз опрашивает провайдера сам — шаг опроса 2 с, потолок ожидания 90 с, и timeout только уменьшает этот бюджет, поднять его выше нельзя. Не успело — придёт 202 с тем же id, джоба остаётся в работе. Опрашивать не обязательно: реконсайлер (проход раз в 150 с) сам досчитает завершившуюся джобу, спишет деньги и сохранит ссылки на результат.

Режимов входа три, и выбирает их не URL, а поля тела: prompt — text-to-video; prompt + image — image-to-video; prompt + video — video-to-video (edit и motion control, где image — персонаж, а video — движение). Что движок сделает со входом, решает он; наша проверка одна — движку, которому входное видео обязательно, запрос без него получает 400 ещё у нас, до провайдера. Список моделей живёт в каталоге (GET /cabinet/api/public/media-models), но обязательность входа и наборы длительностей каталог не отдаёт: это таблица на нашей стороне, наружу видная только текстом ошибки.

/v1/video/generations, /v1/videos/extend и /v1/videos/storyboard — алиасы этого же обработчика: то же тело, те же ответы и ошибки, разницы в поведении между путями нет. Продолжение ролика и сториборд задаются моделью — в каталоге это отдельные движки (*-extend, *-storyboard), — а исходный ролик передаётся полем ref_video_job_id.

Про поля. Длительность и её набор — за движком: seedance — 4–15 с (по умолчанию 5) · kling — 3–15 у 3.0, 3–10 у edit/o1/motion (5) · kling 2.5/2.6 — только 5 или 10 · kling 2.1 — ровно 10 либо ровно 5, смотря какая модель · veo — 4/6/8 (8) · omni-flash — 4/6/8/10, но каждая строка каталога прибита к одному из них · grok — только 6 · extend — 8 у veo, 6 у grok, 4–15 у seedance · сториборд — 6–30 (6). Значение вне набора → 400 с перечислением допустимых (у диапазона — границ). ⚠️ Строка каталога может прибить длительность жёстко (fixed_params) — тогда переданный duration до провайдера не доезжает. ⚠️ Без duration провайдеру уйдёт значение движка по умолчанию, а резерв посчитается по МАКСИМУМУ движка (hold_duration_seconds); списание — по фактической длительности готового ролика. timeout ограничен теми же 90 с, нечисловое значение — те же 90.

Вход принимается под несколькими именами: image — это же images, image_url, image_urls, input_image, init_image, ref_images, а videovideos, video_url, input_video, motion_video, ref_video, ref_videos. URL — картинки и видео одинаково — шлюз скачивает сам (свой User-Agent, свой лимит, запрет внутренних адресов на каждом редиректе); не скачалось или пришло не похожее на медиа тело — ссылка уходит провайдеру как есть. ⚠️ Вход, которого движок не ждёт, шлюз всё равно передаёт провайдеру: каталог сверяется только на обязательность. А параметры вне набора движка (resolution, size, любое незнакомое поле) отбрасываются молча — разрешение и тир задаёт каталог, не запрос.

До провайдера 400 дают: битый JSON, неизвестная или выключенная модель, отсутствующий prompt, duration вне набора движка, обязательный и не переданный вход, вход больше 80 МБ (предел на один декодированный файл). ref_video_job_id проверяется по владельцу: чужой или несуществующий id → 404, чтобы утёкший id ничего не подтверждал. Отказ провайдера на сабмите: его 4xx доезжает как есть — с его кодом и очищенной от имён апстрима причиной, — и это окончательно, джоба failed, резерв снят; 5xx или сеть означают, что заявка МОГЛА быть принята, и возврат наугад был бы двойным зачислением — джоба уходит в unknown_submit и ждёт реконсайлера, который либо её завершит, либо освободит резерв по TTL (у unknown_submit он короткий, 15 минут по умолчанию, у остальных — сутки).

Примеры кода
curl https://api.teamtoken.store/v1/videos \
  -H "Authorization: Bearer sk-…" \
  -H "Content-Type: application/json" \
  -d '{ "model": "seedance-2-fast-720p",
        "prompt": "a corgi surfing a neon wave at sunset",
        "duration": 5,
        "wait": true }'
# оживить картинку / animate an image: prompt + image (base64 и data-URL тоже / base64 and data-URLs too)
curl https://api.teamtoken.store/v1/videos \
  -H "Authorization: Bearer sk-…" \
  -H "Content-Type: application/json" \
  -d '{ "model": "seedance-2-fast-720p",
        "prompt": "slow cinematic push-in, gentle motion",
        "image": "https://example.com/photo.jpg",
        "wait": true }'
# video-to-video: персонаж с картинки повторяет движение из видео /
# the character from the image repeats the motion from the video.
# Поле video ОБЯЗАТЕЛЬНО / the video field is REQUIRED here — без него 400 / a 400 without it.
curl https://api.teamtoken.store/v1/videos \
  -H "Authorization: Bearer sk-…" \
  -H "Content-Type: application/json" \
  -d '{ "model": "kling-3.0-motion-720p",
        "prompt": "the character performs the dance smoothly",
        "image": "https://example.com/character.png",
        "video": "https://example.com/motion.mp4",
        "wait": true }'
import time
import requests

H = {"Authorization": "Bearer sk-…"}

job = requests.post("https://api.teamtoken.store/v1/videos", headers=H, json={
    "model": "seedance-2-fast-720p",
    "prompt": "a corgi surfing a neon wave at sunset",
    "duration": 5,
}).json()                       # 202: {"id": "vid_…", "status": "processing", …}

r = job
while r["status"] not in ("completed", "failed"):
    time.sleep(5)               # статусы до терминального / non-terminal: pending, queued, processing
    r = requests.get("https://api.teamtoken.store/v1/videos/" + job["id"], headers=H).json()

if r["status"] == "failed":
    raise SystemExit(r)         # причина в r["error"] / the reason is in r["error"]

# ссылка из data ведёт на наш /content и требует ключа того же аккаунта /
# the link in data points at our /content and needs a key of the same account
mp4 = requests.get(r["data"][0]["url"], headers=H).content
Запрос
https://api.teamtoken.store/v1

Панель отправляет запрос с этого домена; в своём коде используйте адрес выше.

Ключ никуда не сохраняется: он живёт в этой вкладке до перезагрузки страницы.

имя видео-модели из каталога (GET /cabinet/api/public/media-models)

сцена для генерации; обязателен у каждого видео-движка

длительность в секундах; значение по умолчанию и набор задаёт движок модели

алиас duration; если переданы оба, duration побеждает

кадр готового ролика. Один и тот же набор у всех видео-движков

входная картинка: URL, data-URL, base64 или uuid картинки у провайдера; массив — несколько

входное видео для video-to-video: те же формы, что у image

true — держать соединение и вернуть результат, но не дольше 90 с

сколько секунд ждать; наличие поля включает ожидание даже без wait

id ТВОЕЙ прежней видео-джобы — провайдеру уходит её uuid как исходник

сцены сториборд-движка; шлюз их не валидирует, другим движкам поле не уходит

Запрос уйдёт по-настоящему. Генерация тарифицируется, и деньги резервируются сразу при постановке в очередь.

Ответ

Нажмите «Отправить запрос» выше — и здесь появится ответ.