HAiNA CLI docs
接入指南

TikTok 发布全流程

上传视频 → 发布到 TikTok → 轮询发布状态

TikTok 发布全流程

流程总览

上传视频(拿 videoUrl,7 天有效)→ 发布(拿 shareId)→ 轮询状态(直到 publish_complete / publish_failed)

背后发生的事:你提交发布后,TikTok 会自己来下载这个 URL(不需要你的服务器长期在线)→ 转码 → 内容审核 → 上架。所以「发布」是异步的,提交成功 ≠ 发布成功,一定要轮询到终态。

新版发布接口(1.5.x 起默认):TikTok 发布默认走新版接口;CLI 加 --via beervid 走的是旧版接口(将要废弃——1.6.x 移除,移除前功能不受影响)。两版的参数、返回字段(publishId/shareId/status)、状态轮询(publish status --tt --publish-id,--share-id 为兼容别名)完全一致,对用户无感切换。

📉 官方提醒:发布后视频的清晰度和比特率可能比原文件略低——这是 TikTok 转码时的正常现象(取决于播放设备和网络),不是你的视频坏了。

下文示例中 $BASE 为生产环境地址(https://cli.haina.io),$TOKEN 为「API Keys」页创建的 API Token。

第 1 步:上传视频

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -F "file=@demo.mp4" \
  $BASE/v1/videos

返回 201:

{
  "code": 0,
  "data": {
    "videoId": "01a08b67-4422-78d-a3a214-0c0279fc",
    "videoUrl": "https://sm-cdn-assets.beervid.ai/....mp4",
    "expiresAt": "2026-09-17T06:40:39.716Z",
    "filename": "demo.mp4",
    "sizeBytes": 10904
  }
}

约束:mp4/mov,≤100MB(平台侧限制)。videoUrl 7 天有效,到期后该视频不可再用于发布(2005),需重新上传。

视频规则汇总(平台约束 + 官方硬性规格,不满足会在上传/发布阶段失败):

项规则
格式mp4 / mov(Content-Type video/mp4 / video/quicktime;官方还支持 webm,本平台只收前两种)
大小≤ 100MB(超限 2001;官方上限 1GB,本平台收紧)
TikTok 视频官方规格时长 3–600 秒;宽高均 ≥360px;帧率 23–60 FPS(不满足会在发布阶段被官方拒)
TikTok 视频有效期videoUrl 上传后 7 天有效(平台计时);到期不可发布(2005),需重新上传
TikTok Shop 视频fileId 一次性消耗:发布调用即作废(失败也不可复用),重发需重新上传;无 URL 过期概念
发布频次TikTok 发布:官方限制每账号 ≤6 个/分钟、≤15 个/天(批量发布请自行控制节奏,超限会被官方拒绝);TikTok Shop 挂车:平台侧配额(不同账号不同),超限官方报错,发布成功响应可能带 quota 字段提示剩余配额
TikTok Shop 预审配额50 次/天/账号(API 渠道独立计数;同一 fileId 重复提交会多次消耗);耗尽返回 2002,次日恢复

第 2 步:发布

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "videoUrl": "https://sm-cdn-assets.beervid.ai/....mp4",
    "accountId": "<坐席里 TikTok 绑定的 businessId>",
    "caption": "视频文案 #话题",
    "disableComment": false
  }' \
  $BASE/v1/publish/tiktok

videoUrl 就是第 1 步上传响应里的 videoUrl 原值——平台会校验它属于你的视频库且在 7 天有效期内(查无 1005,过期 2005)。

