Developer Docs › Video generation

Video generation

Video runs as an async task — submit → poll status → download the result. Supports Seedance, Wan, Grok Imagine, HappyHorse, Gemini Omni and more.

Workflow

Video generation takes anywhere from tens of seconds to over ten minutes, so it runs as an async task:

  1. Submit the task: a task ID is returned immediately and the task is queued.
  2. Poll the status: query every 5–10 seconds until it succeeds or fails.
  3. Download the video: on success, download it from the returned link. Links expire (usually after 24 hours) — copy the file to your own storage promptly.

Depending on the model, there are two request formats:

Model family Format Submit endpoint
Seedance (doubao-seedance-*, dreamina-seedance-*) Volcengine Ark-compatible POST /api/v3/contents/generations/tasks
Wan, Grok Imagine, HappyHorse, Gemini Omni, etc. OpenAI video format POST /v1/video/generations

For available video models and prices, see the model catalog (search for keywords such as seedance, wan, video).

Seedance (Volcengine Ark-compatible format)

Request and response formats match the official Volcengine Ark Seedance API; existing Ark code only needs a new URL and key.

  • doubao-seedance-*: China edition; dreamina-seedance-*: international edition. Both take the same parameters.
  • -fast (faster) and -mini (cheaper) variants are also available.

Submit

cURL

curl https://api.onefast.ai/api/v3/contents/generations/tasks \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "dreamina-seedance-2-0-260128",
    "content": [
      {"type": "text", "text": "A cat stretching on a sunny windowsill, slow camera push-in"}
    ],
    "ratio": "16:9",
    "duration": 5,
    "resolution": "720p",
    "generate_audio": false
  }'

Response:

{ "id": "mvt-3d602a25b8da4b00", "model": "dreamina-seedance-2-0-260128", "status": "queued", "created_at": 1790824063 }

Query

cURL

curl https://api.onefast.ai/api/v3/contents/generations/tasks/mvt-3d602a25b8da4b00 \
  -H "Authorization: Bearer $API_KEY"

On success:

{
  "id": "mvt-3d602a25b8da4b00",
  "model": "dreamina-seedance-2-0-260128",
  "status": "succeeded",
  "content": { "video_url": "https://.../video.mp4?..." },
  "duration": 4,
  "resolution": "480p",
  "ratio": "16:9",
  "usage": { "completion_tokens": 40594, "total_tokens": 40594 }
}

status values: queued → running (generating) → succeeded / failed (see the error field).

Common parameters

Parameter Description
model Required
content Required. An array: {"type":"text","text":"..."} is the prompt; {"type":"image_url","image_url":{"url":"..."},"role":"first_frame"} is a first-frame image (role can be first_frame / last_frame / reference_image); {"type":"video_url","video_url":{"url":"..."},"role":"reference_video"} is a reference video
duration Duration in seconds. 2.0 series: 4–15; 2.5 series: 4–30
resolution 480p / 720p (default 720p)
ratio 16:9, 9:16, 1:1, 4:3, 3:4, 21:9 or adaptive (default; adapts to the input media)
generate_audio Generate audio, default true
watermark Add a watermark (optional)
seed Random seed
  • First/last frames and reference images (reference_image) can't be used together; up to 30 reference images.
  • Reference videos (reference_video): the 2.0 series accepts up to 3 clips totalling at most 15 seconds; the 2.5 series up to 10 clips totalling at most 30 seconds. Video URLs must be publicly accessible.
  • Image URLs must be publicly accessible, or inlined as data:image/...;base64,.

Image-to-video example

{
  "model": "dreamina-seedance-2-0-260128",
  "content": [
    {"type": "text", "text": "The girl turns around and smiles"},
    {"type": "image_url", "image_url": {"url": "https://example.com/first.jpg"}, "role": "first_frame"}
  ],
  "duration": 5,
  "resolution": "720p"
}

Billing

Billed on the usage.completion_tokens returned by the upstream (determined by resolution, duration and aspect ratio; 480p / 4 seconds is about 40,000 tokens). An estimate is pre-charged on submission and settled up or refunded on completion; failed tasks are refunded in full automatically.

