Language
Inference Space Docs

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 IDNotes
doubao-seedance-2-0-260128Seedance 2.0
doubao-seedance-2-0-miniSeedance 2.0 Mini
doubao-seedance-2-5-260628Seedance 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
MethodEndpointDescription
POST/api/v3/contents/generations/tasksCreate a video task
GET/api/v3/contents/generations/tasks/{task_id}Get a video task
POST/api/material?Action={Action}&Version=2024-01-01Material 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.

FieldTypeRequiredDescription
modelstringYesA model ID from the table above
contentarrayYesText, image, video, and audio inputs; see below
resolutionstringNoFor example 480p, 720p, or 1080p. Send it explicitly — the billing tier follows the requested resolution
ratiostringNoFor example 16:9, 9:16, or 1:1
durationintegerNoOutput length in seconds
generate_audiobooleanNoWhether the output video has audio
watermarkbooleanNoAdd a watermark
seedintegerNoRandom seed
camera_fixedbooleanNoTry to keep the camera fixed
return_last_framebooleanNoReturn content.last_frame_url on success
omni_reference_task_typestringNoRecommended value reference when generating from reference images with Seedance 2.5
framesintegerNoTotal output frame count
draftbooleanNoDraft mode
service_tierstringNoService tier
execution_expires_afterintegerNoTask lifetime in seconds
toolsobject[]NoModel tool configuration
safety_identifierstringNoBusiness trace identifier; never put secrets here

content items

typeFieldAllowed roleDescription
texttext—Prompt
image_urlimage_url.urlreference_image, first_frame, last_framePublic image URL or an asset://asset-... reference
video_urlvideo_url.urlreference_videoPublic video URL or an asset reference
audio_urlaudio_url.urlreference_audioPublic 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
}
statusDescription
queuedWaiting
runningGenerating
succeededDone; get the video from content.video_url
failedFailed; see error
  • content.video_url expires; 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. 480p and 720p share one tier; 1080p and 4K each have their own. Requests whose content includes a video_url input 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"}'
ActionBodyDescription
CreateAssetGroupName (required), Description, ModelCreate a group; returns Result.Id (for example group-20260623180314-r2rc7)
GetAssetGroupIdGet a group
ListAssetGroupsPageNumber (default 1), PageSize (default 10, max 100), Filter.GroupTypeItems in Result.Items, plus TotalCount
UpdateAssetGroupId, Name, DescriptionUpdate the name or description; an empty Description clears it
DeleteAssetGroupIdDelete the group and its asset records
CreateAssetGroupId (required), AssetType (required: Image / Video / Audio), URL (required, public), Name, ModelCreate an asset; returns Result.Id (for example asset-20260623180317-8sxkd)
GetAssetIdReturns Status, AssetType, URL, Error, and more
ListAssetsPageNumber, PageSize, Filter.GroupIds, Filter.AssetType, Filter.Statuses, Filter.NameFilter by group, type, status, or name
UpdateAssetId, NameRename an asset
DeleteAssetIdDelete an asset

Asset states:

StatusDescription
ProcessingBeing processed or reviewed; not usable yet
ActiveReady for video tasks
FailedProcessing 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 CreateAsset does not mean the asset is ready. Wait for Status to become Active before submitting a video task.
  • Assets are bound to a model. Pass Model when creating groups and assets, using the same value you will send as the task model. 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. Without Model, 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:

ActionBodyReturns
CreateVisualValidateSession{}BytedToken (for fetching the result), H5Link (validation page for the person), QrCode, ExpiresIn (seconds)
GetVisualValidateResultBytedTokenAfter 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": "..."}}:

HTTPerror.codeMeaning and action
400invalid_requestThe body is not valid JSON
400missing_modelmodel is missing
400model_not_available_for_routingmodel is not a video model; check the spelling
400model_not_advertisedThe model is not enabled; message lists the models that are
401—Missing or invalid API key
403access_deniedThe key lacks the ai:video scope
403model_not_authorizedThe model is not enabled for your organization; contact your administrator
404not_foundThe task does not exist or belongs to another organization
503video_not_configured / service_unavailableNo video service is available right now; retry later or contact support
502 / 504upstream_errorThe 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

  1. Video tasks and assets are asynchronous. Decide readiness only from status / Status, never from a successful create call.
  2. 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.
  3. content.video_url expires; store the video as soon as you receive it.
  4. Keep the API key out of frontend code and logs.

See Authentication and API Keys and Model Discovery and Catalog.

On this page