v1 https://videorouter.sh/api/v1

AI Video API for developers

A plain REST interface to AI video models. Create a job, poll it, download the MP4. Pin a provider or let the router pick the cheapest healthy host.

Authentication

Send your key as a bearer token. Keys can carry a monthly spend cap, rate limits and a per-key model allow-list.

Authorization: Bearer llmr_sk_live_...

Create a video

POST /v1/videos — returns 202 with a job.

curl https://videorouter.sh/api/v1/videos \
  -H "Authorization: Bearer $VIDEOROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "bytedance/seedance-2.0-mini", "prompt": "a paper airplane gliding over a city", "duration_secs": 5}'
# -> {"id": "video_...", "status": "queued"}
curl https://videorouter.sh/api/v1/videos/$ID -H "Authorization: Bearer $VIDEOROUTER_API_KEY"
import time, requests
H = {"Authorization": "Bearer llmr_sk_live_..."}
job = requests.post("https://videorouter.sh/api/v1/videos", headers=H, json={
    "model": "bytedance/seedance-2.0-mini", "prompt": "a paper airplane gliding over a city", "duration_secs": 5}).json()
while job["status"] not in ("completed", "failed"):
    time.sleep(5)
    job = requests.get(f"https://videorouter.sh/api/v1/videos/{job['id']}", headers=H).json()
print(job["data"][0]["url"] if job["status"] == "completed" else job["error"])
const H = { Authorization: "Bearer llmr_sk_live_...", "Content-Type": "application/json" };
let job = await (await fetch("https://videorouter.sh/api/v1/videos", { method: "POST", headers: H,
  body: JSON.stringify({ model: "bytedance/seedance-2.0-mini", prompt: "a paper airplane gliding over a city", duration_secs: 5 }) })).json();
while (!["completed", "failed"].includes(job.status)) {
  await new Promise(r => setTimeout(r, 5000));
  job = await (await fetch(`https://videorouter.sh/api/v1/videos/${job.id}`, { headers: H })).json();
}
console.log(job.data?.[0]?.url ?? job.error);
FieldTypeDescription
modelstringModel id, optionally with a host suffix (model/host) to prefer a provider.
promptstringText prompt. Required for most models.
duration_secsnumberSeconds of output. Snapped to a value the model supports; billing uses the snapped value.
aspect_ratio / resolutionstringe.g. 16:9, 720p. Unsupported combinations are ignored (model default), never a 400.
start_image_urlstringImage-to-video: public https URL or base64 data URI.
input_referencesarrayReference-to-video: image entries (model-dependent cap).
input_video_urlstringVideo editing: an existing clip to modify. Needs a prompt describing the edit.
provider / failoverobjectProvider preferences; failover.on_timeout_sec resubmits to the next host (both attempts billed).

Get a video

GET /v1/videos/{id} — poll until status is completed or failed. Polling is not billed. On success data[0].url is the file.

List models

GET /v1/videos/models — every valid model id (ids only). For prices and hosts use the price tables.

Errors

All errors share the OpenAI-style envelope {"error": {"message", "type", "code"}}.

StatusType / codeMeaning
400invalid_request_errorMissing prompt, unknown model id, bad parameters. Don’t retry.
401invalid_api_keyMissing, malformed, revoked or expired key.
402insufficient_credits / spend_cap_exceededBalance empty or monthly cap hit. Don’t retry.
403model_not_allowedModel not in this key’s allow-list.
429rpm_limit / tpm_limitRate limited. Wait Retry-After, retry.
5xxupstream_errorAll candidate hosts failed. Retry with backoff; not billed.

Billing

Charged once at creation from the requested (snapped) duration; polling is free; jobs that end failed because every upstream host failed are not billed. VideoRouter adds a flat 2% platform fee on image and video generation.

FAQ

Is there an SDK?

You don't need one — it's a REST API with a simple job lifecycle. The quickstart shows curl, Python and JavaScript.

How do I know when a video is done?

Poll GET /v1/videos/{id} until status is completed or failed. Polling is not billed.

What happens if a provider fails?

Unpinned requests retry another host serving the same model. Pinned requests fail fast, by design.

Can I limit spend per key?

Yes — keys support monthly caps, rate limits and per-key model allow-lists.

Guides

Using AI video is one part of the job.

VideoRouter puts it next to dozens of other video and image models behind one API key, so you can compare providers, prices and fail over automatically. Compare providers on VideoRouter →