Assets
The asset API stores reference material — images, videos, audio — that Seedance video models can take as input. You add the material once, wait for the upstream asset service to process it, and then refer to it as asset://<asset_id> in a video generation request instead of passing the same public URL every time.
This is a management API, not a generation API. RouteAPI authenticates the call with your API Key, forwards it to the upstream asset service, and returns the upstream response. Calls on these endpoints are not billed as model usage. They are also outside the OpenAI SDK surface, so use plain HTTP rather than an SDK client.
Workflow
Section titled “Workflow”- Create an asset group with
POST /v1/asset-groups. - Add an asset to that group with
POST /v1/assets, passing a publicly downloadable URL. - Poll the asset with
GET /v1/assets/{assetID}until its status isActive. Processing is asynchronous. - Reference the asset as
asset://<asset_id>in themetadata.contentof aPOST /v1/video/generationsrequest. See Using an asset in a Seedance request and Seedance. - Poll the video task as usual. Asset-backed video requests are sent to the upstream once; they are not retried or failed over to another channel.
Endpoints
Section titled “Endpoints”| Method | Path | Purpose | Allocates asset space |
|---|---|---|---|
POST | /v1/asset-groups | Create an asset group | Yes |
GET | /v1/asset-groups | List groups | No |
GET | /v1/asset-groups/{groupID} | Retrieve one group | No |
DELETE | /v1/asset-groups/{groupID} | Delete a group | No |
PUT | /v1/asset-groups/{groupID} | Rename a group | No |
POST | /v1/assets | Add an asset to a group | Yes |
GET | /v1/assets | List assets in a group | No |
GET | /v1/assets/{assetID} | Retrieve one asset (use this to poll) | No |
DELETE | /v1/assets/{assetID} | Delete an asset | No |
PUT | /v1/assets/{assetID} | Rename an asset | No |
POST | /v1/assets/verify-sessions | Create a human verification session | Yes |
GET | /v1/assets/verify-sessions/{sessionID} | Retrieve a verification session | No |
Authentication
Section titled “Authentication”Send a Model API Key in the standard header:
Authorization: Bearer sk-your-routeapi-keyThe Key identifies the RouteAPI account. Assets are isolated per account: every API Key under the same account sees the same groups, assets, and verification sessions, and no other account can see them. The Key is never sent upstream — RouteAPI uses a separate upstream credential on your behalf.
First use and the asset pool key
Section titled “First use and the asset pool key”Each account is bound to one upstream credential, which the asset service uses to keep that account’s material separate. It is assigned automatically the first time the account calls a creating endpoint:
POST /v1/asset-groupsPOST /v1/assetsPOST /v1/assets/verify-sessions
Once assigned, the binding is permanent for the account and is reused by every later call, including video requests that reference asset:// assets.
Read and delete endpoints never allocate. Before the binding exists:
| Call | Response |
|---|---|
GET /v1/asset-groups | 200, empty list: {"total": 0, "asset_groups": []} |
Any other GET or DELETE | 404, {"error": "not found"} |
That is, an empty group list is a normal answer for a new account, while a 404 on a specific asset or group before first use means “this account has no asset space yet”, not “the ID is wrong”.
Group-to-credential routing is a service configuration detail. If your group has no asset channel configured, the first creating call fails with 503 asset_pool_exhausted or 503 asset_pool_misconfigured instead of allocating. Retry later, or contact support if it persists.
Asset groups
Section titled “Asset groups”Create an asset group
Section titled “Create an asset group”POST /v1/asset-groupscurl https://api.routeapi.ai/v1/asset-groups \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "product-refs", "description": "Reference material for product videos" }'| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Group name |
description | string | No | Group description |
Response:
{ "asset_group_id": "local_group_2c8186e6d8b44ffa73b371677bfe4af2", "name": "product-refs", "project_name": "default", "status": "active"}Keep asset_group_id: it is required when adding assets.
List asset groups
Section titled “List asset groups”GET /v1/asset-groupscurl "https://api.routeapi.ai/v1/asset-groups?page=1&page_size=20" \ -H "Authorization: Bearer $ROUTEAPI_KEY"Pagination parameters such as page and page_size are forwarded as-is; the upstream service’s defaults and maximum page size apply.
{ "total": 1, "asset_groups": [ { "asset_group_id": "local_group_2c8186e6d8b44ffa73b371677bfe4af2", "group_type": "AIGC", "project_name": "default", "session_id": "", "status": "active", "created_at": 1792050332, "updated_at": 1792050332 } ]}session_id is only set for groups created through human verification. A verified-person group has group_type LivenessFace; ordinary groups are AIGC.
Retrieve an asset group
Section titled “Retrieve an asset group”curl https://api.routeapi.ai/v1/asset-groups/local_group_2c8186e6d8b44ffa73b371677bfe4af2 \ -H "Authorization: Bearer $ROUTEAPI_KEY"The upstream detail response may return an empty status string. Use the value from the list response or the create response when you need to decide whether a group is usable.
Delete an asset group
Section titled “Delete an asset group”curl -X DELETE https://api.routeapi.ai/v1/asset-groups/local_group_2c8186e6d8b44ffa73b371677bfe4af2 \ -H "Authorization: Bearer $ROUTEAPI_KEY"{ "deleted": "local_group_2c8186e6d8b44ffa73b371677bfe4af2" }Deletion is permanent.
Rename an asset group
Section titled “Rename an asset group”curl -X PUT https://api.routeapi.ai/v1/asset-groups/local_group_2c8186e6d8b44ffa73b371677bfe4af2 \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "new_group_name"}'{ "name": "new_group_name", "updated": "local_group_2c8186e6d8b44ffa73b371677bfe4af2" }Assets
Section titled “Assets”Add an asset
Section titled “Add an asset”POST /v1/assetsOne asset per request. Pass the group, a name, and exactly one source URL.
| Field | Type | Required | Description |
|---|---|---|---|
asset_group_id | string | Yes | Group the asset belongs to |
asset_name | string | Yes | Name used for lookup only; it does not affect generation |
image_url | string | One of three | Public URL of an image |
video_url | string | One of three | Public URL of a video |
audio_url | string | One of three | Public URL of an audio file |
asset_type | string | No | Image, Video, or Audio; inferred from the URL field when omitted |
description | string | No | Asset description |
Pass exactly one of image_url, video_url, and audio_url; the upstream service rejects requests with zero or several. The URL must be downloadable from the public internet without authentication or extra headers. Base64 payloads and internal addresses are not supported, and the URL must stay reachable until processing finishes.
curl https://api.routeapi.ai/v1/assets \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "asset_group_id": "local_group_2c8186e6d8b44ffa73b371677bfe4af2", "asset_name": "product-front", "image_url": "https://cdn.example.com/product-front.jpg" }'{ "asset_id": "local_asset_25e4d0cb018fb5802bf4e466f99707d0", "asset_group_id": "local_group_2c8186e6d8b44ffa73b371677bfe4af2", "asset_name": "product-front", "asset_type": "Image", "asset_uri": "asset://local_asset_25e4d0cb018fb5802bf4e466f99707d0", "status": "Processing"}A successful create means the request was accepted, not that the asset is usable. asset_uri is the form to use in video requests.
Asset status
Section titled “Asset status”| Status | Meaning | What to do |
|---|---|---|
Processing | Still being ingested | Keep polling |
Active | Ready to use | Reference it in a video request |
Failed | Ingestion failed | Check format and size requirements, then add it again |
Retrieve an asset
Section titled “Retrieve an asset”Use this endpoint for polling, every 5–10 seconds, with an overall timeout. Processing usually takes seconds to tens of seconds; video assets and busy periods take longer.
curl https://api.routeapi.ai/v1/assets/local_asset_25e4d0cb018fb5802bf4e466f99707d0 \ -H "Authorization: Bearer $ROUTEAPI_KEY"{ "asset_id": "local_asset_25e4d0cb018fb5802bf4e466f99707d0", "asset_group_id": "local_group_2c8186e6d8b44ffa73b371677bfe4af2", "status": "Active", "asset_uri": "asset://local_asset_25e4d0cb018fb5802bf4e466f99707d0", "url": "https://..."}url is a temporary signed download address. Do not store it; use asset_uri for generation.
A missing or deleted asset returns 404 from the upstream service, for example:
{ "details": "record not found", "error": "asset not found in database" }List and delete assets
Section titled “List and delete assets”# List — asset_group_id is requiredcurl "https://api.routeapi.ai/v1/assets?asset_group_id=local_group_2c8186e6d8b44ffa73b371677bfe4af2" \ -H "Authorization: Bearer $ROUTEAPI_KEY"
# Deletecurl -X DELETE https://api.routeapi.ai/v1/assets/local_asset_25e4d0cb018fb5802bf4e466f99707d0 \ -H "Authorization: Bearer $ROUTEAPI_KEY"{ "deleted": "local_asset_25e4d0cb018fb5802bf4e466f99707d0" }Deleting an asset breaks any new video request that references it.
Rename an asset
Section titled “Rename an asset”curl -X PUT https://api.routeapi.ai/v1/assets/local_asset_25e4d0cb018fb5802bf4e466f99707d0 \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "new_asset_name"}'{ "updated": "local_asset_25e4d0cb018fb5802bf4e466f99707d0" }After the rename, asset detail and list responses return the new name.
Human verification sessions
Section titled “Human verification sessions”Reference material that contains a real person’s face needs the person’s own consent first. A verification session produces a dedicated group for that person’s material; assets added to it are checked for face consistency.
Create a session
Section titled “Create a session”POST /v1/assets/verify-sessionsThe body may be empty ({} or no body at all). callback_url is accepted but not used for redirection.
curl https://api.routeapi.ai/v1/assets/verify-sessions \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{}'{ "session_id": "asset_sess_5e7860e76613dd51a4867bdcbea6076b", "status": "pending", "h5_link": "https://...one-time-link...", "callback_url": ""}Send h5_link only to the person who will be verified. It contains a one-time credential and grants access to the verification page; do not put it in public logs, screenshots, or group chats.
Retrieve a session
Section titled “Retrieve a session”curl https://api.routeapi.ai/v1/assets/verify-sessions/asset_sess_5e7860e76613dd51a4867bdcbea6076b \ -H "Authorization: Bearer $ROUTEAPI_KEY"status | Meaning |
|---|---|
pending | Waiting for the person, or still being processed |
succeeded | Verified; asset_group_id is populated |
failed | Verification failed; read error |
{ "session_id": "asset_sess_a8af6fab34f694ada40c22521448f579", "status": "succeeded", "result_code": "", "error": "", "asset_group_id": "group-20261015202316-4t5jt", "project_name": "default", "created_at": 1792066965, "updated_at": 1792067203}There is no webhook or redirect back to your service — the person’s browser ends on a result page owned by the upstream. Poll the session until status becomes succeeded (usually within minutes), then add that person’s material to the returned asset_group_id. result_code is currently always empty; use status. A failed session with error such as verification expired has to be recreated.
Using an asset in a Seedance request
Section titled “Using an asset in a Seedance request”Reference the asset inside metadata.content of a video generation request, using the asset_uri returned by the asset API:
curl https://api.routeapi.ai/v1/video/generations \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "seedance-2.0-fast", "prompt": "The product in image 1 rotates slowly", "metadata": { "content": [ { "type": "text", "text": "The product in image 1 rotates slowly" }, { "type": "image_url", "image_url": { "url": "asset://local_asset_25e4d0cb018fb5802bf4e466f99707d0" }, "role": "reference_image" } ], "duration": 4, "resolution": "480p", "ratio": "1:1" } }'Use a Seedance model ID from GET /v1/models that supports video generation; see Models. The request format for Seedance, including metadata.content roles and model-specific limits, is documented in Seedance.
Rules that come from the asset service contract:
- The
type/rolepair must match the asset type:image_urlwithreference_image,video_urlwithreference_video,audio_urlwithreference_audio. - Only
Activeassets can be referenced. - Prompt text refers to material by ordinal (“image 1”, “video 1”), counted within each type in
contentorder. Do not put asset IDs in the prompt. - Real-person material must come from a verified group; otherwise the upstream may reject or fail the task.
RouteAPI detects a reference by looking for any JSON string value that starts with asset:// (case-insensitive). Such a request is pinned to your asset channel and sent once, with no retry or channel failover. The request is rejected locally in these cases:
| Status | Code | Cause |
|---|---|---|
| 400 | asset_key_required | The body references asset://, but the account has no asset space yet. Create a group and an asset first |
| 400 | asset_remix_conflict | A remix of a non-asset task tries to reference an asset:// asset |
| 400 | asset_channel_conflict | The API Key is pinned to a specific channel that is not your asset channel |
| 400 | asset_model_unsupported | The model is not supported by your asset channel |
| 503 | asset_key_unavailable | The upstream credential bound to the account is faulted or disabled |
These errors use the task error shape {"code": ..., "message": ..., "data": ...}.
Limits and errors
Section titled “Limits and errors”- Request body —
POSTandPUTbodies are read with a 1 MiB limit. A larger body returns413before anything is sent upstream. Material itself is never uploaded through the body; pass a URL. - Body shape — a non-empty
POSTorPUTbody must be a JSON object. Arrays, bare strings,null, and invalid JSON return400without reaching the upstream service.GETandDELETEbodies are ignored. - Rename body — a
PUTbody must contain a non-empty string fieldname(lowercase key). A missing, empty, or non-stringnamereturns400without reaching the upstream service. - Response body — upstream responses over 10 MiB, or responses that fail to read, return
502. - Timeout — the upstream call has a fixed 60-second timeout; expiration is reported as
502. project_name— if present in aPOSTorPUTbody, this field is removed before forwarding, in any letter case.- Ownership errors — if the upstream answers
403with an ownership denial (“you do not own” / “access denied”), RouteAPI returns404 {"error": "not found"}instead, so the resource looks nonexistent rather than forbidden. - Upstream credential failures — an upstream
401becomes503 asset_key_unavailableand marks the account’s binding as faulted (see above). An upstream402, or an upstream error whose body reads like a billing problem, becomes503 asset_service_unavailable.
Other upstream status codes and bodies are passed through unchanged, including upstream validation errors. RouteAPI’s own errors use one of three shapes:
{ "error": "not found" }{ "error": "<message>", "code": "asset_pool_busy" }{ "error": { "message": "token.invalid (request id: ...)", "type": "error", "code": "" }}The third shape comes from the shared authentication and rate-limit middleware, so check whether error is a string or an object. Local message text is currently Chinese — for the first two shapes, branch on the HTTP status and the code field rather than on the message.
| Status | Code | Meaning | Handling |
|---|---|---|---|
| 400 | — | Body is not a JSON object, or the upstream rejected the parameters | Fix the request; do not retry unchanged |
| 401 | — | Missing, invalid, or deleted API Key | Check Authorization |
| 404 | — | Unknown asset, group, or session; or no asset space yet for this account | Check the ID; create a group first if the account is new |
| 413 | — | Request body exceeds 1 MiB | Send a URL instead of inline data |
| 429 | — | Rate limit triggered | Slow down polling and retry later |
| 500 | asset_pool_internal_error | RouteAPI could not read or write its own asset state | Retry later |
| 500 | — | RouteAPI failed while reading the account’s asset state | Retry later |
| 502 | — | Upstream unreachable, timed out, or returned an unreadable or oversized response | Retry later |
| 503 | asset_key_unavailable | The account’s upstream credential is faulted or disabled | Contact support; retrying will not help |
| 503 | asset_pool_exhausted | Your group has no asset channel, or no credential is available to allocate | Retry later or contact support |
| 503 | asset_pool_busy | Credentials exist but none could be allocated right now | Retry later |
| 503 | asset_pool_misconfigured | Your group maps to more than one asset channel | Contact support |
| 503 | asset_channel_unavailable | The asset channel itself is disabled or misconfigured | Retry later or contact support |
| 503 | asset_service_unavailable | Upstream account balance or hard failure | Retry later or contact support |
Asset calls have no model cost, but when the platform’s model request rate limit is enabled they count against it like any other /v1 request. See Common Errors for the general error model, and Model Pricing for how the video generation that consumes these assets is billed.
Next steps
Section titled “Next steps”- Seedance — Seedance request format and
metadata.contentroles. - Create a video task — the shared request envelope for every video model.
- Models — find a supported video model ID.
- Common Errors — status codes and troubleshooting order.