vennaVenna

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

  1. POST /v1/videos creates the job and returns { id, status: "queued" }.
  2. GET /v1/videos/{id} polls until status is "completed" (or "failed").
  3. GET /v1/videos/{id}/content downloads 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: queuedin_progresscompleted (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.mp4

Billed 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

On this page