WorldRouter

Skip to Content
Media GenerationVideosSeedance video

Generate Videos with Seedance

What you can do

Use WorldRouter’s native async task API for text-to-video, video-to-video, and image-guided Seedance generation.

Before you start

Seedance uses WorldRouter’s native async task API. Create a task, then check its status until the video is ready.

  • Send text-only and reference-video requests directly.
  • Upload reference images first, then use the returned asset URL in your request.

Task routes

  • POST /api/v3/contents/generations/tasks
  • GET /api/v3/contents/generations/tasks/{task_id}

Asset helper routes

Use these routes only for reference-image uploads:

  • POST /v1/asset-groups
  • POST /v1/asset-groups/{asset_group_id}/assets
Tip:

Start from Quickstart if you still need an API key or the base URL. Use the same origin shown there: https://inference-api-pre-d80ca3.worldrouter.ai. Seedance task routes append /api/v3/... and are not part of the OpenAI-compatible /v1/videos surface, so do not nest them under /v1.

Quick start flow

The asset upload step depends on what kind of reference you pass:

  • Text-only or with a video reference: skip the asset step. Send the request directly.
  • Seedance with a public image reference URL: send the public URL directly with a basic Seedance model.
  • Seedance with uploaded assets: use a Seedance asset model and create the asset group with provider: "byteplus_official".
  • Portrait Library references: create a reusable portrait://... asset once, then prepare its Seedance binding before using it with a Seedance asset model. Direct API requests also require a ready BytePlus binding.
  1. 1If you use a basic Seedance model with a public image URL, skip the asset step. For uploaded assets, create an asset group with provider: "byteplus_official" and upload the image.
  2. 2Before using a Portrait Library reference, prepare its Seedance binding. In Playground, prepare the portrait in Portrait Library; direct API requests can use the binding route below.
  3. 3Create a Seedance task with POST /api/v3/contents/generations/tasks.
  4. 4Check task status with GET /api/v3/contents/generations/tasks/{task_id} until the job reaches a terminal state.
  5. 5Read content.video_url from the task response and download the generated video from that URL.

Supported request models

These are the request model names you should send in the model field:

ModelUpstreamBest for
seedance-2.0BytePlusStandard text-to-video.
seedance-2.0-fastBytePlusFaster iterations.
seedance-2.5BytePlusSeedance 2.5 text-to-video.
seedance-2.5-assetBytePlusSeedance 2.5 asset-reference video.
seedance-2.0-assetBytePlusAsset-reference video.
seedance-2.0-asset-fastBytePlusFaster asset-reference video.

These unqualified IDs are the public model names shown in the model catalog and Playground. The previous *-official request IDs remain accepted as backward-compatible aliases; use the names in the table for new integrations. New Seedance tasks and asset operations use BytePlus. Existing Relay task IDs remain pollable and settle at their historical rates, but Relay is not a currently offered Seedance provider.

Important behavior:

For text-only generation or reference-video generation, send your request directly with one of the basic model names above.

If you are using a reference image:

  • Basic Seedance models can use public image URLs directly.
  • Seedance asset models use the asset helper routes with provider: "byteplus_official", then use the returned asset://... URL with seedance-2.0-asset, seedance-2.0-asset-fast, or seedance-2.5-asset. The byteplus_official value is the asset API’s provider enum.
  • Seedance 2.5 supports 480p, 720p, and 1080p outputs. Duration may be -1 for automatic duration or an integer from 4 through 30. It does not accept camera_fixed.
  • Seedance 2.5 also accepts output_format, generate_audio, return_last_frame, priority, execution_expires_after, and callback_url.

When you use an uploaded image asset, use one of the Seedance asset model names.

Text-to-video

Start here if you want the simplest Seedance workflow.

This example shows the basic text-to-video flow: send a prompt, create a task, then check its progress until the video is ready.

curl
curl -X POST "https://inference-api-pre-d80ca3.worldrouter.ai/api/v3/contents/generations/tasks" \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{
  "model": "seedance-2.0",
  "content": [
    {
      "type": "text",
      "text": "A cinematic video of a city street at sunset."
    }
  ],
  "resolution": "720p",
  "duration": 5
}'

You can start with this example as-is, then adjust the parts that matter most for your use case:

  • model: choose a standard or faster Seedance generation path.
  • text: describe the subject, style, setting, and motion you want.
  • resolution: choose the output quality supported by the model.
  • duration: choose how long the generated video should be.

