Developer Docs › Video generation

Video generation

Video runs as an async task — submit → poll status → download the result. Supports Seedance, Wan, Grok Imagine, MiniMax H3, 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, MiniMax H3, 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 https:// URLs of at most 4000 characters, or asset://<Id> references from the asset library. Do not put base64 or data: URLs in image_url: such requests are rejected with 400 InvalidParameter (… is too long (… max 4000)) and are not billed. For local files, upload them to the private asset library first (see below).

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"
}

Draft mode (Seedance 2.5)

Render a cheap 480p draft first to check framing, camera movement and how the prompt was understood, then turn the draft you like into a 1080p final that closely matches it. You only pay the 1080p price for the shot you keep.

1. Render a draft. Submit as usual with "draft": true; resolution must be 480p.

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-5-260628",
    "content": [{"type": "text", "text": "An orange cat chasing butterflies in a sunny garden, smooth tracking shot"}],
    "draft": true,
    "resolution": "480p",
    "duration": 5,
    "ratio": "16:9"
  }'

2. Render the final from the draft. Once the draft has succeeded, put a single draft_task item in content and set resolution to 1080p:

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-5-260628",
    "content": [{"type": "draft_task", "draft_task": {"id": "mvt-3d602a25b8da4b00"}}],
    "resolution": "1080p"
  }'
  • Supported models: dreamina-seedance-2-5-260628, doubao-seedance-2-5-260628. Other models return 400.
  • draft_task.id is the draft's task ID (mvt-…); the draft's metadata.id (cgt-…) is accepted too. The draft must have been submitted with the same API key, must have succeeded, and is valid for 7 days.
  • In the final request, content must contain only the draft_task item and model must match the draft. The prompt, reference media, duration, ratio, seed and generate_audio are inherited from the draft; sending them returns 400. watermark, return_last_frame and execution_expires_after can be set again.
  • Billing: the draft is billed at 480p; the final is billed at 1080p for the same duration as the draft. If the draft used a reference video, the final is billed at the reference video input tier as well.
  • When queried, a draft returns "draft": true; a final returns draft_task_id pointing to its draft. POST /v1/video/generate accepts the same draft / draft_task fields.

Real-person content moderation

This check applies to Seedance models only. Other video models, such as Wan and MiniMax-H3, are not subject to it.

Every image, video and audio input of a Seedance task is checked by the upstream provider before generation. Inputs that show real, identifiable people are rejected:

Error code Meaning
InputImageSensitiveContentDetected.PrivacyInformation A reference image or first/last frame may contain a real person
InputVideoSensitiveContentDetected.PrivacyInformation A reference video may contain a real person
  • Each input is checked on its own. If both the image and the video show real people, replacing only one of them is not enough.
  • The check is applied to the media itself. Describing a real person as "fictional" in the prompt does not change the result.
  • The check is not limited to visible faces. Realistic bodies, hands, silhouettes or figures can also trigger it, and so can photorealistic AI-generated people.
  • Rejected tasks are not billed; the pre-charge is refunded in full.
  • For choreography or motion references, use footage that does not show identifiable real people.

To use a consistent fictional character, add it to the private asset library below.

Private asset library (virtual characters)

Seedance's input moderation may reject images that contain realistic human faces (errors such as InputImageSensitiveContentDetected). If you need a fictional or AI-generated character to appear consistently across videos, upload it to the private asset library first, then reference it in content by its asset ID. Library assets are trusted inputs and are not blocked by that check.

Real people are not allowed. Assets must not resemble any real person — do not upload photos of real people, including yourself, your customers, employees or public figures. Only fictional or AI-generated characters are permitted. Uploading real-person likenesses violates the upstream provider's terms and may lead to your account being suspended.

This restriction applies to Seedance only; Wan and MiniMax-H3 are not subject to it.

1. Create the asset. Set model to the model you will generate with. URL must be publicly accessible; AssetType is Image, Video or Audio.

cURL

curl https://api.onefast.ai/v1/sd/assets \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "dreamina-seedance-2-5-260628", "URL": "https://example.com/fictional-character.png", "Name": "fictional-character-01", "AssetType": "Image"}'
{ "success": true, "data": { "Id": "mva-e144dfc364a647f1", "Ref": "asset://mva-e144dfc364a647f1", "Status": "Processing", "AssetType": "Image", "Name": "fictional-character-01", "Model": "dreamina-seedance-2-5-260628", "Error": null } }

Assets are valid for 7 days; re-upload after that. Use data.Ref directly in content.

2. Wait until it is active. Query GET /v1/sd/assets/{Id} until data.Status is Active (usually a few seconds).

cURL

curl https://api.onefast.ai/v1/sd/assets/mva-e144dfc364a647f1 \
  -H "Authorization: Bearer $API_KEY"

