Anatomy of a text-to-video API request and response
Updated 2026-10-17
A text-to-video call is a small JSON body and a long wait. Almost every integration bug comes from misunderstanding one of three things: which fields are hints and which are constraints, what the job object looks like at each stage, and when money moves. This page takes one request apart piece by piece, using VideoRouter's POST /videos as the worked example. The same structure shows up, with different names, in most video APIs.
The smallest useful request
curl https://videorouter.sh/api/v1/videos \
-H "Authorization: Bearer llmr_sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"model": "kling-v3.0-std",
"prompt": "a paper airplane gliding over a city at dusk, slow tracking shot",
"duration_secs": 5,
"aspect_ratio": "16:9"
}'
The response does not contain a video. It contains a job: an id and a status of queued. Everything else in this guide is about the fields you sent and the states that job passes through.
Field by field
| Field | Role | Behaviour worth knowing |
|---|---|---|
model | Selects the checkpoint, and optionally a host | An id such as kling-v3.0-std lets the platform choose a host. Suffixing a host (model/host) is a soft preference that can still fall back. Tiers like Std, Pro and 4K are separate model ids, not a quality flag. |
prompt | Describes the scene and motion | The only field required for plain text-to-video. Describe change over time: subject, action, camera. |
duration_secs | Requested clip length | This is the number you are billed on. Supported lengths differ per model, so check the model page. |
aspect_ratio | Frame shape, e.g. 16:9, 9:16, 1:1 | Documented set includes 16:9, 9:16, 1:1, 4:3, 3:4 and 21:9, but each model supports only some. |
resolution | Resolution tier, e.g. 720p | Same caveat: unsupported values are not rejected. |
provider | Routing preferences | Optional object. only, ignore, order, sort, policy and allow_fallbacks shape which hosts are tried. |
Soft fields and hard fields
The distinction that causes the most surprise: for size, an unrecognised or unsupported resolution and aspect_ratio combination never returns a 400. VideoRouter's documentation says it is ignored and the model's default is used, the same as omitting both. That is friendly for exploration and dangerous for production, because a request can succeed and still deliver a different shape than you asked for. Verify output dimensions in your pipeline instead of trusting the status. By contrast, fields a model genuinely cannot accept, such as an image input on a text-only model, do return a clear 400, which you should treat as a bug in your code and never retry.
Routing hints inside the body
Provider fields are optional and most callers should skip them: with no provider and no host suffix, the request goes to the cheapest healthy host and falls back on rejection. When you do need control, three levels exist. A host suffix on the model is a preference. provider.sort or provider.policy reorders the same candidate walk by something other than price. provider.only with allow_fallbacks: false is a hard pin that fails fast with a single attempt. Read the provider selection docs before using the last one; it removes your safety net.
The job lifecycle
queued: accepted, not yet running. You have an id and have been charged.in_progress: a host is rendering.completed: the finished object carries the output indata[0].url.failed: the object carries anerrorinstead of data.
You move between states by calling GET /videos/{id}, which is free. A loop that sleeps a few seconds between calls with a hard deadline is enough; the quickstart has the complete loop. Do not hold HTTP connections open waiting for a clip.
{
"id": "video_...",
"status": "completed",
"data": [ { "url": "https://..." } ]
}
Copy that URL into your own storage right away. It is a delivery link, not an archive.
Errors at creation time
Failures before a job exists use an OpenAI-style envelope: {"error": {"message": ..., "type": ..., "code": ...}}. Branch on HTTP status first. A 400 means the request is wrong. A 401 is a bad key. A 402 means a spend cap or empty balance, distinguished by code. A 429 is a rate limit and should be retried after the Retry-After value. Server-class statuses mean every candidate host failed. Creation failures never produce a job, so there is nothing to poll.
What gets billed, and when
- At creation. The charge is computed from the requested
duration_secsand the rate for the chosen model and host, plus the 2.5% platform fee on image and video. - Not for polling. Status calls are free.
- Not for upstream failure. A job that ends
failedbecause every upstream host failed is not billed. - Yes for a second job. Re-submitting after a completed clip you did not like is a new charge, as is any hedge you opt into with
failover.on_timeout_secif both attempts finish.
Because the charge depends on duration and host, the cheapest way to cost a request before sending it is a live table:
| Model | Cheapest host | Priciest host | Cheapest is | Hosts |
|---|---|---|---|---|
| bytedance/seedance-2.5 (720p) | SoCheap-Overseas $0.1419 / second | Fal-US $0.5676 / second | 75% lower | 11 |
| bytedance/seedance-2.0 (720p) | MachGen $0.1275 / second | Fal $0.3034 / second | 58% lower | 8 |
| alibaba/wan-3.0 (480p) | Replicate $0.025 / second | Alibaba $0.05 / second | 50% lower | 11 |
| google/gemini-omni-flash | Google $0.1 / second | Fal $0.13 / second | 23% lower | 4 |
| minimax/h3 (768p) | kie.ai $0.04 / second | WaveSpeedAI-resell $0.1 / second | 60% lower | 13 |
| alibaba/happyhorse-1.1 (720p) | Pika $0.098 / second | Alibaba $0.14 / second | 30% lower | 4 |
| bytedance/seedance-2.0-fast (480p) | Atlas Cloud $0.027 / second | Fal $0.2419 / second | 89% lower | 8 |
| bytedance/seedance-2.0-mini (480p) | Atlas Cloud $0.0112 / second | Fal $0.0721 / second | 84% lower | 7 |
| kling-o3 (720p) | SandBase $0.0588 / second | Tencent TokenHub $0.084 / second | 30% lower | 4 |
| kling-v3 (720p) | SandBase $0.0588 / second | Tencent TokenHub $0.084 / second | 30% lower | 8 |
| google/gemini-omni-1.1-flash (360p) | WaveSpeedAI $0.03 / second | Google $0.1 / second | 70% lower | 3 |
| lightricks/ltx-2.3-pro (2160p) | MachGen $0.1 / second | SandBase $0.32 / second | 69% lower | 2 |
Per second, before VideoRouter's 2.5% platform fee. For tiered models each row compares the resolution tier with the widest host-to-host gap. Built 2026-10-17 from the live catalog.
Duration is the lever you control: request the length you will actually use. The pricing page has the current rates, and the build-versus-buy guide covers what else sits around this call in production.
Putting it together
A well-behaved client sends the smallest request that expresses its intent, treats size fields as hints and verifies the result, polls with a deadline, copies the output out immediately, and logs the job id next to the prompt so a charge can be traced to a request. Create a key and send the example above with a five-second duration to see each state for yourself.
Frequently asked questions
What fields does a text-to-video API request need?
At minimum a model and a prompt. In practice you also send duration_secs, and optionally aspect_ratio, resolution and provider routing hints.
What does the API return when I create a video?
A job object with an id and a status of queued, not a video. You poll the job until it is completed or failed, then read the output URL from data[0].url.
Does an unsupported aspect ratio cause an error?
Not on VideoRouter: an unsupported resolution and aspect_ratio combination is ignored and the model default is used, so verify output dimensions.
When am I charged for a video job?
Once at creation, based on the requested duration. Polling is free and a job that fails because every upstream host failed is not billed.
Keep reading
- What Is a Video Generation API? How It Works and What It Costs
- Video Generation API Use Cases and the Workflow Each Needs
- Text-to-Video vs Image-to-Video vs Reference-to-Video (API)
- Build vs Buy Video Generation: Hosted API or Self-Hosting?
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 →