Typical create response:

200 OK
{
"id": "task-123",
"requestId": "req-123"
}

This response confirms that the task was created successfully. Your video is not ready yet.

Save the returned task id, then continue to Check task status.

Check task status

All Seedance requests follow the same async flow after task creation, including text-to-video, reference-video, and reference-image requests.

Use the task id returned from the create response to check whether the video is still running, has succeeded, or has failed.

curl
curl "https://inference-api-pre-d80ca3.worldrouter.ai/api/v3/contents/generations/tasks/task-123" \
-H "Authorization: Bearer your_api_key"

Typical status response:

200 OK
{
"id": "task-123",
"model": "seedance-2.0",
"status": "succeeded",
"content": {
  "video_url": "https://media.example.com/seedance/output.mp4"
},
"resolution": "720p",
"duration": 5,
"usage": {
  "output_tokens": 7522
}
}

When status becomes succeeded, use content.video_url to download the final video.

WorldRouter normalizes upstream task states into:

  • queued
  • running
  • succeeded
  • failed
  • cancelled
  • expired

A failed task also carries an error object with the upstream code and a bounded message. When the failure is one WorldRouter can explain, the object adds a guidance field describing what to change.

Use a reference video

Reference videos can be sent directly in the request. Unlike image references, they do not require the asset helper flow.

JSON body
{
"model": "seedance-2.0",
"content": [
  {
    "type": "text",
    "text": "Keep the motion rhythm of the input video and restyle it into a cinematic tea scene."
  },
  {
    "type": "video_url",
    "role": "reference_video",
    "video_url": {
      "url": "https://example.com/input.mp4"
    }
  }
],
"resolution": "720p",
"duration": 5
}

You can customize the same core fields here as well:

  • text: describe the style or transformation you want.
  • video_url: provide the reference video.
  • resolution: choose the output quality supported by the model.
  • duration: choose the target output length.

After submitting the request, follow the same steps in Check task status to monitor the task and download the result.

Asset flow for image references

Basic Seedance models can use public image URLs directly. Use the asset flow below when you want uploaded assets with seedance-2.0-asset, seedance-2.0-asset-fast, or seedance-2.5-asset.

Portrait Library

Portrait Library is WorldRouter’s reusable character asset layer. Seedance asset models use virtual portraits in later video requests. Portrait creation stores a generic WorldRouter asset and does not create a Seedance provider asset immediately. Prepare the Seedance binding in Portrait Library before using the portrait with an asset model in Playground. Playground reads the ready binding and submits its asset://... reference to the inference task route. Direct API requests also require a ready BytePlus binding before the portrait can be used. Relay bindings are not available for new materialization.

The first supported portrait type is virtual, for AI-generated or otherwise non-real-human characters. Real-human portraits require provider verification and are intentionally rejected by this route until the provider verification flow is enabled. Portrait images are stored in WorldRouter object storage. Local uploads are copied directly into the durable portrait prefix; URL imports are fetched once, copied into the same storage, and no longer depend on the original host for later previews or provider materialization.

Create a virtual portrait from a local image:

curl
curl -X POST "https://inference-api-pre-d80ca3.worldrouter.ai/v1/portraits" \
-H "Authorization: Bearer your_api_key" \
-H "Idempotency-Key: portrait-upload-luna-1" \
-F "name=Luna" \
-F "kind=virtual" \
-F "description=AI-generated character reference" \
-F "image=@./luna.png"

You can also import a portrait from an HTTP(S) image URL:

curl
curl -X POST "https://inference-api-pre-d80ca3.worldrouter.ai/v1/portraits" \
-H "Authorization: Bearer your_api_key" \
-H "Idempotency-Key: portrait-create-luna-1" \
-H "Content-Type: application/json" \
-d '{
  "name": "Luna",
  "kind": "virtual",
  "image_url": "https://example.com/luna.png",
  "description": "AI-generated character reference"
}'

If you call the backend directly, prepare the Seedance binding before submitting the asset generation request. Use provider: "byteplus_official":