可选字段均有默认值,按需覆盖即可:caption(视频文案,≤2200 字符,支持 #话题 和 @提及——注意 @提及 仅对互关好友生效,且在 App 内显示为纯文本)、isBrandOrganic / isBrandedContent(品牌标记,默认 false)、disableComment / disableDuet / disableStitch(互动开关,默认 false = 不禁用)、封面二选一(thumbnailOffset 视频内取帧毫秒,默认 0 = 首帧,别填超过视频时长的值,官方会直接判发布失败;customThumbnailUrl 自定义图片封面——用 POST /v1/images 上传拿到的 imageUrl,传了它 thumbnailOffset 被官方忽略)。逐字段说明见 API 参考 · POST /v1/publish/tiktok。

返回 201:

{
  "code": 0,
  "data": {
    "publishId": "01a08b67-845d-77d-bc0938-aa21d13e",
    "capability": "TT",
    "shareId": "v_pub_url~v2.7683890605927450638",
    "status": "publish_complete"
  }
}

记住 shareId——查状态要用。每次发布都会自动记入后台「发布记录」页,随时可查。 状态值已归一化(小写):submitted → publish_processing → publish_complete / publish_failed(官方错误为 beervid_error)。

第 3 步:轮询状态

curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/v1/tiktok/v1.3/business/publish/status?publishId=v_pub_url~v2.7683890605927450638"

publishId 即第 2 步响应的 shareId(官方 publish_id)。命中你的发布记录时,每次查询都会顺带更新发布记录(归属校验 + 回写自动完成)。旧路径 GET /v1/publish/tiktok/status?shareId= 将要废弃(1.6.x 移除)。

{
  "code": 0,
  "data": {
    "publishId": "…",
    "shareId": "v_pub_url~v2.7683890605927450638",
    "status": "publish_complete",
    "postId": "7234567890123",
    "beervid": { "status": "PUBLISH_COMPLETE", "post_ids": ["7234567890123"] }
  }
}

建议轮询间隔 5–10 秒,最多 ~30 次,到终态(publish_complete / publish_failed / beervid_error)即停。每次查询都会顺带更新你的发布记录。

几个实用细节:

  • postId(TikTok item_id)在 publish_complete 后自动写入发布记录——但官方的数据处理可能延迟约 3 分钟,刚成功没拿到 postId 时过几分钟再查一次即可。
  • 拿到 postId 就能做数据回收(见第 4 步)。
  • 发布失败时,失败原因在响应的 beervid.reason(官方原因码,如 frame_rate_check_failed = 帧率不合规,改视频参数后重传再发)。
  • 官方状态里还可能出现 SEND_TO_USER_INBOX(草稿箱场景)——本平台不开放草稿发布,正常不会遇到,看到它说明官方把帖子放进了创作者收件箱。

第 4 步(可选):取视频数据

发布成功后,拿 status 响应里的 postId(TikTok item_id)拉播放/互动指标——走「数据洞察」面(与官方接口路由一致):

curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/v1/tiktok/v1.3/business/video/list?account=<businessId>&videoIds=7234567890123"

返回 data.videos[] 为官方原样字段(video_views/likes/comments/shares/reach 等,字段可得性取决于授权 scope;可用 fields 自选字段集)。account 为坐席里 TikTok 绑定的 businessId 或 username。

旧端点 GET /v1/publish/stats?itemId=(将要废弃,1.6.x 移除)仍可用,但请优先用上面的新版路由。

图片帖发布

图片帖(photo post)走新版端点(旧版无此能力):

haina tt photo publish --account <businessId> \
  --photo-urls "https://…/a.jpg,https://…/b.jpg" \
  --privacy-level PUBLIC_TO_EVERYONE \
  --title "标题" --caption "描述" --wait

关键口径(官方文档实证):

  • photo_urls 1-35 张公网 https 图(每张 ≤20MB,JPG/JPEG/WebP);--privacy-level 必填(可用档位先查 haina tt insight video-settings --account … 的 privacy_level_options)。
  • --draft 发草稿(进 TikTok App 收件箱,坐席授权需含 video.upload scope)。
  • 配额硬限(图片与视频同额):每账号 6 帖/分钟、15 帖/天;超限官方报错透传。
  • URL 所有权验证:2023-11 起未验证域名拒发;Beervid OSS 域名已验证,自有域名需在 TikTok 侧先做 URL 资源所有权验证。
  • 状态轮询与视频完全同口径(publish status --tt --publish-id,草稿终态为 publish_draft_inbox)。

发布辅助(写 caption / 挂地域标签前查询):

haina tt hashtag suggest --account <id> --keyword summer   # 话题推荐(10 条,含播放量)
haina tt location search --account <id> --query "new york" # 地域标签(id+name 配对用于 --location-id/--location-name)

BGM:给视频挂官方商用音乐(CML)

视频帖和图片帖都可以挂 TikTok 官方商用音乐库(CML)的 BGM,分两步:先选曲,再发布。

选曲(discovery music)

haina tt music trending --account <id> --genre POP --country US --date-range 7DAY

返回 ≤100 条热门曲目(可按流派/地区/时间窗筛选)。发布要用的 ID 是表格里的 trendingSongClipId(近 30 天最热剪辑)或 fullSongClipId(完整曲目)——不是 commercialMusicId(那是曲目 id,仅供选曲参考查询):

--json 消费的取值路径:songClipId 在嵌套对象里——data.list[].trendingSongClip.songClipId / data.list[].fullDurationSongClip.songClipId(表格两列是它们的 flatten 展示)。注意 fullDurationSongClip 可能为空对象 {}(此时只能用 trendingSongClip);曲目顶层 duration 对剪辑曲目可能为 0,真实时长看 clip 对象的 duration。

haina tt music trending-videos --account <id> --commercial-music-id <曲目id>  # 看这首曲子的热门视频(选曲参考)

试听直接打开响应里的 previewUrl(不过期)。

发布时挂 BGM

haina tt video publish --video-url <url> --account <id> \
  --music-id <trendingSongClipId> \
  --music-volume 50 --original-sound-volume 100 \
  --wait

图片帖指定曲目:haina tt photo publish ... --music-id <songClipId>(自动配乐用 --auto-add-music)。

必读的三个坑

  1. 音量默认是静音:官方 API 音乐音量和原声音量默认都是 0(静音),App 内默认是 50。本平台对齐 App——传了 --music-id 但没给音量时自动按 50/50;显式传 0 以你为准。要保留视频原声(口播人声)必须 --original-sound-volume > 0。注意这与 TikTok Shop 挂车的 --music-id(电商库)语义不同:挂车侧是完全覆盖原声,CML 是混音(音乐/原声两个音量滑杆)。
  2. CML ≠ App 流行曲库:热门版权流行歌不对 Business 账号开放(App 内也一样)。要流行歌只能发草稿(--draft)后在 App 内手动加。
  3. 音乐裁剪:--music-start / --music-end 毫秒;不传 --music-end 默认取到视频时长(10s 音乐配 2s 视频只放前 2s)。

查询发布记录

curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/v1/publish/records?capability=TT&page=1&pageSize=20"

On this page