Video generation
Generate video with wan-2.2-t2v through the async queued → completed → content flow.
Video is the one gateway surface that isn't a single request/response round trip. A model like
wan-2.2-t2v can take minutes to render, so POST /v1/videos answers immediately with a
queued job and you poll for the result — the same shape OpenAI's Sora API uses.
Three calls, one job
POST /v1/videoscreates the job and returns{ id, status: "queued" }.GET /v1/videos/{id}polls untilstatusis"completed"(or"failed").GET /v1/videos/{id}/contentdownloads the rendered mp4 bytes.
Create the job
Send a prompt and model. The optional knobs below are validated against that model's
supported ranges — an unsupported value 400s before anything is queued. The response is a
VideoGenerationJob, not the video itself:
Prop
Type
curl https://gateway.venna.net/v1/videos \
-H "Authorization: Bearer $VENNA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "wan-2.2-t2v",
"prompt": "a red fox trotting through fresh snow, cinematic",
"seconds": 5,
"size": "720p",
"aspect_ratio": "16:9"
}'{
"id": "video-6d1f...",
"object": "video.generation.job",
"created_at": 1735689600,
"status": "queued",
"model": "wan-2.2-t2v"
}const res = await fetch("https://gateway.venna.net/v1/videos", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VENNA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "wan-2.2-t2v",
prompt: "a red fox trotting through fresh snow, cinematic",
seconds: 5,
size: "720p",
aspect_ratio: "16:9",
}),
});
const job = await res.json();
console.log(job.id, job.status); // "video-6d1f...", "queued"import os
import requests
res = requests.post(
"https://gateway.venna.net/v1/videos",
headers={"Authorization": f"Bearer {os.environ['VENNA_API_KEY']}"},
json={
"model": "wan-2.2-t2v",
"prompt": "a red fox trotting through fresh snow, cinematic",
"seconds": 5,
"size": "720p",
"aspect_ratio": "16:9",
},
)
job = res.json()
print(job["id"], job["status"]) # "video-6d1f...", "queued"This surface is Venna-specific (no OpenAI SDK equivalent), so every language shows raw HTTP against the same base URL and bearer key the rest of the gateway uses.
Poll for completion
GET /v1/videos/{id} returns the same VideoGenerationJob shape with an updated status:
queued → in_progress → completed (or failed, with an error.code/error.message
pair). Poll on an interval instead of hammering the endpoint:
async function waitForVideo(id: string): Promise<void> {
while (true) {
const res = await fetch(`https://gateway.venna.net/v1/videos/${id}`, {
headers: { Authorization: `Bearer ${process.env.VENNA_API_KEY}` },
});
const job = await res.json();
if (job.status === "completed") return;
if (job.status === "failed") {
throw new Error(`video ${id} failed: ${job.error?.message}`);
}
await new Promise((r) => setTimeout(r, 5000));
}
}curl https://gateway.venna.net/v1/videos/video-6d1f... \
-H "Authorization: Bearer $VENNA_API_KEY"A 404 means the job ID is unknown, expired, or belongs to another workspace — not a transient error worth retrying.
Download the mp4
Once status is "completed", GET /v1/videos/{id}/content streams the raw mp4 bytes.
Calling it earlier returns 409 — keep polling instead of racing the render:
const content = await fetch(
`https://gateway.venna.net/v1/videos/${job.id}/content`,
{ headers: { Authorization: `Bearer ${process.env.VENNA_API_KEY}` } },
);
const bytes = await content.arrayBuffer();
await Bun.write("fox.mp4", bytes);curl https://gateway.venna.net/v1/videos/video-6d1f.../content \
-H "Authorization: Bearer $VENNA_API_KEY" \
-o fox.mp4Billed by generated seconds
Video is metered by output SECONDS, fps-scaled — not by request or by wall-clock render
time. A 409 (not-yet-ready) or failed job never charges you; only a completed job
settles the meter against the model's reported billedSeconds.
Next steps
- API reference — Videos — full request/response schemas for all three endpoints.
- Quickstart — base URL and key setup shared across every gateway surface.