Video generation behind a relay: three API shapes, four billing traps, one acceptance checklist
Why video is the least tidy category
Text has a de facto standard in POST /v1/chat/completions. Images have POST /v1/images/generations. Video has nothing.
To date there is no unified protocol: every vendor ships its own submission method, its own task state machine and its own way of collecting the result. A relay sits in the middle and can only pass each vendor through individually, so on the same site two video models may be called in completely different ways.
That single fact determines three things: you cannot write one generic wrapper, you cannot bill against a single multiplier, and you cannot apply one retry policy.
Three API shapes
Shape one: OpenAI-style asynchronous task
Submission returns a task object, then you poll by id:
# submit
curl -s https://<base_url>/v1/videos \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"model":"<video-model>","prompt":"a cat walking on a beach","seconds":"8","size":"1280x720"}'
# poll
curl -s https://<base_url>/v1/videos/<video_id> -H "Authorization: Bearer $KEY"
Test: the submission response must contain id and status, and only when status becomes completed does the response expose a downloadable location.
Shape two: submit, poll, fetch
More common with Chinese vendors: the task id is task_id, status lives at a separate path, and the result URL has a limited lifetime.
curl -s https://<base_url>/v1/video/generations \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"model":"<model>","prompt":"...","duration":5}'
# => {"task_id":"..."}
curl -s https://<base_url>/v1/video/generations/<task_id> -H "Authorization: Bearer $KEY"
# => {"status":"succeed","data":{"url":"https://.../xxx.mp4"}}
Shape three: synchronous return (rare)
The URL comes back immediately, sometimes as base64. Only suitable for very short, very low resolution clips, because the HTTP connection has to stay open for tens of seconds and any proxy timeout along the way will cut it.
| Dimension | Shape one | Shape two | Shape three |
|---|---|---|---|
| Submit path | /v1/videos | /v1/video/generations | same as submit |
| Key fields | id / status | task_id / status | url |
| Typical duration | 30-180 s | 20-300 s | 5-20 s |
| Timeout risk | low (polling uses short connections) | low | high |
| Result lifetime | often 24 h | often 1-24 h | may expire instantly |
If your client or gateway is bad at long-lived connections, use shape one or two and never shape three.
Four billing traps
Trap one: per second, per call and per token coexist on one site
On a single site, model A bills per generated second, model B bills a flat price per call, and model C bills input tokens plus output video tokens. The consumption figure in your dashboard is a blend, so you cannot reverse-engineer a unit price from it.
Do this: run one minimum request per video model — same prompt, shortest duration, lowest resolution — and record the charge. That is your baseline unit price, and every later anomaly is measured against it.
Trap two: resolution and duration change the price, but the multiplier table only covers text
A relay's "multiplier" is usually defined against text tokens. For video, 720p versus 1080p and five seconds versus ten can differ two to three times in price, while the table shows a single number.
Test: run the same prompt at 720p/5 s and at 1080p/10 s and compare the charge. If the ratio does not match the vendor's public price list, the relay has added its own tier coefficient — and it owes you an explanation.
Trap three: failures are billed
Whether the charge is refunded after a failure (content moderation rejection, upstream timeout, a task stuck in processing) varies by provider, and this is almost never written in the price list.
Do this: deliberately submit a prompt that must be rejected, then look at the billing record. That is worth more than ten pages of terms.
Trap four: result URLs expire
The returned url is usually a presigned object storage address with a lifetime between a few minutes and 24 hours. Once it expires you cannot retrieve the video again, and relays generally do not offer a re-issue endpoint.
The only correct move is to download immediately rather than storing the URL as a permanent link.
A six-step acceptance checklist
| Step | Action | Pass criteria |
|---|---|---|
| 1 | Submit a minimum request | Got id or task_id, HTTP 200 |
| 2 | Poll to a terminal state | Status moves from processing to completed, and a terminal state exists (it must not sit in processing forever) |
| 3 | Download at once | After curl -o out.mp4, [ -s out.mp4 ] is true and the size exceeds 100 KB |
| 4 | Verify media properties | Duration and resolution read by ffprobe match the request |
| 5 | Reconcile the charge | Dashboard consumption equals the baseline from step 1 |
| 6 | Retry once | Submit the same request again and confirm it does not charge twice for one success |
Step 4 is the one most often skipped and the one that exposes the most. Asking for ten seconds and receiving five has actually happened on relays, because the upstream rounds to a tier and the gateway never validates it.
ffprobe -v error -select_streams v:0 \
-show_entries stream=width,height,duration \
-of csv=s=x:p=0 out.mp4
# expected output such as 1280x720x8.000000
When a relay is worth it for video
Only two situations justify going through a relay:
- You need several video models side by side and do not want a separate account and deposit for each;
- You operate in mainland China while the vendor only accepts overseas payment, making a relay the only practical settlement path.
Conversely, if your volume is steady and you use one model, going direct is cheaper and easier to debug — a middle layer for this kind of API delivers no protocol unification, only one more place to fail.
Summary
- Video has no shared protocol; the shape decides how your code is written.
- Measure unit prices yourself; per second, per call and per token cannot be mixed.
- Resolution and duration change the price, and multiplier tables usually ignore them.
- Test refund behaviour with a request that is guaranteed to fail.
- Result URLs expire, so download as soon as you get one.
- One successful generation is not acceptance: let the numbers
ffprobereports decide.
Was this useful? If you have run into something the article does not cover, tell us and we will verify it and update the text.
Related articles
MCP through a relay: what works, what never will, and how to test it in ten minutes
A relay selection decision tree: working back from your scenario to a site
Logging and reconciliation: attributing token spend to the project level
Migrating suppliers gradually: replacing an API site without interrupting service
Build your own probe: twenty lines of script to monitor your API suppliers
IP allowlists, sub-accounts and permission tiers: a survey of relay governance
Read on: a few of the services we list
This article does not point at a specific model family, so here are a few of the highest-scoring services we list, in case you want to read on. They are ordered by score and uptime and are not a recommendation.
See the full list in the directory, or browse by model family.