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:
- Submit the task: a task ID is returned immediately and the task is queued.
- Poll the status: query every 5–10 seconds until it succeeds or fails.
- 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.