Skip to content

Seedance_V3原生(SC)官方 SDK(AK/SK)接入 ​

已有火山方舟 SDK 或按火山 V4 规范签名的客户端,可不改代码接入本站素材库与真人认证(/sc/*):把 SDK 的 endpoint 指向本站,AK 与 SK 均填平台令牌即可。

适用范围:素材库与真人认证的全部接口(SC 素材库/真人认证)。视频生成任务接口(/v3/video/tasks、/v1/videos)使用 Bearer 鉴权,不支持 AK/SK,见 SC 生成视频。

SDK 配置 ​

配置项值
endpoint / base_url$URL/sc(含 http:// 或 https://,末尾不加斜杠)
AK(Access Key)平台令牌(令牌页展示的完整 sk- 值)
SK(Secret Key)平台令牌(与 AK 同一个值)
regioncn-beijing
serviceark

火山 Go SDK 使用 WithEndpoint 设置本站地址;SDK 会自动完成请求签名。

签名协议 ​

与火山 V4 签名协议一致。自行实现签名的客户端须满足:

  • X-Date:UTC 时间 yyyyMMdd'T'HHmmss'Z' 格式,与服务端时钟偏差不超过 5 分钟;
  • X-Content-Sha256:最终发送的请求体原始字节的 SHA-256 小写 hex(GET 请求对空体计算),且必须参与签名(签名后不要重新序列化请求体);
  • Authorization:
HMAC-SHA256 Credential={AK}/{yyyyMMdd}/cn-beijing/ark/request, SignedHeaders=content-type;host;x-content-sha256;x-date, Signature={hex}
  • 密钥派生:HMAC(SK, 日期) → HMAC(·, cn-beijing) → HMAC(·, ark) → HMAC(·, "request"),再对 StringToSign 签名;
  • CanonicalRequest 由请求方法、路径、规范化的查询串(按 key 排序、严格百分号编码)、规范化的签名头、SignedHeaders 与请求体哈希组成。

签名校验失败返回 SignatureDoesNotMatch;时钟偏差超限返回 RequestTimeTooSkewed。验签通过后,令牌有效性、账号状态、IP 限制等校验与 Bearer 方式完全一致。

Python 手动签名 ​

不依赖 SDK、仅用标准库实现的通用签名函数,覆盖素材 RPC 与真人认证 REST(GET/POST):

python
import hmac, hashlib, json, time
from urllib.request import Request, urlopen

KEY = "sk-..."  # 平台令牌,AK=SK 同一个值
HOST = "你的网关地址"

def pct(s):
    safe = set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~")
    return "".join(c if c in safe else "".join("%%%02X" % b for b in c.encode()) for c in s)

def signed_request(method, path, query="", body_obj=None):
    body = b"" if body_obj is None else json.dumps(body_obj).encode()
    t = time.gmtime()
    xdate = time.strftime("%Y%m%dT%H%M%SZ", t)
    short = time.strftime("%Y%m%d", t)
    payload_hash = hashlib.sha256(body).hexdigest()
    sh = ["content-type", "host", "x-content-sha256", "x-date"]
    ch = (f"content-type:application/json\nhost:{HOST}\n"
          f"x-content-sha256:{payload_hash}\nx-date:{xdate}\n")
    q = {}
    for kv in query.split("&"):
        if kv:
            k, _, v = kv.partition("=")
            q.setdefault(k, []).append(v)
    cq = "&".join(f"{pct(k)}={pct(v)}" for k in sorted(q) for v in sorted(q[k]))
    cr = "\n".join([method, path, cq, ch, ";".join(sh), payload_hash])
    scope = f"{short}/cn-beijing/ark/request"
    sts = "\n".join(["HMAC-SHA256", xdate, scope,
                     hashlib.sha256(cr.encode()).hexdigest()])
    k = KEY.encode()
    for e in (short, "cn-beijing", "ark", "request"):
        k = hmac.new(k, e.encode(), hashlib.sha256).digest()
    sig = hmac.new(k, sts.encode(), hashlib.sha256).hexdigest()
    url = f"https://{HOST}{path}" + (f"?{query}" if query else "")
    req = Request(url, data=body if method == "POST" else None, method=method,
                  headers={"Content-Type": "application/json", "X-Date": xdate,
                           "X-Content-Sha256": payload_hash, "Host": HOST,
                           "Authorization": f"HMAC-SHA256 Credential={KEY}/{scope}, "
                                            f"SignedHeaders={';'.join(sh)}, Signature={sig}"})
    return json.load(urlopen(req, timeout=30))

def rpc(action, body):
    return signed_request("POST", "/sc/", f"Action={action}&Version=2024-01-01", body)

素材组操作 ​

python
# 创建素材组
r = rpc("CreateAssetGroup", {"Name": "我的素材组", "GroupType": "AIGC"})
group_id = r["Result"]["Id"]

# 查询 / 更新
rpc("GetAssetGroup", {"Id": group_id})
rpc("UpdateAssetGroup", {"Id": group_id, "Description": "品牌宣传素材"})

# 删除
rpc("DeleteAssetGroup", {"Id": group_id})

素材操作 ​

python
# 上传素材(异步:返回 Id 后需轮询到 Active)
r = rpc("CreateAsset", {
    "GroupId": group_id,
    "URL": "https://example.com/photo.jpg",   # 公网可达
    "AssetType": "Image",                      # Image / Video / Audio
    "Name": "参考图",
})
asset_id = r["Result"]["Id"]

# 轮询处理状态
import time
while True:
    r = rpc("GetAsset", {"Id": asset_id})["Result"]
    if r["Status"] != "Processing":
        break
    time.sleep(3)
assert r["Status"] == "Active", r.get("Error")
asset_url = r["URL"]   # 引用 URL(约 12 小时有效),供任务 content 引用

# 更新名称 / 删除
rpc("UpdateAsset", {"Id": asset_id, "Name": "新名称"})
rpc("DeleteAsset", {"Id": asset_id})

列表与分页 ​

python
# 页码式
r = rpc("ListAssets", {"Filter": {"GroupType": "AIGC"}, "PageNumber": 1, "PageSize": 10})
items, total = r["Result"]["Items"], r["Result"]["TotalCount"]

# 游标式:MaxResults 起步,携带 NextToken 续页,直到响应无 NextToken
token, all_items = None, []
while True:
    body = {"Filter": {"GroupType": "AIGC"}, "MaxResults": 20}
    if token:
        body["NextToken"] = token
    r = rpc("ListAssets", body)["Result"]
    all_items += r["Items"]
    token = r.get("NextToken")
    if not token:
        break

ListAssetGroups 用法相同(Filter 不含 Statuses)。

真人认证 ​

REST 与 RPC 两种方式二选一,凭据不通用。

REST 方式(verification_id):

python
# 创建会话:保存 verification_id,在浏览器打开 h5_url 完成人脸认证
r = signed_request("POST", "/sc/api/seedance/face-verifications",
                   "", {"return_url": "https://your.site/back"})
verification_id, h5_url = r["verification_id"], r["h5_url"]

# 查询结果:status=verified 后使用 group_id(LivenessFace 素材组)
r = signed_request("GET", f"/sc/api/seedance/face-verifications/{verification_id}")
if r["status"] == "verified":
    liveness_group_id = r["group_id"]

RPC 方式(BytedToken):

python
# 创建会话(CallbackURL 必填,认证完成回调携带 resultCode=10000)
r = rpc("CreateVisualValidateSession", {"CallbackURL": "https://your.site/callback"})
byted_token, h5_link = r["Result"]["BytedToken"], r["Result"]["H5Link"]

# 查询结果:认证通过后 Result 返回 GroupId
r = rpc("GetVisualValidateResult", {"BytedToken": byted_token})
liveness_group_id = r["Result"].get("GroupId")   # 未完成时可能为空,稍后再查

认证通过后拿到的 LivenessFace 素材组即可用于 CreateAsset 上传真真人像素材;浏览器跳回 return_url / 收到回调不代表成功,以查询接口为准。

错误处理 ​

错误统一为 { "ResponseMetadata": { "Error": { "Code", "Message" } } } 信封(REST 真人认证为 {"error": {...}})。常见 Code:

Code说明
SignatureDoesNotMatch签名不符:确认 AK/SK 均填平台令牌、对最终发送的原始请求体签名
RequestTimeTooSkewedX-Date 与服务器时钟偏差超 5 分钟,校准本机时间
InvalidParameter.DownloadFailed素材 URL 不可公网访问
AccessDenied操作了不属于自己的资源(素材按客户隔离)
GroupIdRequired上传素材必须携带本人 GroupId

接口的 Action 列表、参数与分页说明见 SC 素材库/真人认证。

© 2026 API-RUN 版权所有