ModelArk Docs
English Assets API

Private virtual portrait library

ModelArk · Seedance 2.0 & 2.5 · Asset management for consistent characters in video generation
docs.byteplus.com/en/docs/ModelArk/2333565 ↗

Warning
  • The upload asset (CreateAsset) API is asynchronous. System processing may result in queuing, which can increase the time required for storage; higher latency is expected for uploading video.
  • SLA for uploading time is not guaranteed.
  • You must ensure that the uploaded virtual portrait meets the following conditions:
    • You legally own the asset and have full rights to use and dispose of it. The asset does not contain unauthorized third-party trademarks or logo content.
    • The asset must not resemble any real human person's portrait, must not be plagiarized or misappropriated, and must not infringe on any third party's portrait rights.
    • The asset does not contain content that violates regulations, contravenes public order and morals, or infringes on the legal rights of third parties.

The Dreamina Seedance 2.0 (Seedance 2.0) and Dreamina Seedance 2.5 (Seedance 2.5) models have comprehensive multi-modal capabilities. To ensure creators can fully leverage the powerful video generation capabilities of Seedance 2.0 and 2.5, you can upload virtual portraits to a private asset library for more stable video generation results.

The workflow for using the private asset library is as follows:

Workflow: Activate Advanced Creation Rights → Create asset group → Upload asset → Get Asset ID → Use in video generation

Workflow: Activate Advanced Creation Rights → Create asset group → Upload asset → Get Asset ID → Use in video generation

Prerequisites

To access the full features of the Asset Library, you need to activate the Advanced Creation Rights. Refer to Dreamina Seedance Advanced Creation Rights Purchase Guide.

Structure of the asset library

Tip
  • Only assets required for inference need to be added to the library; do not add assets that are not required for inference.
  • Only the Asset ID of assets already added to the library can be used for video generation; assets for regular image/video generation do not need to be added to the library.
  • Each uploaded asset must be preprocessed. You can poll the GetAsset API to check the asset status (the asset can only be used for video generation when the status is Active).

Taking uploading a character image asset as an example:

Single image file format requirements:

To ensure that the character's face, clothing details, and other features in the generated video are consistent with the reference image, we recommend providing the following types of reference images for the virtual portrait:

Best practices for portrait asset content:

Full-body reference image requirements

Example: full-body reference image (vertical, frontal)

Example: full-body reference image (vertical, frontal)

Facial close-up image requirements

Example: facial close-up reference image (vertical, frontal, above shoulders)

Example: facial close-up reference image (vertical, frontal, above shoulders)

Assets API list

Note
To call the Assets API interface, you must use Access Key authentication. For details, refer to Obtaining Access Keys.

Asset (group) creation

APIDescription
POSTCreateAssetGroupCreate an asset group. When creating an asset group for the first time, you must specify GroupType. Once set, this field cannot be changed.
POSTCreateAssetCreate an asset. This interface can be used to upload personal assets. After creating an asset, call GetAsset to check if the asset has been processed successfully.

Asset (group) management

Rate limits

Tip
  • QPS: Maximum total request count per second; request will return an error when this limit is exceeded.
  • QPM: Maximum total request count per minute; requests will return an error when this limit is exceeded.
APIRate limits by account
CreateAssetGroup10 QPS
CreateAssetAPI rate limits vary by rights type. For detailed limits, see Dreamina Seedance Advanced Creation Rights Purchase Guide.
ListAssetGroups10 QPS
ListAssets10 QPS
GetAsset100 QPS
GetAssetGroup10 QPS
UpdateAsset10 QPS
UpdateAssetGroup10 QPS
DeleteAsset10 QPS
DeleteAssetGroup5 QPS

Tutorial

Upload assets to the virtual portrait library (API & UI console)

You can upload your own virtual portraits to the private asset library.