3. Reference it in a task with asset://<Id> wherever an image, video or audio URL is accepted:

{
  "model": "dreamina-seedance-2-5-260628",
  "content": [
    {"type": "text", "text": "The character walks through a rainy neon street at night"},
    {"type": "image_url", "image_url": {"url": "asset://mva-e144dfc364a647f1"}, "role": "reference_image"}
  ],
  "duration": 5,
  "resolution": "720p"
}

The same asset:// references also work with POST /v1/video/generate.

Rules:

  • Same model: the task's model must be exactly the model given when the asset was created. An asset created for dreamina-seedance-2-5-260628 cannot be used with dreamina-seedance-2-0-260128.
  • Same model for all assets in one request: every asset referenced in a single task must have been created for the same model.
  • Same API key: an asset can only be queried and referenced with the API key that created it. Asset IDs from your own BytePlus / Volcengine account cannot be used.
  • Supported models: currently dreamina-seedance-2-0-260128, dreamina-seedance-2-0-fast-260128, dreamina-seedance-2-0-mini-260615, dreamina-seedance-2-5-260628, doubao-seedance-2-0-260128, doubao-seedance-2-0-fast-260128, doubao-seedance-2-0-mini-260615, doubao-seedance-2-5-260628.
  • Creating and querying assets is free; video tasks that use assets are billed as usual.
Error Cause
403 video asset … was not found for this API key The asset was not created via POST /v1/sd/assets, or was created with a different API key
403 The requested model does not match the model the referenced video asset was created with model differs from the model used when creating the asset
400 all asset references must belong to the same video channel and model Assets created for different models are mixed in one request
409 Reference material '…' is still processing The asset is not Active yet; wait a few seconds and retry
400 video asset … was created with a retired upload flow The asset was created before 2026-10-15; re-upload it and use the new reference

Failure error codes

When a Seedance task fails — at submission or later in the task's error field — error.code tells you what to do next. Failed tasks are not billed; any pre-charge is refunded in full.

error.code Meaning What to do
invalid_parameter / InvalidParameter.* A request parameter was rejected Fix the parameter named in message, then resubmit
content_moderation_rejected / *SensitiveContentDetected.* The input or output was rejected by content moderation Replace or revise the content; resubmitting the same content will be rejected again
video_download_failed A reference image / video / audio URL could not be downloaded Make sure the URL is publicly accessible (no login, expired signature, hotlink protection or IP restriction)
video_processing_timeout / video_processing_error Temporary error in the generation service The content does not need to change; retry
video_submit_timeout The submission timed out and could not be confirmed The content does not need to change; retry
concurrent_task_limit The concurrent task limit for this model has been reached (HTTP 429 at submission) Wait for running tasks to finish, then submit again
service_temporarily_unavailable The service is temporarily unavailable (HTTP 503 at submission) Retry later
video_generation_failed Other failure Retry; if it keeps failing, contact support with the task ID

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 / MiniMax H3 / 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 / 720p / 1080p Same as wan3.0-video; see "Wan 3.0 family" below
gemini-omni Fixed at 4 720p aspect_ratio: 16:9

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

Grok Imagine 1.5 and the MiniMax H3 family

grok-imagine-video-1.5, minimax-h3, minimax-h3-2k, minimax-h3-max and minimax-h3-max-turbo are also submitted with POST /v1/video/generations and polled with the query endpoint above. Parameters:

  • Duration goes in duration (whole seconds, required); seconds also works.
  • Aspect ratio goes in ratio; aspect_ratio also works. If both are sent, duration / ratio win.
  • Pass a first-frame image URL in image.

cURL

curl https://api.onefast.ai/v1/video/generations \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "grok-imagine-video-1.5",
    "prompt": "A paper boat drifting on a rainy street, cinematic",
    "duration": 5,
    "resolution": "720p",
    "ratio": "9:16"
  }'
Model Duration (s) Resolution ratio First frame / reference images
grok-imagine-video-1.5 1–15 480p / 720p / 1080p 16:9 (default), 9:16, 1:1, 4:3, 3:4, 3:2, 2:3, 21:9, 5:2; with a first frame and no ratio, the frame's ratio is used Up to 1
minimax-h3-max, minimax-h3-max-turbo 5–15 480p / 768p 16:9 (default), 9:16, 1:1, 4:3, 3:4, 21:9; with a first frame you can pass adaptive (follow the frame, also the default when a first frame is given) Up to 2 (including first/last frame)
minimax-h3 4–15 Fixed at 768P — —
minimax-h3-2k 4–15 Fixed at 2K — —
  • If resolution is omitted, the video is generated and billed at the default tier shown in the model catalog. For minimax-h3 / minimax-h3-2k the resolution is set by the model name and any resolution you pass has no effect.
  • These models do not accept reference videos, reference audio or asset:// references from the asset library; such requests are rejected with 400 and not billed.
  • A duration, resolution or ratio outside the ranges above is rejected with 400 at submission and not billed.