Token prices come in two tiers, both listed in the model catalog:

Tier Applies when Notes
Standard Text only, first/last frames, reference images, reference audio The main unit price in the catalog
Reference video input content contains a reference video (reference_video) Lower unit price (about 60% of Standard), but token usage counts output duration + reference video duration

Reference videos must be passed as video_url items in the content array with role set to reference_video, and the URL must not be empty. The reference video input tier applies only when a reference video is actually submitted upstream with the task; otherwise the Standard tier applies.

For tasks billed at the reference video input tier, an extra amount is pre-charged at submission based on the maximum reference length (15 seconds for the 2.0 series, 30 seconds for the 2.5 series) and settled against actual usage on completion. With a low balance, the submission may be rejected for insufficient pre-charge.

Wan / Grok / HappyHorse / Gemini Omni (OpenAI video format)

Submit

cURL

curl https://api.onefast.ai/v1/video/generations \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "grok-imagine-video",
    "prompt": "A paper boat drifting on a rainy street, cinematic",
    "duration": 5
  }'

Response:

{ "id": "task_piMMxpEa...", "task_id": "task_piMMxpEa...", "object": "video", "model": "grok-imagine-video", "status": "queued", "progress": 0 }

Query

cURL

curl https://api.onefast.ai/v1/video/generations/task_piMMxpEa... \
  -H "Authorization: Bearer $API_KEY"

On success, status is completed and the video URL is in the url field (some models also return video_url):

{ "id": "task_piMMxpEa...", "object": "video", "status": "completed", "progress": 100, "url": "https://.../video.mp4" }

status values: queued → in_progress → completed / failed.

Parameters by model

Model Duration (s) Resolution Other
grok-imagine-video 1–15 Default Supports aspect_ratio, seed, negative_prompt
happyhorse-1.1-t2v 3–15 Default 1080P Same as above
wan-2-7-video 2–15 720p aspect_ratio: 16:9
wan-3-0-video 2–30 480p aspect_ratio: 16:9 / auto; optional sound, watermark
gemini-omni Fixed at 4 720p aspect_ratio: 16:9

Common fields: model (required), prompt (required), duration (or seconds), resolution, aspect_ratio.

Billing

Billed per second: seconds actually generated × the model's unit price (rounded up, 1-second minimum). Failed tasks are refunded automatically.

Full example: submit and wait for the result

Python

import os, time, requests

BASE = "https://api.onefast.ai"
H = {"Authorization": f"Bearer {os.environ['API_KEY']}"}

task = requests.post(f"{BASE}/v1/video/generations", headers=H, json={
    "model": "grok-imagine-video",
    "prompt": "A paper boat drifting on a rainy street, cinematic",
    "duration": 5,
}).json()
task_id = task["id"]

while True:
    r = requests.get(f"{BASE}/v1/video/generations/{task_id}", headers=H).json()
    if r["status"] in ("completed", "succeeded"):
        url = r.get("url") or r.get("video_url")
        open("video.mp4", "wb").write(requests.get(url).content)
        print("saved video.mp4")
        break
    if r["status"] == "failed":
        raise RuntimeError(r.get("error"))
    time.sleep(8)

Node.js

import fs from "node:fs";

const BASE = "https://api.onefast.ai";
const H = { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" };

const task = await (await fetch(`${BASE}/v1/video/generations`, {
  method: "POST", headers: H,
  body: JSON.stringify({ model: "grok-imagine-video", prompt: "A paper boat drifting on a rainy street, cinematic", duration: 5 }),
})).json();

for (;;) {
  const r = await (await fetch(`${BASE}/v1/video/generations/${task.id}`, { headers: H })).json();
  if (r.status === "completed" || r.status === "succeeded") {
    const buf = Buffer.from(await (await fetch(r.url || r.video_url)).arrayBuffer());
    fs.writeFileSync("video.mp4", buf);
    console.log("saved video.mp4");
    break;
  }
  if (r.status === "failed") throw new Error(JSON.stringify(r.error));
  await new Promise((s) => setTimeout(s, 8000));
}

Poll every 5–10 seconds, and never more than once per second. Save the task ID right after submitting — you can only query by ID.