Create a video

POST/v1/videos
The same handler also answers on: /v1/video/generations · /v1/videos/extend · /v1/videos/storyboard

Queues a video generation: 202 with a job id, or the finished result inline with wait: true.

Request

modelstringbodyrequired
a video model name from the catalog (GET /cabinet/api/public/media-models)
promptstringbodyrequired
the scene to generate; required by every video engine
durationnumberbody
length in seconds; the default and the accepted set come from the model's engine
secondsnumberbody
alias of duration; if both are sent, duration wins
aspect_ratiostringbody= 16:9
the finished clip's frame. The same set on every video engine
16:9 · 9:16 · 1:1
imagestring|arraybody
an input image: a URL, a data-URL, base64 or the uuid of an image at the provider; an array — several
videostring|arraybody
an input video for video-to-video: the same forms as image
waitbooleanbody= false
true — hold the connection and return the result, but no longer than 90 s
timeoutnumberbody= 90
how many seconds to wait; its presence enables waiting even without wait
ref_video_job_idstringbody
the id of a video job of YOURS — its provider uuid is passed as the source
scenesstring|arraybody
scenes for the storyboard engine; unvalidated, and not forwarded on other engines

Responses

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

fails our own check before the provider; the reason is in error.message

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

PROVIDER_CODE — the provider rejected the submit: its own 4xx is echoed, job failed, hold released

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

no key in the request, or the key is not ours

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

the balance does not cover this request's worst-case price

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

ref_video_job_id points at a job that does not exist or is not yours

{ "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"
}

a 5xx or network on submit: the job may have been accepted — unknown_submit, hold stays

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

PROVIDER_CODE — with wait: true the generation failed: the hold is released, body has cost_usd: "0"

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

the hold could not be recorded: the job is failed and the money is free

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

the provider answered 200 with no job id: the job is failed and the hold released

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

Needs a key in Authorization: Bearer or x-api-key.

Generation is asynchronous. By default the endpoint answers 202 with a job id and the size of the hold (estimated_cost_usd), and the result is collected via GET /v1/videos/{job_id}. With wait: true (or any timeout present) the gateway polls the provider itself — 2 s per poll, capped at 90 s; timeout can only shorten that budget, never raise it. If the budget runs out you get a 202 with the same job id and the job keeps running. Polling is optional: the reconciler (one pass every 150 s) finalizes a finished job on its own — charges it and stores the result links.

There are three input modes, chosen by the body rather than by the URL: prompt — text-to-video; prompt + image — image-to-video; prompt + video — video-to-video (edit and motion control, where image is the character and video the motion). What the engine does with an input is the engine's business; our own check is one — an engine that requires an input video answers 400 without one on our side, before the provider. The model list lives in the catalog (GET /cabinet/api/public/media-models), but the catalog does not serve the required inputs or the duration sets: those are a table on our side, visible from outside only through our error text.

/v1/video/generations, /v1/videos/extend and /v1/videos/storyboard are aliases of this same handler: the same body, the same responses and errors, no per-path behaviour at all. Extending a clip and storyboarding are selected by the model — separate engines in the catalog (*-extend, *-storyboard) — and the source clip is passed in ref_video_job_id.

About the fields. Length and its accepted set belong to the engine: seedance — 4–15 s (5 by default) · kling — 3–15 on 3.0, 3–10 on edit/o1/motion (5) · kling 2.5/2.6 — only 5 or 10 · kling 2.1 — exactly 10 or exactly 5, depending on the model · veo — 4/6/8 (8) · omni-flash — 4/6/8/10, though every catalog row pins one of them · grok — 6 only · extend — 8 on veo, 6 on grok, 4–15 on seedance · storyboard — 6–30 (6). A value outside the set answers 400 listing what is allowed (for a range, its bounds). ⚠️ A catalog row may pin the length outright (fixed_params), and then the duration you sent never reaches the provider. ⚠️ Omit duration and the provider gets the engine's default while the hold is sized at the engine's MAXIMUM (hold_duration_seconds); the charge follows the finished clip's actual length. timeout is clamped at the same 90 s, and a non-numeric value falls back to the same 90.

Inputs are accepted under several names: image is also images, image_url, image_urls, input_image, init_image, ref_images, and video is also videos, video_url, input_video, motion_video, ref_video, ref_videos. URLs — images and videos alike — are fetched by the gateway itself (own User-Agent, own size cap, internal addresses refused at every redirect hop); if the fetch fails or the body does not look like media, the link is forwarded as-is. ⚠️ An input the engine does not expect is still forwarded to the provider: the catalog is consulted for requiredness only. Parameters outside the engine's set (resolution, size, any unknown field) are dropped silently — resolution and tier come from the catalog, not from the request.

What answers 400 before the provider: broken JSON, an unknown or disabled model, a missing prompt, a duration outside the engine's set, a required input not sent, an input over 80 MB (the cap is per decoded file). ref_video_job_id is owner-scoped: someone else's id, or a missing one, answers 404 so a leaked id confirms nothing. A provider submit failure: its 4xx is echoed verbatim — with its code and its reason, scrubbed of upstream names — and that is final, the job is failed and the hold released; a 5xx or a network error means the job MAY have been accepted, and refunding on a guess would be a double credit — the job goes to unknown_submit and waits for the reconciler, which either completes it or releases the hold on TTL (unknown_submit has a short one, 15 minutes by default; everything else, a day).

Code examples
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
Request
https://api.teamtoken.store/v1

The panel calls this domain; in your own code use the address above.

The key is never stored: it lives in this tab until you reload the page.

a video model name from the catalog (GET /cabinet/api/public/media-models)

the scene to generate; required by every video engine

length in seconds; the default and the accepted set come from the model's engine

alias of duration; if both are sent, duration wins

the finished clip's frame. The same set on every video engine

an input image: a URL, a data-URL, base64 or the uuid of an image at the provider; an array — several

an input video for video-to-video: the same forms as image

true — hold the connection and return the result, but no longer than 90 s

how many seconds to wait; its presence enables waiting even without wait

the id of a video job of YOURS — its provider uuid is passed as the source

scenes for the storyboard engine; unvalidated, and not forwarded on other engines

This request really goes out. A generation is billed, and its price is reserved the moment the job is queued.

Response

Press “Send request” above and the answer shows up here.