Video Generation and Material Library API
The Seedance native task API — create video tasks, poll for results, and register reusable reference materials in the material library.
This guide is for developers generating video with Seedance (doubao-seedance-*). The API keeps Seedance's native task format (a content array plus task polling), so existing native-format code only needs a new Base URL and API key.
Hailuo, Grok Imagine, Veo, and similar models use the OpenAI-style /v1/videos API; see Other video models.
Choose a model
| Model ID | Notes |
|---|---|
doubao-seedance-2-0-260128 | Seedance 2.0 |
doubao-seedance-2-0-mini | Seedance 2.0 Mini |
doubao-seedance-2-5-260628 | Seedance 2.5; currently supports 480p and 720p. When referencing library assets, the material group must be created for 2.5 (see Material library) |
Which models your organization can use is shown in the console. Sending a model that is not enabled returns model_not_advertised, and message lists the models that are.
Quick start: submit → poll → download
The API key needs the ai:video (or ai:*) scope; see Authentication. Keep the key on your server.
# China region, accelerated route; China international route is https://global.inf.space, Global region is https://ai.inf.space
export BASE_URL="https://cn.inf.space"
# 1. Create a task (a public image used directly as a reference)
curl -X POST "$BASE_URL/api/v3/contents/generations/tasks" \
-H "Authorization: Bearer $INFERENCE_SPACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seedance-2-0-260128",
"content": [
{"type": "text", "text": "A natural, cinematic running shot with a stable camera; no subtitles or watermark"},
{"type": "image_url", "role": "reference_image", "image_url": {"url": "https://example.com/person.jpg"}}
],
"resolution": "720p",
"ratio": "16:9",
"duration": 5,
"watermark": false
}'
# → {"id": "cgt-20260731120000-example", ...}
# 2. Poll until status is succeeded or failed (every 5–10 seconds)
curl "$BASE_URL/api/v3/contents/generations/tasks/cgt-20260731120000-example" \
-H "Authorization: Bearer $INFERENCE_SPACE_API_KEY"
# 3. On success, download content.video_url
curl -L "$VIDEO_URL" -o output.mp4| Method | Endpoint | Description |
|---|---|---|
POST | /api/v3/contents/generations/tasks | Create a video task |
GET | /api/v3/contents/generations/tasks/{task_id} | Get a video task |
POST | /api/material?Action={Action}&Version=2024-01-01 | Material library and human validation (see below) |
The prompt goes in content[].text, not a top-level prompt, and model must be a non-empty string. Do not send this page's request body to /v1/videos; that API reads a top-level prompt and returns prompt 不能为空 (prompt must not be empty).
Create-task parameters
The request body is JSON. Apart from model and content, fields are passed to the model as-is; accepted values depend on the selected model.
| Field | Type | Required | Description |
|---|---|---|---|
model | string | Yes | A model ID from the table above |
content | array | Yes | Text, image, video, and audio inputs; see below |
resolution | string | No | For example 480p, 720p, or 1080p. Send it explicitly — the billing tier follows the requested resolution |
ratio | string | No | For example 16:9, 9:16, or 1:1 |
duration | integer | No | Output length in seconds |
generate_audio | boolean | No | Whether the output video has audio |
watermark | boolean | No | Add a watermark |
seed | integer | No | Random seed |
camera_fixed | boolean | No | Try to keep the camera fixed |
return_last_frame | boolean | No | Return content.last_frame_url on success |
omni_reference_task_type | string | No | Recommended value reference when generating from reference images with Seedance 2.5 |
frames | integer | No | Total output frame count |
draft | boolean | No | Draft mode |
service_tier | string | No | Service tier |
execution_expires_after | integer | No | Task lifetime in seconds |
tools | object[] | No | Model tool configuration |
safety_identifier | string | No | Business trace identifier; never put secrets here |
content items
type | Field | Allowed role | Description |
|---|---|---|---|
text | text | — | Prompt |
image_url | image_url.url | reference_image, first_frame, last_frame | Public image URL or an asset://asset-... reference |
video_url | video_url.url | reference_video | Public video URL or an asset reference |
audio_url | audio_url.url | reference_audio | Public WAV / MP3 URL or an asset reference |
First frame, last frame, and reference video:
[
{"type": "text", "text": "The person runs from the left side of the frame to the right"},
{"type": "image_url", "role": "first_frame", "image_url": {"url": "https://example.com/first.jpg"}},
{"type": "image_url", "role": "last_frame", "image_url": {"url": "https://example.com/last.jpg"}},
{"type": "video_url", "role": "reference_video", "video_url": {"url": "https://example.com/ref.mp4"}}
]Reference audio: use type=audio_url with role=reference_audio, up to 3 clips per request, together with at least one image or video input — audio alone is rejected. audio_url is input audio; generate_audio controls whether the output has audio. They are unrelated.
Every URL must be directly downloadable by the video service, without cookies, a login session, or private networking.
Get a task and the response
The created task object carries the task ID in id:
{"id": "cgt-20260731120000-example"}Save id and use it to poll. You can only query tasks your organization created through this API; any other ID returns 404.
Successful task response:
{
"id": "cgt-20260731120000-example",
"model": "doubao-seedance-2-0-260128",
"status": "succeeded",
"content": {
"video_url": "https://...",
"last_frame_url": "https://..."
},
"usage": {"completion_tokens": 100858, "total_tokens": 100858},
"resolution": "720p",
"ratio": "16:9",
"duration": 5,
"created_at": 1782208997,
"updated_at": 1782209105
}status | Description |
|---|---|
queued | Waiting |
running | Generating |
succeeded | Done; get the video from content.video_url |
failed | Failed; see error |
content.video_urlexpires; download and store the video soon after success.- Optional fields (
seed,frames,framespersecond,generate_audio,last_frame_url, and so on) vary by model, so handle missing values. - Billing uses
usage.completion_tokens.480pand720pshare one tier;1080pand4Keach have their own. Requests whosecontentincludes avideo_urlinput are billed at the video-input rate. See Pricing or the console for unit prices.
Material library
For one-off media, put the public URL directly in content. To reuse the same media repeatedly (for example, the same person), register it in the material library and reference it with asset://asset-....
Flow: CreateAssetGroup → CreateAsset → poll GetAsset until Status is Active → reference asset://asset-... in content.
Every material operation uses one endpoint, selected by Action, with a JSON body:
# China region, accelerated route; China international route is https://global.inf.space, Global region is https://ai.inf.space
curl -X POST "https://cn.inf.space/api/material?Action=CreateAssetGroup&Version=2024-01-01" \
-H "Authorization: Bearer $INFERENCE_SPACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"Name": "product-references", "Description": "product reference assets"}'| Action | Body | Description |
|---|---|---|
CreateAssetGroup | Name (required), Description, Model | Create a group; returns Result.Id (for example group-20260623180314-r2rc7) |
GetAssetGroup | Id | Get a group |
ListAssetGroups | PageNumber (default 1), PageSize (default 10, max 100), Filter.GroupType | Items in Result.Items, plus TotalCount |
UpdateAssetGroup | Id, Name, Description | Update the name or description; an empty Description clears it |
DeleteAssetGroup | Id | Delete the group and its asset records |
CreateAsset | GroupId (required), AssetType (required: Image / Video / Audio), URL (required, public), Name, Model | Create an asset; returns Result.Id (for example asset-20260623180317-8sxkd) |
GetAsset | Id | Returns Status, AssetType, URL, Error, and more |
ListAssets | PageNumber, PageSize, Filter.GroupIds, Filter.AssetType, Filter.Statuses, Filter.Name | Filter by group, type, status, or name |
UpdateAsset | Id, Name | Rename an asset |
DeleteAsset | Id | Delete an asset |
Asset states:
Status | Description |
|---|---|
Processing | Being processed or reviewed; not usable yet |
Active | Ready for video tasks |
Failed | Processing or review failed; see Error |
Reference an asset in a video task:
{"type": "image_url", "role": "reference_image", "image_url": {"url": "asset://asset-20260623180317-8sxkd"}}- An asset ID returned by
CreateAssetdoes not mean the asset is ready. Wait forStatusto becomeActivebefore submitting a video task. - Assets are bound to a model. Pass
Modelwhen creating groups and assets, using the same value you will send as the taskmodel. Seedance 2.5 requires"Model": "doubao-seedance-2-5-260628"; assets registered for 2.0 are rejected by 2.5 tasks and must be registered again for 2.5. WithoutModel, the platform picks a model available to the account. - The
asset://prefix is lowercase. Only reference assets registered by your own organization.
Response format
Material responses use this envelope (excerpt). Log ResponseMetadata.RequestId and include it when reporting issues.
{
"ResponseMetadata": {"RequestId": "2026062317822087486485654725234569", "Action": "CreateAssetGroup", "Version": "2024-01-01"},
"Result": {"Id": "group-20260623180314-r2rc7"}
}On failure, Result.Error carries Code and Message. Some business errors return HTTP 200, so check both the HTTP status and Result.Error.
Human validation
Only for accounts with human validation enabled. These also use the /api/material endpoint:
| Action | Body | Returns |
|---|---|---|
CreateVisualValidateSession | {} | BytedToken (for fetching the result), H5Link (validation page for the person), QrCode, ExpiresIn (seconds) |
GetVisualValidateResult | BytedToken | After successful validation, Result.GroupId is the material group the account can use |
CreateRealValidateH5 | {} | Management page Result.H5Link and Result.ExpiresIn |
Sessions and links expire; save the returned values and recreate them when needed.
Errors
Video task errors look like {"error": {"code": "...", "message": "..."}}:
| HTTP | error.code | Meaning and action |
|---|---|---|
| 400 | invalid_request | The body is not valid JSON |
| 400 | missing_model | model is missing |
| 400 | model_not_available_for_routing | model is not a video model; check the spelling |
| 400 | model_not_advertised | The model is not enabled; message lists the models that are |
| 401 | — | Missing or invalid API key |
| 403 | access_denied | The key lacks the ai:video scope |
| 403 | model_not_authorized | The model is not enabled for your organization; contact your administrator |
| 404 | not_found | The task does not exist or belongs to another organization |
| 503 | video_not_configured / service_unavailable | No video service is available right now; retry later or contact support |
| 502 / 504 | upstream_error | The video service was unreachable or timed out; retry later |
When parameters are invalid (for example, audio without an image or video, or a resolution the model does not support), the video service's validation error is returned with its original status code and reason.
Material errors appear in Result.Error.Code: AccessDenied (403, the key lacks the ai:video scope), ServiceUnavailable (503, no material service available), or a business error from the material service.
If polling fails, simply retry the poll; do not create a new task because a poll failed.
Python example
Register an asset → wait until ready → create a task → poll for the result. Requires Python 3.9+ and requests. If you do not need to reuse media, skip the material steps and put the public URL directly in image_url.url.
import os
import time
import requests
# China region, accelerated route; China international route is https://global.inf.space, Global region is https://ai.inf.space
BASE_URL = os.getenv("INFERENCE_SPACE_BASE_URL", "https://cn.inf.space")
MODEL = os.getenv("INFERENCE_SPACE_VIDEO_MODEL", "doubao-seedance-2-0-260128")
HEADERS = {"Authorization": f"Bearer {os.environ['INFERENCE_SPACE_API_KEY']}"}
def material(action, payload=None):
r = requests.post(
f"{BASE_URL}/api/material",
params={"Action": action, "Version": "2024-01-01"},
headers=HEADERS, json=payload or {}, timeout=30,
)
r.raise_for_status()
result = r.json().get("Result") or {}
if (result.get("Error") or {}).get("Code"):
raise RuntimeError(f"{action} failed: {result['Error']}")
return result
def poll(fetch, done, failed, timeout=900, interval=5):
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
item = fetch()
if done(item):
return item
if failed(item):
raise RuntimeError(item)
time.sleep(interval)
raise TimeoutError("polling timed out")
def main():
group = material("CreateAssetGroup", {"Name": "person-references", "Model": MODEL})
asset = material("CreateAsset", {
"GroupId": group["Id"],
"Model": MODEL,
"AssetType": "Image",
"URL": os.environ["PERSON_IMAGE_URL"],
})
poll(lambda: material("GetAsset", {"Id": asset["Id"]}),
done=lambda a: a.get("Status") == "Active",
failed=lambda a: a.get("Status") == "Failed")
r = requests.post(
f"{BASE_URL}/api/v3/contents/generations/tasks",
headers=HEADERS, timeout=30,
json={
"model": MODEL,
"content": [
{"type": "text", "text": "A stable running shot in natural light; no subtitles"},
{"type": "image_url", "role": "reference_image",
"image_url": {"url": f"asset://{asset['Id']}"}},
],
"resolution": "720p",
"ratio": "16:9",
"duration": 5,
"watermark": False,
},
)
r.raise_for_status()
task_id = r.json()["id"]
def get_task():
resp = requests.get(f"{BASE_URL}/api/v3/contents/generations/tasks/{task_id}",
headers=HEADERS, timeout=30)
resp.raise_for_status()
return resp.json()
task = poll(get_task,
done=lambda t: t.get("status") == "succeeded",
failed=lambda t: t.get("status") == "failed")
print(task["content"]["video_url"])
if __name__ == "__main__":
main()Integration checklist
- Video tasks and assets are asynchronous. Decide readiness only from
status/Status, never from a successful create call. - Store group IDs, asset IDs, validation tokens, and task IDs for reuse, polling, and cleanup. Before deleting a group, confirm no workflow still references its assets.
content.video_urlexpires; store the video as soon as you receive it.- Keep the API key out of frontend code and logs.
See Authentication and API Keys and Model Discovery and Catalog.
Vision Segmentation (SAM3 Image/Video)
POST /v1/vision-segment/predictions for image segmentation and POST /v1/vision-segment/video for video segmentation (SAM3), returning masks, overlays, boxes, and confidence scores from text, point, or box prompts.
Other video models
Generate videos with Hailuo 3, Grok Imagine 1.5, Veo 3.1, and Gemini Omni Flash through the /v1/videos async task API — model selection, size and duration enumerations, reference images/audio, polling, and download.