curl
curl -X POST "https://inference-api-pre-d80ca3.worldrouter.ai/v1/portraits/portrait_123/bindings/seedance" \
-H "Authorization: Bearer your_api_key" \
-H "Idempotency-Key: portrait-binding-seedance-1" \
-H "Content-Type: application/json" \
-d '{
  "provider": "byteplus_official"
}'

Once the binding is ready, use the returned asset_url or the portrait://... reference in the matching asset generation request. The backend resolves a ready portrait reference to asset://.... Playground also converts a ready Seedance portrait reference before submitting the task:

JSON body
{
"model": "seedance-2.0-asset",
"content": [
  {
    "type": "text",
    "text": "Luna walks through a sunlit city street."
  },
  {
    "type": "image_url",
    "role": "reference_image",
    "image_url": {
      "url": "asset://asset_123"
    }
  }
],
"resolution": "720p",
"duration": 5
}

1. Create an asset group

curl
curl -X POST "https://inference-api-pre-d80ca3.worldrouter.ai/v1/asset-groups" \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{
  "name": "seedance-demo-group",
  "description": "reference assets for a Seedance run",
  "provider": "byteplus_official"
}'

Response:

200 OK
{
"id": "group-1",
"name": "seedance-demo-group",
"description": "reference assets for a Seedance run",
"provider": "byteplus_official",
"requestId": "req-456"
}

2. Upload the reference image into that group

curl
curl -X POST "https://inference-api-pre-d80ca3.worldrouter.ai/v1/asset-groups/group-1/assets" \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{
  "name": "reference-image",
  "description": "main character reference",
  "type": "image",
  "url": "https://example.com/reference.png"
}'

You may also pass moderation_strategy as Default or Skip, and project_name if your BytePlus project requires it.

Response:

200 OK
{
"id": "asset-1",
"asset_group_id": "group-1",
"name": "reference-image",
"description": "main character reference",
"type": "image",
"url": "asset://asset-1",
"source_url": "https://example.com/reference.png",
"provider": "byteplus_official",
"requestId": "req-789"
}

3. Use asset://... in the generation payload

For uploaded assets, use the matching Seedance asset model. This example uses Seedance 2.0; use seedance-2.5-asset for Seedance 2.5 assets:

Seedance asset JSON body
{
"model": "seedance-2.0-asset",
"asset_group_id": "group-1",
"content": [
  {
    "type": "text",
    "text": "The reference object moves through a cinematic studio scene."
  },
  {
    "type": "image_url",
    "role": "reference_image",
    "image_url": {
      "url": "asset://asset-1"
    }
  }
],
"resolution": "720p",
"duration": 5
}

After submitting the request, follow Check task status to monitor the task and download the result.

Notes and limits

  • Seedance requires a team-scoped key with enough available credits. Low balance returns 402 seedance_balance_too_low.
  • Pending Seedance jobs per user are capped. Too many running tasks return 429 seedance_too_many_pending_tasks.
  • Task reads are owner-scoped. You can only poll tasks created by the same authorized owner context.
  • Asset groups and uploaded assets are also owner-scoped.
  • New Seedance task and asset creation uses BytePlus. Existing historical Relay task IDs remain pollable through the status route and settle at their recorded rates.
  • The public task status endpoint returns metadata and content.video_url; there is no separate public /v1/videos/{id}/content download route for Seedance.

Troubleshooting

SymptomLikely causeWhat to do
400 unsupported_modelThe model ID is unsupported for this endpointUse one of the public Seedance model IDs from the table above on POST /api/v3/contents/generations/tasks.
400 with “may contain real person”The provider’s content filter read a reference image as a photo of a real personUse an illustrated or AI-generated character reference. Real-person likenesses require provider authorization that is not enabled on this account. The error.guidance field repeats this.
403 AccessDenied from BytePlus asset helpersThe BytePlus AK/SK lacks asset-library permissionsGrant the key actions such as ark:CreateAssetGroup, ark:CreateAsset, and ark:GetAsset.
402 seedance_balance_too_lowThe scoped team does not have enough available creditsRecharge on Credits and retry.
403 media_resource_access_deniedThe task, asset group, or asset belongs to another owner contextReuse the same account/team/API key that created the resource.
429 seedance_too_many_pending_tasksToo many of your Seedance tasks are still runningWait for active tasks to finish before creating more.
424 with an error bodyThe upstream video provider failed or timed outTransient provider failure — retry after a short wait. The body keeps the upstream error and upstream_status.

See also