Tip
You must ensure that the uploaded virtual portrait meets the following conditions:
  • You legally own the asset and have full rights to use and dispose of it. The asset does not contain unauthorized third-party trademarks or logo content.
  • The asset must not resemble the portrait or likeness of any natural person, must not involve plagiarism or misappropriation of any natural person's portrait or likeness.
  • The asset does not contain content that violates laws and regulations, public order and good morals, or infringes on the legal rights of third parties.

ModelArk will conduct a security review of the assets you upload. Once approved, you can use the asset for video generation.

You can use OpenAPI interfaces or upload assets in the Model Playground.

Use the console

Open Model Playground > My assets > Virtual Portrait > Manage assets.

  1. Create an asset group.
  2. Upload assets to the asset group.
ModelArk Console — My assets → Virtual Portrait → Manage assets

ModelArk Console — My assets → Virtual Portrait → Manage assets

Use the API

CreateAssetGroup — Create an asset group

Tip

Use the POST CreateAssetGroup API to create an asset group. Include the following fields in the request body:

Tip
If ProjectName is not specified in the request, the asset group will be created in the default project by default. You need to use this field to ensure the asset group is created in the correct project.
Note
AK/SK authentication is required. For details, see API access key management.
GO CreateAssetGroup Request
package main


import (
"fmt"


"github.com/bytedance/sonic"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/credentials"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/session"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/universal"
)