Wan 3.0 family

wan3.0-video, wan3.0-video-prime (fast edition) and the -lm models wan3.0-video-lm, wan3.0-video-prime-lm, wan3.0-video-pro-lm and wan3.0-video-pro-prime-lm are all submitted with POST /v1/video/generations and polled with the query endpoint above. On success status is completed; read the video link from video_url.

cURL

curl https://api.onefast.ai/v1/video/generations \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "wan3.0-video-lm",
    "prompt": "The man slowly turns his head and smiles",
    "duration": 5,
    "resolution": "720p",
    "first_frame_url": "https://example.com/portrait.jpg"
  }'
Model Resolution Smart duration (-1) Reference video
wan3.0-video 480p / 720p / 1080p — Yes
wan3.0-video-prime 480p / 720p / 1080p(faster) — Yes
wan3.0-video-lm 480p / 720p / 1080p Yes Yes
wan3.0-video-prime-lm 480p / 720p / 1080p(faster) Yes Yes
wan3.0-video-pro-lm 1080p / 2k / 4k Yes Yes
wan3.0-video-pro-prime-lm 1080p / 2k / 4k(faster) Yes Yes
Field Description
duration (or seconds) Integer seconds, 2–30, required. The -lm models also accept -1 (smart duration: the model picks the length)
resolution See the table above; if omitted, the default tier listed in the model catalog is generated and billed
ratio (or aspect_ratio) 16:9 (text-to-video default), 9:16, 4:3, 1:1, 3:4; wan3.0-video / wan3.0-video-prime also accept 21:9. With a first frame you can pass adaptive (follow the first frame; also the default when a first frame is given)
first_frame_url First-frame image URL (image also works)
last_frame_url Last-frame image URL; requires a first frame
image_urls Array of reference image URLs (people, objects)
seed Random seed, 0–2147483647
generate_audio -lm models only: generate an audio track
prompt_extend -lm models only: let the model expand the prompt
video_urls Array of reference video URLs (video editing / replacing the main subject), up to 3; input video length is billed per second too
  • Smart duration billing: with -1, 30 seconds are reserved at submission; on completion you are billed for the seconds actually generated and the difference is refunded, so your balance must cover 30 seconds.
  • Reference video billing: with reference videos, billed seconds = input video length + output length; 15 seconds per reference video are reserved at submission and settled against the actual length on completion.
  • Reference audio and asset:// library references are not supported; the -lm models do not support watermark: true. Such requests return 400 immediately and are not billed.
  • Duration, resolution or ratio outside the ranges above returns 400 at submission and is not billed.
  • Video links can be downloaded for 24 hours from submission — save them to your own storage promptly.

Billing

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

Some models are priced per resolution tier (e.g. 480p / 720p / 1080p for grok-imagine-video-1.5, 480p / 768p for minimax-h3-max). The per-second price of each tier and the default tier are listed separately in the model catalog.

Failed tasks and error codes

When a task fails (status is failed), the query response tells you why and what to do. Failed tasks are never billed; any reserved amount is refunded automatically.

You can also review failures under Usage Logs in the console: filter by failed requests, or search by task ID (mvt-… / local-reserve-…) or request ID in the extra filters. The message there matches the API response and shows whether the charge was refunded.

  • OpenAI video format (/v1/video/generations/{id}, /v1/videos/{id}): error holds the reason and error_code the machine-readable code. If error_code is absent, read error.
  • Seedance (Volcengine Ark-compatible format): error.message holds the reason; error.code is one of the codes below, or the official Ark code (InvalidParameter.*, *SensitiveContentDetected.*) when there is one.
  • If the submission itself is rejected, the same code and reason come back immediately in the error response.
error_code Meaning What to do
content_moderation_rejected The input image / video / text, or the generated result, was rejected by content moderation Replace the material or revise the prompt. Resubmitting the same content will be rejected again
video_download_failed A reference image / video URL could not be downloaded Make sure the URL is publicly accessible: no login, no expired signature, no hotlink protection or IP restriction
invalid_parameter / InvalidParameter A request parameter is invalid (the message says which one), e.g. an image_url that is too long because base64 was put in it Fix the parameter and resubmit
video_processing_error / video_processing_timeout Temporary error or timeout on the model service Resubmit as is; nothing needs to change
video_submit_timeout The submission timed out and could not be confirmed Resubmit as is
concurrent_task_limit Too many tasks are running for this model Wait for running tasks to finish, then resubmit
service_temporarily_unavailable The service is temporarily unavailable Retry later
video_generation_failed Any other failure (the message includes the error code when there is one) Retry; if it keeps failing, contact support with the task id

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.