跳到主要内容

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. 1

    你的服务端

    调用 POST /api/seedance-assets/real-persons/verifications,传入 callback_url。保存返回的 verification_id(和你的用户关联),把 verification_url 交给本人。

  2. 2

    本人

    在 30 分钟内打开 verification_url,在火山引擎官方页面确认授权并完成活体检测。

  3. 3

    火山引擎 → YingTu → 本人的浏览器

    认证流程结束后,本人的浏览器先跳到 YingTu 的中转地址,再被 302 重定向到你的 callback_url,URL 上附带 verification_id 和 result_code。

  4. 4

    你的服务端

    回调页被打开时(或者定时轮询),调用 GET /api/seedance-assets/real-persons/verifications/{verification_id},直到 status 为 verified。

  5. 5

    你的服务端

    在 expires_at 之前调用 POST /api/seedance-assets/real-persons,提交本人清晰正面照的地址,拿到素材 id 和 uri(状态为 Processing)。

  6. 6

    你的服务端

    轮询 GET /api/seedance-assets/real-persons/{id} 到 Active,然后把 uri 作为 reference_image 用于视频生成。

回调是本人浏览器的页面跳转,不是服务器通知。YingTu 不会向你的服务器发送任何 webhook。本人认证后直接关掉页面,你的回调页就不会被打开,所以一定要能靠轮询 GET 拿到结果。

基础约定#

项目取值说明
Base URLhttps://yingtu.ai下文所有路径都拼在这个域名后面。
认证Authorization: Bearer $LAOZHANG_API_KEYBearer 区分大小写,后面跟一个空格和完整的 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 8601expires_at 是 UTC 时间,例如 2026-09-25T08:30:00.000Z。

YingTu 会拿你的 Key 请求 LaoZhang 的 /v1/models 做校验,校验通过的结果在单个服务实例上缓存 5 分钟。所以刚停用的 Key 最多还能继续调用 5 分钟;LaoZhang 校验服务暂时不可用时返回 503 authentication_unavailable,稍后重试即可。

接口#

创建认证会话

POST/api/seedance-assets/real-persons/verifications

为一位需要授权的真人创建认证会话。每次调用都会新建一个会话,并计入写入额度。verification_url 交给本人打开;verification_id 由你的服务端保存。

Shell请求
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_urlstring是本人认证结束后浏览器要跳到的你的页面。必须是公网 HTTPS 地址,规则和 image_url 相同(见“创建真人素材”)。可以带查询参数(比如你的用户或订单号),跳转时原样保留;但同名的 verification_id、result_code 参数会被覆盖。
languagestring否认证页语言:zh(简体中文,默认)、en、zh-Hant。区分大小写,其它值返回 400。火山引擎目前只提供这三种语言,俄语、日语、韩语、西班牙语用户建议用 en。

请求体只能包含这两个字段。

