Seedance 真人素材 API
为真实存在的人创建 Seedance 真人素材。你的服务端先创建认证会话,本人在火山引擎官方页面完成授权和活体检测,然后你用本人照片创建素材,拿到 asset:// URI 用于视频生成。调用方不需要登录 YingTu,但本人必须亲自完成认证。
最后更新:2026-09-25 · 已对照线上接口代码逐项核对
概述#
视频里要出现真实存在的人(主播、代言人、讲师等)时,Seedance 要求先由本人授权,不能直接用普通照片。这组接口把授权流程封装成三步:创建认证会话 → 本人认证 → 创建素材。
认证页面由火山引擎(字节跳动旗下云服务)提供,域名是 ark.volcengine.com,本人需要用带摄像头的设备完成活体检测。YingTu 不接触本人的证件或人脸数据,只转发认证结果。
认证链接必须由本人亲自完成,不能代做。如果角色是 AI 生成或虚构的形象,请用更简单的 虚拟人素材 API。
准备工作#
- 一个 LaoZhang API Key。素材接口只校验 Key 是否有效,不限分组;但之后用素材生成视频必须用 SeeDance2 分组的 Key。建议全程用同一个 SeeDance2 Key,因为
verification_id和素材id只能用创建它的 Key 查询。 - 你自己的服务端。接口不支持浏览器直接调用,Key 也不能放在前端。
- 一个回调页面(
callback_url):公网 HTTPS 地址,本人认证结束后浏览器会跳到这里。它是给人看的网页,不是接收通知的 webhook。 - 一个把认证链接交给本人的渠道,比如在你的 App 或网页里直接打开,或发短信、邮件给本人。
- 本人一张清晰正面照的公网 HTTPS 地址,以及保存
verification_id、id、uri的持久化存储。
接入流程#
- 1
你的服务端
调用
POST /api/seedance-assets/real-persons/verifications,传入callback_url。保存返回的verification_id(和你的用户关联),把verification_url交给本人。 - 2
本人
在 30 分钟内打开
verification_url,在火山引擎官方页面确认授权并完成活体检测。 - 3
火山引擎 → YingTu → 本人的浏览器
认证流程结束后,本人的浏览器先跳到 YingTu 的中转地址,再被 302 重定向到你的
callback_url,URL 上附带verification_id和result_code。 - 4
你的服务端
回调页被打开时(或者定时轮询),调用
GET /api/seedance-assets/real-persons/verifications/{verification_id},直到status为verified。 - 5
你的服务端
在
expires_at之前调用POST /api/seedance-assets/real-persons,提交本人清晰正面照的地址,拿到素材id和uri(状态为Processing)。 - 6
你的服务端
轮询
GET /api/seedance-assets/real-persons/{id}到Active,然后把uri作为reference_image用于视频生成。
回调是本人浏览器的页面跳转,不是服务器通知。YingTu 不会向你的服务器发送任何 webhook。本人认证后直接关掉页面,你的回调页就不会被打开,所以一定要能靠轮询 GET 拿到结果。
基础约定#
| 项目 | 取值 | 说明 |
|---|---|---|
| Base URL | https://yingtu.ai | 下文所有路径都拼在这个域名后面。 |
| 认证 | Authorization: Bearer $LAOZHANG_API_KEY | Bearer 区分大小写,后面跟一个空格和完整的 Key。缺失或格式不对返回 401 invalid_api_key。 |
| 请求体 | Content-Type: application/json | 只有 POST 需要。必须是 JSON 对象,不超过 16 KB(16384 字节),不能带接口没有列出的字段。 |
| 响应 | JSON | 成功和失败都带 request_id,同一个值也在 X-Request-Id 响应头里。所有响应都带 Cache-Control: no-store。 |
| 调用位置 | 只能从服务端调用 | 接口不返回 CORS 响应头,浏览器里直接调用会被拦截;而且 Key 不能出现在前端代码里。 |
| 时间格式 | ISO 8601 | expires_at 是 UTC 时间,例如 2026-09-25T08:30:00.000Z。 |
YingTu 会拿你的 Key 请求 LaoZhang 的 /v1/models 做校验,校验通过的结果在单个服务实例上缓存 5 分钟。所以刚停用的 Key 最多还能继续调用 5 分钟;LaoZhang 校验服务暂时不可用时返回 503 authentication_unavailable,稍后重试即可。
接口#
创建认证会话
为一位需要授权的真人创建认证会话。每次调用都会新建一个会话,并计入写入额度。verification_url 交给本人打开;verification_id 由你的服务端保存。
curl -X POST https://yingtu.ai/api/seedance-assets/real-persons/verifications \
-H "Authorization: Bearer $LAOZHANG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"callback_url": "https://app.example.com/seedance/verified?user=42",
"language": "zh"
}'| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
callback_url | string | 是 | 本人认证结束后浏览器要跳到的你的页面。必须是公网 HTTPS 地址,规则和 image_url 相同(见“创建真人素材”)。可以带查询参数(比如你的用户或订单号),跳转时原样保留;但同名的 verification_id、result_code 参数会被覆盖。 |
language | string | 否 | 认证页语言:zh(简体中文,默认)、en、zh-Hant。区分大小写,其它值返回 400。火山引擎目前只提供这三种语言,俄语、日语、韩语、西班牙语用户建议用 en。 |
请求体只能包含这两个字段。
HTTP/1.1 201 Created
Content-Type: application/json
Cache-Control: no-store
X-Request-Id: req_4f0c2a9e8b7d4c1fa3e56b7d9c0e1f2a
X-RateLimit-Limit: 12
X-RateLimit-Remaining: 11
X-RateLimit-Reset: 1790326800
{
"request_id": "req_4f0c2a9e8b7d4c1fa3e56b7d9c0e1f2a",
"verification_id": "ver_Hq3nW8bYt5...Kd0sE",
"status": "requires_user_action",
"verification_url": "https://ark.volcengine.com/...&lng=zh",
"callback_url": "https://app.example.com/seedance/verified?user=42",
"expires_at": "2026-09-25T08:30:00.000Z"
}| 字段 | 类型 | 说明 |
|---|---|---|
request_id | string | 本次请求的追踪号。 |
verification_id | string | 认证会话 id,以 ver_ 开头。它是加密令牌,通常 300 个字符以上、长度不固定,并绑定当前 Key。保存在服务端,查询认证和创建素材都用它。 |
status | string | 固定为 requires_user_action,表示等待本人去完成认证。 |
verification_url | string | 火山引擎官方认证页地址(https://ark.volcengine.com/...),已按 language 带上 lng 参数。只交给本人,不要写进日志或公开页面。 |
callback_url | string | 你传入的回调地址(经过 URL 规范化)。 |
expires_at | string | verification_id 的过期时间,UTC,创建后 30 分钟。过期后查询认证和创建素材都返回 410。 |
查询认证状态
查询本人是否已通过认证。每次调用都会实时向火山引擎查询,不计入写入额度。建议在回调页被打开时查一次;没有结果就每 5 秒左右轮询,直到 verified 或过期。
# VERIFICATION_ID 是你保存的 verification_id(ver_...)
curl https://yingtu.ai/api/seedance-assets/real-persons/verifications/$VERIFICATION_ID \
-H "Authorization: Bearer $LAOZHANG_API_KEY"| 路径参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
verification_id | string | 是 | 创建会话时返回的 verification_id,或回调 URL 上带回的 verification_id,两者都可以。 |
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
X-Request-Id: req_4f0c2a9e8b7d4c1fa3e56b7d9c0e1f2a
{
"request_id": "req_4f0c2a9e8b7d4c1fa3e56b7d9c0e1f2a",
"verification_id": "ver_Hq3nW8bYt5...Kd0sE",
"status": "pending",
"expires_at": "2026-09-25T08:30:00.000Z"
}HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
X-Request-Id: req_4f0c2a9e8b7d4c1fa3e56b7d9c0e1f2a
{
"request_id": "req_4f0c2a9e8b7d4c1fa3e56b7d9c0e1f2a",
"verification_id": "ver_Hq3nW8bYt5...Kd0sE",
"status": "verified",
"expires_at": "2026-09-25T08:30:00.000Z"
}| 字段 | 类型 | 说明 |
|---|---|---|
request_id | string | 本次请求的追踪号。 |
verification_id | string | 原样返回你传入的值。 |
status | string | pending 或 verified,含义见“状态说明”。 |
expires_at | string | 这个 verification_id 的过期时间(UTC)。 |
pending 的 HTTP 状态码是 200,不是错误。过期后这个接口返回 410 verification_expired,不再返回状态。
创建真人素材
认证通过后,用本人照片创建真人素材。必须在 verification_id 过期前调用。接口返回 202,素材在后台处理,之后和虚拟人素材一样查询状态。每次调用都计入写入额度。
# VERIFICATION_ID 是你保存的 verification_id(ver_...)
curl -X POST https://yingtu.ai/api/seedance-assets/real-persons \
-H "Authorization: Bearer $LAOZHANG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"verification_id": "'"$VERIFICATION_ID"'",
"image_url": "https://cdn.example.com/people/presenter-a.jpg",
"name": "presenter-a"
}'| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
verification_id | string | 是 | 状态已经是 verified 的会话 id,必须是同一个 Key 创建的。 |
image_url | string | 是 | 本人清晰正面照的公网 HTTPS 地址。照片里应当是完成认证的这个人。 |
name | string | 否 | 素材名称,去掉首尾空格后 1~64 个字符。不需要就不要传;传空字符串或 null 会返回 400。 |
请求体只能包含这三个字段。YingTu 按这个顺序检查:请求体格式 → image_url → verification_id 是否属于你(403)→ 是否过期(410)→ 是否已通过认证(409)→ 创建素材。
图片地址要求(image_url 和 callback_url 通用)
- 必须以
https://开头,不能带用户名或密码(https://user:pass@...)。首尾空格会被去掉。 - 主机不能是
localhost、*.localhost、*.local,也不能是内网或保留 IP:0.x、10.x、127.x、169.254.x、172.16.x–172.31.x、192.168.x,以及 IPv6 的::、::1、::ffff:*、fc00::/7、fe80::/10。 - YingTu 只检查地址格式,不会下载图片;真正下载图片的是火山引擎。所以地址必须能被公网匿名访问:不要防盗链,不要需要登录或 Cookie。如果用签名地址(如 OSS、S3、COS 预签名链接),有效期至少要撑到素材变成
Active或Failed。 - 图片本身的问题(内容、格式、尺寸等)通常在异步处理时暴露为
status: "Failed";少数情况下火山引擎会在创建时直接拒绝,返回 502upstream_error。
HTTP/1.1 202 Accepted
Content-Type: application/json
Cache-Control: no-store
X-Request-Id: req_4f0c2a9e8b7d4c1fa3e56b7d9c0e1f2a
X-RateLimit-Limit: 12
X-RateLimit-Remaining: 10
X-RateLimit-Reset: 1790326800
{
"request_id": "req_4f0c2a9e8b7d4c1fa3e56b7d9c0e1f2a",
"id": "ast_9Qk2vX7mL0pT...cRz4w",
"object": "seedance.asset",
"kind": "real_person",
"name": "presenter-a",
"status": "Processing",
"uri": "asset://asset-20260925080012-x7k2p"
}示例里的 id 做了截断。响应字段:
| 字段 | 类型 | 说明 |
|---|---|---|
request_id | string | 本次请求的追踪号,格式为 req_ 加 32 位十六进制。找技术支持时提供它。 |
id | string | YingTu 素材 id,以 ast_ 开头,只用于查询状态。它是加密令牌,通常 250 个字符左右、长度不固定,并绑定创建它的 API Key。原样保存,不要截断或解析。 |
object | string | 固定为 seedance.asset。 |
kind | string | 固定为 real_person。 |
name | string | 素材名称。没有名称时为空字符串。 |
status | string | 素材状态,原样来自火山引擎:Processing、Active 或 Failed。见“状态说明”。 |
uri | string | 生成视频时使用的素材地址,格式为 asset://asset-...。创建后不会变化。原样使用,不要拼接或改写。 |
created_at | string | 可选。火山引擎返回的创建时间;上游没有返回时不出现。 |
updated_at | string | 可选。火山引擎返回的最近更新时间。 |
failure | object | 可选。只在火山引擎报告错误时出现,形如 { "code": "...", "message": "..." },两个字段都可能是空字符串。 |
查询真人素材
查询真人素材的最新状态。每次调用都会实时向火山引擎查询,不计入写入额度。只能用创建时的同一个 Key 查询。素材 id 不会过期,认证会话过期不影响已经创建的素材。
# ASSET_ID 是创建接口返回的 id(ast_...)
curl https://yingtu.ai/api/seedance-assets/real-persons/$ASSET_ID \
-H "Authorization: Bearer $LAOZHANG_API_KEY"| 路径参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | 创建真人素材返回的 id(ast_...)。不要传 verification_id、uri 或 asset-...,那样会返回 403。 |
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
X-Request-Id: req_4f0c2a9e8b7d4c1fa3e56b7d9c0e1f2a
{
"request_id": "req_4f0c2a9e8b7d4c1fa3e56b7d9c0e1f2a",
"id": "ast_9Qk2vX7mL0pT...cRz4w",
"object": "seedance.asset",
"kind": "real_person",
"name": "presenter-a",
"status": "Active",
"uri": "asset://asset-20260925080012-x7k2p",
"created_at": "2026-09-25T08:00:12Z",
"updated_at": "2026-09-25T08:01:03Z"
}素材处理失败时 HTTP 状态码仍然是 200,status 为 Failed,并带 failure 对象。请看 status 字段,不要只看状态码。
状态说明#
认证会话
| 状态 | 在哪里出现 | 含义 | 你该做什么 |
|---|---|---|---|
requires_user_action | 只在创建会话的响应里 | 会话已创建,等本人打开链接。 | 把 verification_url 交给本人。 |
pending | GET 查询 | 还没有通过的认证结果。本人没打开链接、正在认证、中途放弃、认证没通过,都显示 pending。 | 继续轮询到 expires_at。过期仍未通过,就创建新会话让本人重试。 |
verified | GET 查询 | 本人已通过认证。 | 在 expires_at 之前调用 POST /real-persons 创建素材。 |
| HTTP 410 | GET 查询和创建素材 | verification_id 已过期(verification_expired)。 | 创建新会话。已经创建的素材不受影响。 |
没有“认证失败”状态。本人认证没通过时,查询结果一直是 pending,直到过期。想更早提示用户,可以参考回调里的 result_code,但是否通过始终以 GET 查询到 verified 为准。
素材
| status | 含义 | 你该做什么 |
|---|---|---|
Processing | 火山引擎正在处理照片。创建接口返回的通常就是这个状态。 | 每 5~10 秒查询一次。这时还不能用于生成。 |
Active | 处理完成,可以用于视频生成。 | 停止轮询,开始用 uri 生成视频。 |
Failed | 处理失败,failure 里有火山引擎给出的原因。 | 按原因换照片后,在认证有效期内重新创建;已过期就要重新认证。 |
素材的 status 是火山引擎原样返回的值。如果出现表里没有的值,按“未就绪”处理并继续查询。
认证回调#
创建会话时,YingTu 交给火山引擎的是自己的中转地址,不是你的 callback_url。本人认证结束后,火山引擎把本人的浏览器带到中转地址,YingTu 再把浏览器 302 重定向到你的 callback_url:
# 1) Volcengine sends the person's browser to YingTu's relay URL
GET https://yingtu.ai/api/seedance-assets/real-persons/callback/cb_...?bytedToken=...&resultCode=10000
# 2) YingTu answers with a redirect to your callback_url
HTTP/1.1 302 Found
Location: https://app.example.com/seedance/verified?user=42&verification_id=ver_Zp7cM1xRa4...Ty9vB&result_code=10000| 附加参数 | 说明 |
|---|---|
verification_id | YingTu 新签发的认证会话 id,同样绑定你的 Key,正常情况下和你创建时拿到的指向同一次认证,可以直接用于查询和创建素材。它和创建时返回的字符串不一样(每次加密结果都不同),不要拿两者做字符串比较。只有火山引擎带回了会话令牌时才会附加。 |
result_code | 火山引擎给出的结果码,YingTu 原样透传、不做解释。只有火山引擎带回时才会附加。 |
- 回调只是“本人回来了”的信号。URL 上的参数任何人都能伪造,不要凭
result_code判断是否通过,一律用 GET 查询确认verified。 - 推荐用你创建会话时保存的
verification_id查询,用callback_url里你自己的参数(例如user=42)找到对应记录。 - 火山引擎的会话令牌(
bytedToken)不会出现在跳转后的 URL 里。 - 中转地址在会话创建 35 分钟后失效。超时后本人会看到 HTTP 400 和
{"error":"Invalid or expired verification callback."},不会跳到你的页面。 - 回调页是给本人看的。建议显示“认证已提交,正在确认”,再由你的服务端去查询结果。
错误码#
所有错误都用同一种结构返回。请同时看 HTTP 状态码和 error.code;error.retryable 为 true 表示原样重试有可能成功,为 false 表示需要先改请求或联系支持。下面的示例是真实返回:
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
X-Request-Id: req_42480b1cfd2147439b8c514ad8688ab0
{
"error": {
"code": "invalid_request",
"message": "Unknown request field: consent_reference.",
"request_id": "req_42480b1cfd2147439b8c514ad8688ab0",
"retryable": false
}
}| HTTP | code | 什么时候出现 | 怎么处理 | 可重试 |
|---|---|---|---|---|
| 400 | invalid_request | 请求体不是 JSON 对象、缺少必填字段、字段值不合法,或带了接口没有列出的字段。message 会指出是哪个字段。 | 按 message 修改请求。原样重试不会成功。 | 否 |
| 401 | invalid_api_key | 没有 Authorization 头、格式不是 Bearer <key>,或 LaoZhang 判定 Key 无效、已停用。 | 检查 Key 和请求头格式。 | 否 |
| 403 | resource_not_owned | id 或 verification_id 不是当前 Key 创建的;或被截断、改动;或把虚拟人 id 拿到真人路径查询。不存在和不属于你都返回 403,不会返回 404。 | 确认用的是创建时的同一个 Key、路径类型正确、id 原样未改。 | 否 |
| 403 | capability_unavailable | 火山引擎侧的素材权益暂不可用。这是 YingTu 服务账号的问题,不是你的 Key。 | 带上 request_id 联系技术支持。 | 否 |
| 409 | verification_pending | 用还没通过认证的 verification_id 调用 POST /real-persons。 | 先用 GET 查到 verified 再创建。不要反复 POST 重试:每次 POST 都会消耗写入额度。 | 是 |
| 410 | verification_expired | verification_id 已超过 expires_at。GET 查询和创建素材都会返回。 | 重新创建认证会话,让本人重新认证。 | 否 |
| 413 | payload_too_large | 请求体超过 16 KB。 | 精简请求体。正常请求远小于这个上限。 | 否 |
| 415 | unsupported_media_type | POST 请求的 Content-Type 不是 application/json。 | 加上 Content-Type: application/json。 | 否 |
| 429 | rate_limit_exceeded | 当前 Key 超过每小时 12 次写入;或火山引擎侧限流。 | 有 Retry-After 时等待对应秒数;没有时(上游限流)用指数退避重试。 | 是 |
| 500 | internal_error | YingTu 内部未预期的错误。 | 带上 request_id 联系技术支持。POST 可能已部分执行,重试有可能产生重复素材。 | 否 |
| 502 | upstream_error | 火山引擎返回错误,或返回的数据不完整(例如没有素材 ID、认证链接没通过安全检查)。 | 以 retryable 为准:true 时稍后重试;false 时通常是请求内容被上游拒绝,修改后再试。 | 视情况 |
| 503 | authentication_unavailable | 暂时无法向 LaoZhang 校验 Key。 | 稍后重试。 | 是 |
| 503 | service_unavailable | YingTu 服务端配置暂不可用。 | 稍后重试;持续出现请联系技术支持。 | 是 |
另外两种不是 JSON 错误结构的情况:请求方法不对(例如对创建接口发 GET)时返回 405,没有响应体;真人认证的回调中转地址失效时,本人的浏览器会看到 400,详见“认证回调”。
限流与追踪#
- 每个 API Key 每小时最多 12 次写入请求。写入请求指所有 POST 接口的合计:创建虚拟人素材、创建真人认证会话、创建真人素材。GET 查询不计数。
- 只要 Key 校验通过,POST 就会计数,包括被 400、403、409、410、413、415 拒绝的请求和上游失败的请求。401 不计数。所以请先在本地校验参数再发送。
- 计数窗口从这一小时里的第一次 POST 开始,满 1 小时后整体清零,不是滑动窗口。
- 成功的 POST 响应带
X-RateLimit-Limit、X-RateLimit-Remaining、X-RateLimit-Reset(窗口重置时间,Unix 秒)。错误响应不带这三个头。 - 超过限制返回 429
rate_limit_exceeded,并带Retry-After(秒)。火山引擎侧限流同样返回 429,但没有Retry-After。 - 一个真人素材至少需要 2 次 POST(创建认证会话 + 创建素材),所以一个 Key 每小时最多约 6 个真人素材。等待认证时请轮询 GET,不要反复 POST。
- 如果这个额度不够你的业务量,请联系 YingTu 技术支持。
X-Request-Id: req_4f0c2a9e8b7d4c1fa3e56b7d9c0e1f2a
X-RateLimit-Limit: 12
X-RateLimit-Remaining: 11
X-RateLimit-Reset: 1790326800在 Seedance 视频生成中使用素材#
视频生成不在 YingTu 的素材接口里完成,而是调用 LaoZhang 的 Seedance 接口 POST https://api.laozhang.ai/seedance/api/v3/contents/generations/tasks。这一步的 Key 必须是 SeeDance2 分组。
- 在
content数组里加一项image_url:url填素材的uri(asset://...),role填reference_image。 - 只能用状态为
Active的素材。还在Processing或已经Failed的素材不要提交。 - 同一个
uri可以在任意多个任务里重复使用,不需要每次重新创建素材。 - 每个任务的参考图数量有上限:Seedance 2.0 系列最多 9 张,Seedance 2.5 最多 30 张。
- 其它参数(模型、比例、时长、分辨率、查询任务结果)以 LaoZhang Seedance 文档 为准。
curl -X POST https://api.laozhang.ai/seedance/api/v3/contents/generations/tasks \
-H "Authorization: Bearer $LAOZHANG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seedance-2-0-fast-260128",
"content": [
{ "type": "text", "text": "参考图中的角色在雨夜的霓虹街头慢慢走向镜头,电影感光影" },
{
"type": "image_url",
"image_url": { "url": "asset://asset-20260925080012-x7k2p" },
"role": "reference_image"
}
],
"ratio": "16:9",
"duration": 5,
"resolution": "720p",
"watermark": false,
"generate_audio": false
}'创建成功后返回 { "id": "cgt-..." },再用 GET .../contents/generations/tasks/{id} 查询任务结果。
通过 API 创建的素材不会出现在 YingTu 网页工作台的账号素材里,也不能在工作台里选用;它们只能通过 LaoZhang 的 Seedance API 使用。
完整代码示例#
从创建认证会话到素材可用的完整流程。运行前先设置环境变量 LAOZHANG_API_KEY。实际业务里第 2 步通常由回调页触发,这里用轮询代替。
import os
import time
import requests
BASE_URL = "https://yingtu.ai/api/seedance-assets"
HEADERS = {"Authorization": f"Bearer {os.environ['LAOZHANG_API_KEY']}"}
def api(method, path, **kwargs):
resp = requests.request(method, BASE_URL + path, headers=HEADERS, timeout=60, **kwargs)
body = resp.json()
if not resp.ok:
err = body["error"]
raise RuntimeError(f"{resp.status_code} {err['code']}: {err['message']} ({err['request_id']})")
return body
# 1. 创建认证会话。callback_url 上可以带你自己的参数,回跳时原样保留
session = api("POST", "/real-persons/verifications", json={
"callback_url": "https://app.example.com/seedance/verified?user=42",
"language": "zh",
})
verification_id = session["verification_id"] # 存到你的数据库,和 user=42 关联
print("把这个链接发给本人:", session["verification_url"])
# 2. 等本人完成认证。可以在回调页被打开时触发,也可以直接轮询;
# 过期后会抛出 410 verification_expired
while api("GET", f"/real-persons/verifications/{verification_id}")["status"] != "verified":
time.sleep(5)
# 3. 用本人的清晰正面照创建素材
asset = api("POST", "/real-persons", json={
"verification_id": verification_id,
"image_url": "https://cdn.example.com/people/presenter-a.jpg",
"name": "presenter-a",
})
# 4. 等素材可用
for _ in range(120):
if asset["status"] in ("Active", "Failed"):
break
time.sleep(5)
asset = api("GET", f"/real-persons/{asset['id']}")
print(asset["status"], asset["uri"], asset.get("failure"))// Node.js 18+,保存为 real-person.mjs 后运行:node real-person.mjs
const BASE_URL = "https://yingtu.ai/api/seedance-assets";
const AUTH = { Authorization: "Bearer " + process.env.LAOZHANG_API_KEY };
const JSON_HEADERS = { "Content-Type": "application/json" };
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function api(path, init = {}) {
const res = await fetch(BASE_URL + path, {
...init,
headers: { ...AUTH, ...init.headers },
signal: AbortSignal.timeout(60_000),
});
const body = await res.json();
if (!res.ok) {
const { code, message, request_id } = body.error;
throw new Error(res.status + " " + code + ": " + message + " (" + request_id + ")");
}
return body;
}
// 1. 创建认证会话;callback_url 上你自己的参数会原样带回
const session = await api("/real-persons/verifications", {
method: "POST",
headers: JSON_HEADERS,
body: JSON.stringify({
callback_url: "https://app.example.com/seedance/verified?user=42",
language: "zh",
}),
});
const verificationId = session.verification_id; // 存到你的数据库,和 user=42 关联
console.log("把这个链接发给本人:", session.verification_url);
// 2. 等本人完成认证;过期后会抛出 410 verification_expired
while ((await api("/real-persons/verifications/" + verificationId)).status !== "verified") {
await sleep(5000);
}
// 3. 用本人的清晰正面照创建素材
let asset = await api("/real-persons", {
method: "POST",
headers: JSON_HEADERS,
body: JSON.stringify({
verification_id: verificationId,
image_url: "https://cdn.example.com/people/presenter-a.jpg",
name: "presenter-a",
}),
});
// 4. 等素材可用
for (let i = 0; i < 120 && !["Active", "Failed"].includes(asset.status); i++) {
await sleep(5000);
asset = await api("/real-persons/" + asset.id);
}
console.log(asset.status, asset.uri, asset.failure);常见问题#
回调会通知我的服务器吗?
不会。回调是本人浏览器的 302 跳转,你的服务器只有在本人的浏览器打开回调页时才会收到请求。请始终能通过轮询 GET 拿到结果。
为什么回调里的 verification_id 和创建时拿到的不一样?
YingTu 在回调时重新签发了一个加密令牌,所以字符串不同,但正常情况下指向同一次认证,两个都能用。请用 callback_url 里你自己的参数来对应用户,不要比较这两个字符串。
本人认证没通过,状态会变成什么?
仍然是 pending,直到 expires_at 后返回 410。接口没有“失败”状态。可以让本人重新打开同一个链接再试(在有效期内),或创建新会话。
认证过期后,已经创建的素材还能用吗?
能。过期只影响 verification_id,素材的 id 和 uri 不会过期。
一次认证可以创建几个素材?
YingTu 不限制次数:在 expires_at 之前,同一个 verified 的 verification_id 每调用一次 POST /real-persons 就创建一个新素材,并消耗一次写入额度。每张照片能否处理成功由火山引擎决定。
认证链接可以先生成、晚点再发吗?
不建议。verification_id 从创建起 30 分钟过期。请在本人准备好认证时再创建会话。
id 和 uri 有什么区别?应该保存哪个?
两个都要保存。id(ast_...)是 YingTu 的查询凭证,只能用来调 GET 查状态,并且只对创建它的 Key 有效。uri(asset://...)是火山引擎里的素材地址,生成视频时用它。
数据库字段应该留多长?
ast_... 通常 250 个字符左右,ver_... 通常 300 个字符以上,而且长度不固定。请用 TEXT 这类不限长度的类型,不要用 VARCHAR(255),也不要假设固定格式。
为什么查询不存在的 id 返回 403,而不是 404?
id 是加密令牌,YingTu 服务端不保存素材列表。令牌解不开、被改过、属于别的 Key,或者路径类型不对,都统一返回 403 resource_not_owned。
POST 超时了,重试会不会重复创建?
会。接口没有幂等键,每次成功的 POST 都会新建一个素材或会话,并消耗一次写入额度。创建接口会同步等待火山引擎返回,客户端超时建议设为 60 秒以上。如果超时后重试,多出来的素材不用即可。
换了 API Key 以后,旧素材怎么办?
旧 Key 创建的 id 用新 Key 查询会返回 403。uri 本身不绑定 Key。建议换 Key 之前先把素材查到 Active,并保存好 uri。
有列表接口或删除接口吗?
目前没有。YingTu 不保存通过 API 创建的素材,请在你自己的系统里记录 id、uri 和业务信息。
能在浏览器前端直接调用吗?
不能。接口不返回 CORS 响应头,浏览器会拦截;而且 Key 放在前端会泄露。请由你的服务端调用。
这些接口收费吗?
素材接口目前不扣 LaoZhang 余额。用素材生成视频时,按 Seedance 视频生成的价格计费。
更新记录#
- 2026-09-25:文档按线上接口逐项核对后重写,补充接入流程、回调说明、完整错误码和常见问题。接口行为没有变化。