func main () {
config := byteplus.NewConfig().WithCredentials(credentials.NewStaticCredentials( "<your_ak>" , "<your_sk>" , "" )).WithRegion( "ap-southeast-1" )
sess, _ := session.NewSession(config)
resp, err := universal.New(sess).DoCall(
universal.RequestUniversal{
ServiceName: "ark" ,
Action: "CreateAssetGroup" ,
Version: "2024-01-01" ,
HttpMethod: universal.POST,
ContentType: universal.ApplicationJSON,
},
& map [ string ]any{
"Name" : "<GROUP_NAME>" ,
"Description" : "<GROUP_DESC>" ,
"ProjectName" : "default" ,
},
)
if err != nil {
fmt.Printf( "error: %v\n" , err)
return
}
if resp == nil {
return
}
respData, err := sonic.Marshal(resp)
fmt.Println( string (respData))
JSON Response
{
"Id" : "group-20260318033332-*****"

CreateAsset — Upload an asset

Use the POST CreateAsset API to upload an asset.

Tip
The CreateAsset API is asynchronous. Processing may be queued, which can increase ingestion time. Upload-time SLAs are not guaranteed.

Provide the following in the request:

Tip
If ProjectName is not specified in the request, the asset will be uploaded to the default project by default.

Notes:

GO CreateAsset Request
package main


import (
"fmt"


"github.com/bytedance/sonic"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/credentials"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/session"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/universal"
)


func main () {
config := byteplus.NewConfig().WithCredentials(credentials.NewStaticCredentials( "<YOUR_AK>" , "<YOUR_SK>" , "" )).WithRegion( "ap-southeast-1" )
sess, _ := session.NewSession(config)
resp, err := universal.New(sess).DoCall(
universal.RequestUniversal{
ServiceName: "ark" ,
Action: "CreateAsset" ,
Version: "2024-01-01" ,
HttpMethod: universal.POST,
ContentType: universal.ApplicationJSON,
},
& map [ string ]any{
"GroupId" : "group-20260324142802-f7wmx" ,
"URL" : "<IMAGE_URL>" ,
"AssetType" : "Image" ,
"Moderation" : map [ string ]any{ // Skip the Content Pre-filter review, must turn off content pre-filter on the console first
"Strategy" : "Skip" ,
},
"ProjectName" : "default" ,
},
)
if err != nil {
fmt.Printf( "error: %v\n" , err)
return
}
if resp == nil {
return
}
respData, err := sonic.Marshal(resp)
fmt.Println( string (respData))
JSON Response
{
"Id" : "asset-20260318071009-*****"

Retrieve virtual portraits (API & Console)

You can retrieve virtual portrait assets using the following methods.

Retrieve information of a single asset

You can use POST GetAsset to retrieve information for a single asset by specifying the asset ID.

Tip
To obtain complete API parameters, rate limits, and other information, see Virtual portrait library API reference.
GO GetAsset Request
package main


import (
"fmt"


"github.com/bytedance/sonic"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/credentials"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/session"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/universal"
)


func main () {
config := byteplus.NewConfig().WithCredentials(credentials.NewStaticCredentials( "<YOUR_AK>" , "<YOUR_SK>" , "" )).WithRegion( "ap-southeast-1" )
sess, _ := session.NewSession(config)
resp, err := universal.New(sess).DoCall(
universal.RequestUniversal{
ServiceName: "ark" ,
Action: "GetAsset" ,
Version: "2024-01-01" ,
HttpMethod: universal.POST,
ContentType: universal.ApplicationJSON,
},
& map [ string ]any{
"Id" : "asset-20260324143042-*****" ,
"ProjectName" : "default" , // Make sure to specify the correct project name where the asset is stored
},
)
if err != nil {
fmt.Printf( "error: %v\n" , err)
return
}
if resp == nil {
return
}
respData, err := sonic.Marshal(resp)
fmt.Println( string (respData))
JSON Response
{
"GroupId" : "group-20260318033332-7vw4m" ,
"Status" : "Active" ,
"Moderation" : {
"Strategy" : "Default"
},
"CreateTime" : "2026-03-18T03:57:10Z" ,
"AssetType" : "Image" ,
"UpdateTime" : "2026-03-18T03:57:14Z" ,
"LastInferenceTime" : "2026-07-24T10:08:49+08:00" ,
"ProjectName" : "default" ,
"Id" : "asset-20260318035710-*****" ,
"Name" : "" ,
"URL" : "https://ark-media-asset-ap-southeast-1.tos-ap-southeast-1.volces.com/300060****/03301616086559****.jpg?X-Tos-Algorithm=***********"
Tip
LastInferenceTime indicates the most recent time when an asset was used to submit a video generation task. You can periodically check LastInferenceTime and proactively delete assets that have not been used for a long time to save storage costs.

Query asset information

You can use POST ListAssets to query assets. Supports:

Tip
ListAssets supports NextToken pagination and page-number pagination. For pagination methods, parameters, and examples, see Virtual portrait library API reference.
GO ListAssets Request
package main


import (
"fmt"


"github.com/bytedance/sonic"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/credentials"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/session"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/universal"
)


func main () {
config := byteplus.NewConfig().WithCredentials(credentials.NewStaticCredentials( "<your_ak>" , "<your_sk>" , "" )).WithRegion( "ap-southeast-1" )
sess, _ := session.NewSession(config)
resp, err := universal.New(sess).DoCall(
universal.RequestUniversal{
ServiceName: "ark" ,
Action: "ListAssets" ,
Version: "2024-01-01" ,
HttpMethod: universal.POST,
ContentType: universal.ApplicationJSON,
},
& map [ string ]any{
"Filter" : map [ string ]any{
"GroupIds" : [] string { "group-20260324142802-*****" },
"GroupType" : "AIGC" ,
"Statuses" : [] string { "Active" , "Processing" }, // Supported statuses: Active (Upload successfully, Asset ID available for use), Processing, Failed
},
"MaxResults" : 2 ,
"SortBy" : "CreateTime" ,
"SortOrder" : "Desc" ,
},
)
if err != nil {
fmt.Printf( "list assets error: %v\n" , err)
return
}
if resp == nil {
return
}
respData, err := sonic.Marshal(resp)
fmt.Println( string (respData))
JSON Response
{
"Items" : [
{
"Id" : "asset-20260318035710-kctzf" ,
"Name" : "" ,
"AssetType" : "Image" ,
"CreateTime" : "2026-03-18T03:57:10Z" ,
"UpdateTime" : "2026-03-18T03:57:14Z" ,
"LastInferenceTime" : "2026-07-24T10:08:49+08:00" ,
"ProjectName" : "default" ,
"URL" : "https://ark-media-asset-ap-southeast-1.tos-ap-southeast-1.volces.com/300060****/03301616086559****.jpg?X-Tos-Algorithm=***********" ,
"GroupId" : "group-20260318033332-*****" ,
"Status" : "Active" ,
"Moderation" : {
"Strategy" : "Default"
}
},
{
"GroupId" : "group-20260318033332-*****" ,
"Status" : "Active" ,
"Id" : "asset-20260318034804-wtnjr" ,
"Name" : "" ,
"URL" : "image_url" ,
"AssetType" : "Image" ,
"CreateTime" : "2026-03-18T03:48:04Z" ,
"UpdateTime" : "2026-03-18T03:48:08Z" ,
"LastInferenceTime" : "2026-07-24T02:08:49Z" ,
"ProjectName" : "default" ,
"Moderation" : {
"Strategy" : "Default"
}
}
],
"NextToken" : "****bmNyeXB0ZWRfY3Vyc29yIjoiLi4****"

Query asset groups

Use POST ListAssetGroups to query asset group information.

Tip
ListAssetGroups supports NextToken pagination and page-number pagination. For pagination methods, parameters, and examples, see Virtual portrait library API reference.
GO ListAssetGroups Request
package main


import (
"fmt"


"github.com/bytedance/sonic"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/credentials"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/session"
"github.com/byteplus-sdk/byteplus-go-sdk-v2/byteplus/universal"
)


func main () {
config := byteplus.NewConfig().WithCredentials(credentials.NewStaticCredentials( "<YOUR_AK>" , "<YOUR_SK>" , "" )).WithRegion( "ap-southeast-1" )
sess, _ := session.NewSession(config)
resp, err := universal.New(sess).DoCall(
universal.RequestUniversal{
ServiceName: "ark" ,
Action: "ListAssetGroups" ,
Version: "2024-01-01" ,
HttpMethod: universal.POST,
ContentType: universal.ApplicationJSON,
},
& map [ string ]any{
"Filter" : map [ string ]any{
"Name" : "<FIGURE_GROUP>" , // Support fuzzy search
"GroupIds" : [] string { "group-20260324142802-*****" , "group-20260324142910-*****" },
"GroupType" : "AIGC" ,
},
"MaxResults" : 1 ,
"SortBy" : "CreateTime" ,
"SortOrder" : "Desc" ,
},
)
if err != nil {
fmt.Printf( "error: %v\n" , err)
return
}
if resp == nil {
return
}
respData, err := sonic.Marshal(resp)
fmt.Println( string (respData))
JSON Response
{
"Items" : [
{
"UpdateTime" : "2026-03-18T03:33:32Z" ,
"Id" : "group-20260318033332-*****" ,
"Name" : "figure_group_1" ,
"Description" : "Figure group 1" ,
"GroupType" : "AIGC" ,
"ProjectName" : "default" ,
"CreateTime" : "2026-03-18T03:33:32Z"
}
],
"NextToken" : "****bmNyeXB0ZWRfY3Vyc29yIjoiLi4****"

Update/delete assets or asset group

You can call UpdateAsset, UpdateAssetGroup, DeleteAsset, and DeleteAssetGroup to update or delete assets and asset groups.

We recommend periodically checking LastInferenceTime by using GetAsset or ListAssets, and proactively deleting assets that have not been used for a long time to save storage costs.

Example: Upload assets and use GetAsset to retrieve asset information

The following example creates an asset, queries the asset Status, and determines whether to continue polling based on the status. The code executes the following logic:

BP_Upload_Asset_Get_Info.go — The query result is illustrated as follows:

TEXT BP_Upload_Asset_Get_Info.go Output
asset status: Active
asset is active, URL =

Sample code in other programming languages

Download: bp-demo.zip

Tip
Caution: Replace the AK and SK in the Demo. To call other interfaces such as ListAssets, replace ACTION and BODY in the demo code.

Generate video using portrait assets

After obtaining the Asset ID, private portrait assets can be used to generate video.

For details, see Use preset digital characters.

Use the asset URI in the content._url.url field of the Video Generation API to generate video.

Tip
Asset URI pattern: asset://<Asset_Id>
Tip
In the prompt you send to the model, reference assets using type + index, such as Image 1 and Video 1.
Do not use an Asset ID directly in the prompt.
Example: "The girl in Image 1 is wearing the outfit from Image 2 and is arranging items on the counter…"

Sample code:

PYTHON Python — Generate video with portrait asset
import os
import time
# Install SDK: pip install arkruntime
from arkruntime import Ark
client = Ark(
# The base URL for model invocation
base_url= "https://ark.ap-southeast.bytepluses.com/api/v3" ,
# Get API Key: https://ai.byteplus.com/ark/region:ap-southeast-1/apikey
api_key=os.environ.get( "ARK_API_KEY" ),
)
if __name__ == "__main__" :
print ( "----- create request -----" )
create_result = client.content_generation.tasks.create(
model= "dreamina-seedance-2-0-260128" , # Replace with Model ID
content=[
{
"type" : "text" ,
"text" : "Vertical HD close-up video of a beauty blogger (Image 1). She has bold, glamorous makeup with no facial shine or glare and a sweet smile. She holds a face cream jar (Image 2), presents it directly to the camera. The background is fresh and minimalist. Energetic and sweet style. English voiceover: 'I found my holy grail face cream! It has a cloud-like creamy texture that absorbs instantly. Perfect for post-all-nighter rescue, deep hydration and moisturization—my skin glows naturally even without makeup!' "
},
{
"type" : "image_url" ,
"image_url" : {
"url" : "asset://asset-20260225023032-gnzwk"
},
"role" : "reference_image"
},
{
"type" : "image_url" ,
"image_url" : {
"url" : "https://ark-doc.tos-ap-southeast-1.bytepluses.com/doc_image/r2v_ref_image.png"
},
"role" : "reference_image"
},
],
generate_audio= True ,
ratio= "16:9" ,
duration= 11 ,
watermark= True ,
)
print (create_result)
print ( "----- polling task status -----" )
task_id = create_result. id
while True :
get_result = client.content_generation.tasks.get(task_id=task_id)
status = get_result.status
if status == "succeeded" :
print ( "----- task succeeded -----" )
print (get_result)
break
elif status == "failed" :
print ( "----- task failed -----" )
print ( f"Error: {get_result.error} " )
break
else :
print ( f"Current status: {status} , Retrying after 30 seconds..." )

FAQs

1. Why is it not possible to generate video or retrieve asset information after assets are uploaded successfully?

The asset library is isolated by Project (ProjectName).

For more information about project, see the related IAM docs.

Tip
If ProjectName is not specified in the request, the asset group will be created in the default project by default.

2. How to manage user permissions for the asset library?

You can use Access control (IAM) to finely manage user actions in the asset library. Settings can be customized through the following steps:

Create a custom policy

  1. Open Access control > Create policy.
  2. Input the policy name.
  3. Switch to the JSON editor, paste the custom policy below into the editor, and click submit to save.
JSON IAM Policy JSON
{
"Statement" : [
{
"Effect" : "Allow" ,
"Action" : [
"ark:*Asset*"
],
"Resource" : [
"*"
]
}
]

Grant permissions to users/user groups

  1. Click User management > User / User group, select the user or user group to grant permissions to.
  2. In Policy, select the policy created in Step 1.
  3. (Optional) In Limit to project resources, select the project to which the policy applies.
  4. Click submit.

After completing the above actions, the user or user group can manage assets in the corresponding project.

For more information about IAM, please refer to the IAM docs.

3. How should I reference uploaded assets in the prompt (content.text)?

In the prompt, reference assets using the format "asset type + index", for example: Image 1, Video 1, Audio 1.

Note
Do not reference assets by Asset ID in the prompt.

For example, the sample below includes five reference images and one reference audio file. Use the prompt format:

PLAIN Prompt (content.text)
Create a fresh, creamy-painting–style short drama with a light, upbeat guitar track driving quick beat cuts. Use a cream-white base with peach-pink highlights, soft lighting, no visual effects—emotion conveyed through expressions. 0–2s: Two fast cuts: the CEO from Image 1 accidentally bumps into the female lead wearing the outfit from Image 2 (character from Image 3); they freeze and lock eyes, then a hand close-up as the CEO pulls off his suit jacket and drapes it over her shoulders. Gentle guitar starts; soft SFX of a coffee cup dropping and fabric brushing. 2–6s: Three fast cuts: the female lead, wrapped in his jacket, looks down and smiles shyly (cheeks flushed, close-up); the CEO watches her back with a faint smile and says "我们一起走吧," voice style referenced from Audio 1 (profile shot); the two share a black umbrella in the rain, fingertips touch then quickly pull back (near shot). Rainy backdrop references Image 4; each cut hits a light drum accent with rain and umbrella SFX, a subtle soft-mist look. 6–8s: Slow-motion eye-contact smiles; the text portion from Image 5 appears at the lower right, with small text "NEW EP DAILY" at the lower left. Minimal pale-pink petals drift; the guitar resolves gently as the frame freezes on their side-by-side profiles.
BASH cURL Request Example
curl --location 'https://ark.ap-southeast-1.bytepluses.com/api/v3/contents/generations/tasks' \
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer $ARK_API_KEY' \
-d '{
"model": "seedance-2-0-260128",
"content": [
{
"type": "text",
"text": "Create a fresh, creamy-painting–style short drama with a light, upbeat guitar track driving quick beat cuts. Use a cream-white base with peach-pink highlights, soft lighting, no visual effects—emotion conveyed through expressions. 0–2s: Two fast cuts: the CEO from 【Image 1】 accidentally bumps into the female lead wearing the outfit from 【Image 2】 (character from 【Image 3】); they freeze and lock eyes, then a hand close-up as the CEO pulls off his suit jacket and drapes it over her shoulders. Gentle guitar starts; soft SFX of a coffee cup dropping and fabric brushing. 2–6s: Three fast cuts: the female lead, wrapped in his jacket, looks down and smiles shyly (cheeks flushed, close-up); the CEO watches her back with a faint smile and says “我们一起走吧,” voice style referenced from 【Audio 1】 (profile shot); the two share a black umbrella in the rain, fingertips touch then quickly pull back (near shot). Rainy backdrop references 【Image 4】; each cut hits a light drum accent with rain and umbrella SFX, a subtle soft-mist look. 6–8s: Slow-motion eye-contact smiles; the text portion from 【Image 5】 appears at the lower right, with small text “NEW EP DAILY” at the lower left. Minimal pale-pink petals drift; the guitar resolves gently as the frame freezes on their side-by-side profiles."
},
{
"type": "image_url",
"role": "reference_image",
"image_url": {
"url": "asset://asset-20260224213248-ghm5g"
}
},
{
"type": "image_url",
"role": "reference_image",
"image_url": {
"url": "asset://asset-20260224213248-4lb57"
}
},
{
"type": "image_url",
"role": "reference_image",
"image_url": {
"url": "asset://asset-20260224213248-rc7b7"
}
},
{
"type": "image_url",
"role": "reference_image",
"image_url": {
"url": "asset://asset-20260224213248-wpgsn"
}
},
{
"type": "image_url",
"role": "reference_image",
"image_url": {
"url": "asset://asset-20260224213248-9wrmp"
}
},
{
"type": "audio_url",
"role": "reference_audio",
"audio_url": {
"url": "asset://asset-20260224213248-g5nv7"
}
}
],
"generate_audio": true,
"ratio": "16:9",
"duration": 11,
"watermark": false

Virtual portrait library API reference

For complete API parameters, request/response schemas, error codes, and rate limit details, see the official Virtual portrait library API reference ↗.