HTTP响应 · 201 Created
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_idstring本次请求的追踪号。
verification_idstring认证会话 id,以 ver_ 开头。它是加密令牌,通常 300 个字符以上、长度不固定,并绑定当前 Key。保存在服务端,查询认证和创建素材都用它。
statusstring固定为 requires_user_action,表示等待本人去完成认证。
verification_urlstring火山引擎官方认证页地址(https://ark.volcengine.com/...),已按 language 带上 lng 参数。只交给本人,不要写进日志或公开页面。
callback_urlstring你传入的回调地址(经过 URL 规范化)。
expires_atstringverification_id 的过期时间,UTC,创建后 30 分钟。过期后查询认证和创建素材都返回 410。

查询认证状态

GET/api/seedance-assets/real-persons/verifications/{verification_id}

查询本人是否已通过认证。每次调用都会实时向火山引擎查询,不计入写入额度。建议在回调页被打开时查一次;没有结果就每 5 秒左右轮询,直到 verified 或过期。

Shell请求
# VERIFICATION_ID 是你保存的 verification_id(ver_...)
curl https://yingtu.ai/api/seedance-assets/real-persons/verifications/$VERIFICATION_ID \
  -H "Authorization: Bearer $LAOZHANG_API_KEY"
路径参数类型必填说明
verification_idstring是创建会话时返回的 verification_id,或回调 URL 上带回的 verification_id,两者都可以。
HTTP响应 · 200 OK · 尚未通过
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响应 · 200 OK · 已通过
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_idstring本次请求的追踪号。
verification_idstring原样返回你传入的值。
statusstringpending 或 verified,含义见“状态说明”。
expires_atstring这个 verification_id 的过期时间(UTC)。

pending 的 HTTP 状态码是 200,不是错误。过期后这个接口返回 410 verification_expired,不再返回状态。

创建真人素材

POST/api/seedance-assets/real-persons

认证通过后,用本人照片创建真人素材。必须在 verification_id 过期前调用。接口返回 202,素材在后台处理,之后和虚拟人素材一样查询状态。每次调用都计入写入额度。

Shell请求
# 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_idstring是状态已经是 verified 的会话 id,必须是同一个 Key 创建的。
image_urlstring是本人清晰正面照的公网 HTTPS 地址。照片里应当是完成认证的这个人。
namestring否素材名称,去掉首尾空格后 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";少数情况下火山引擎会在创建时直接拒绝,返回 502 upstream_error。
HTTP响应 · 202 Accepted
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_idstring本次请求的追踪号,格式为 req_ 加 32 位十六进制。找技术支持时提供它。
idstringYingTu 素材 id,以 ast_ 开头,只用于查询状态。它是加密令牌,通常 250 个字符左右、长度不固定,并绑定创建它的 API Key。原样保存,不要截断或解析。
objectstring固定为 seedance.asset。
kindstring固定为 real_person。
namestring素材名称。没有名称时为空字符串。
statusstring素材状态,原样来自火山引擎:Processing、Active 或 Failed。见“状态说明”。
uristring生成视频时使用的素材地址,格式为 asset://asset-...。创建后不会变化。原样使用,不要拼接或改写。
created_atstring可选。火山引擎返回的创建时间;上游没有返回时不出现。
updated_atstring可选。火山引擎返回的最近更新时间。
failureobject可选。只在火山引擎报告错误时出现,形如 { "code": "...", "message": "..." },两个字段都可能是空字符串。

查询真人素材

GET/api/seedance-assets/real-persons/{id}

查询真人素材的最新状态。每次调用都会实时向火山引擎查询,不计入写入额度。只能用创建时的同一个 Key 查询。素材 id 不会过期,认证会话过期不影响已经创建的素材。

Shell请求
# ASSET_ID 是创建接口返回的 id(ast_...)
curl https://yingtu.ai/api/seedance-assets/real-persons/$ASSET_ID \
  -H "Authorization: Bearer $LAOZHANG_API_KEY"
路径参数类型必填说明
idstring是创建真人素材返回的 id(ast_...)。不要传 verification_id、uri 或 asset-...,那样会返回 403。
HTTP响应 · 200 OK · 可用
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 交给本人。
pendingGET 查询还没有通过的认证结果。本人没打开链接、正在认证、中途放弃、认证没通过,都显示 pending。继续轮询到 expires_at。过期仍未通过,就创建新会话让本人重试。
verifiedGET 查询本人已通过认证。在 expires_at 之前调用 POST /real-persons 创建素材。
HTTP 410GET 查询和创建素材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:

Text跳转过程
# 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_idYingTu 新签发的认证会话 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错误响应
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
  }
}
HTTPcode什么时候出现怎么处理可重试
400invalid_request请求体不是 JSON 对象、缺少必填字段、字段值不合法,或带了接口没有列出的字段。message 会指出是哪个字段。按 message 修改请求。原样重试不会成功。否
401invalid_api_key没有 Authorization 头、格式不是 Bearer <key>,或 LaoZhang 判定 Key 无效、已停用。检查 Key 和请求头格式。否
403resource_not_ownedid 或 verification_id 不是当前 Key 创建的;或被截断、改动;或把虚拟人 id 拿到真人路径查询。不存在和不属于你都返回 403,不会返回 404。确认用的是创建时的同一个 Key、路径类型正确、id 原样未改。否
403capability_unavailable火山引擎侧的素材权益暂不可用。这是 YingTu 服务账号的问题,不是你的 Key。带上 request_id 联系技术支持。否
409verification_pending用还没通过认证的 verification_id 调用 POST /real-persons。先用 GET 查到 verified 再创建。不要反复 POST 重试:每次 POST 都会消耗写入额度。是
410verification_expiredverification_id 已超过 expires_at。GET 查询和创建素材都会返回。重新创建认证会话,让本人重新认证。否
413payload_too_large请求体超过 16 KB。精简请求体。正常请求远小于这个上限。否
415unsupported_media_typePOST 请求的 Content-Type 不是 application/json。加上 Content-Type: application/json。否
429rate_limit_exceeded当前 Key 超过每小时 12 次写入;或火山引擎侧限流。有 Retry-After 时等待对应秒数;没有时(上游限流)用指数退避重试。是
500internal_errorYingTu 内部未预期的错误。带上 request_id 联系技术支持。POST 可能已部分执行,重试有可能产生重复素材。否
502upstream_error火山引擎返回错误,或返回的数据不完整(例如没有素材 ID、认证链接没通过安全检查)。以 retryable 为准:true 时稍后重试;false 时通常是请求内容被上游拒绝,修改后再试。视情况
503authentication_unavailable暂时无法向 LaoZhang 校验 Key。稍后重试。是
503service_unavailableYingTu 服务端配置暂不可用。稍后重试;持续出现请联系技术支持。是

另外两种不是 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 技术支持。
Text成功的 POST 响应头
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 文档 为准。
ShellcURL · 创建视频任务
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 步通常由回调页触发,这里用轮询代替。

PythonPython · requests
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.jsJavaScript · Node.js 18+
// 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:文档按线上接口逐项核对后重写,补充接入流程、回调说明、完整错误码和常见问题。接口行为没有变化。