Blog/开发者工具/

抖音榜单监测 API 教程

用一个 SandBase 密钥监测抖音高赞榜并把每条视频解析成稳定的 aweme_id——轮询榜单、按 ID 去重,无需登录、无需 SDK。

深色电影质感画面:抖音榜单解析为稳定视频 ID,汇入 Agent 内核

抖音的榜单能露出此刻正在飙升的内容——但监测任务需要的不止是一张快照。你想按计划轮询榜单,识别出哪些视频已经见过,并把一切挂到一个稳定标识上,让你的去重和存储在多次运行之间保持干净。这篇教程接的正是这条链路:轮询高赞榜、把每条视频解析成规范的 aweme_id,再把一条去重后的流交给你的 Agent。

一个 SandBase API 密钥,不需要登录抖音,不需要 SDK。每个端点的 API 参考是其参数和响应信封(id/status/model/outputs[0].data)的权威来源;下面展示的业务载荷字段名只是一个示例结构、并非保证的 schema,请以一份真实响应为准核对。

要了解抖音完整的端点面,见 抖音公开数据 API 总览。准备动手?获取 SandBase API 密钥。

先说结论

  • 两个端点——douyin/billboard/hot-total-high-like-list 和 douyin/web/aweme-id——用同一个密钥读取。
  • 榜单把排名视频嵌在 data.data.objs 下并带上游 code;每个条目带 item_id、item_title、nick_name、like_cnt。
  • 把视频 URL 解析成规范的 aweme_id,让去重和存储都挂在一个稳定标识上。
  • 仅公开、只读数据。你这边无需发帖、无需登录;用 SandBase API 密钥鉴权即可。

方案

  1. 轮询榜单。 调用 douyin/billboard/hot-total-high-like-list,检查上游 code,从 data.data.objs 读排名视频。
  2. 解析稳定 ID。 对每条视频调用 douyin/web/aweme-id 拿规范的 aweme_id。
  3. 去重并做增量。 维护一个已见 ID 集合;只输出自上次运行以来新增的条目。

SandBase 抖音 API 页面,展示 billboard 簇及端点路径 SandBase 上的抖音 API 页面——billboard 簇里包含高赞榜端点。

第 1 步 —— 轮询榜单

每个 SandBase Model API 调用都是向 /v1/api/<vendor>/<path> 发一个 POST,把密钥放在 Authorization 头里。读 outputs 前先按 status 分支判断,而且——因为榜单包了一层——读 data.objs 前先检查上游 code。

import os
import requests

SANDBASE = "https://api.sandbase.ai/v1/api"
HEADERS = {
    "Authorization": f"Bearer {os.environ['SANDBASE_API_KEY']}",
    "Content-Type": "application/json",
}


def call(path: str, body: dict) -> dict:
    resp = requests.post(f"{SANDBASE}/{path}", headers=HEADERS, json=body, timeout=90)
    resp.raise_for_status()
    out = resp.json()
    if out.get("status") != "completed":
        raise RuntimeError(out.get("error", {}).get("message", f"{path} 未完成"))
    return out["outputs"][0]["data"]


def billboard() -> list[dict]:
    data = call("douyin/billboard/hot-total-high-like-list", {})
    if data.get("code") != 0:
        raise RuntimeError(data.get("message", "抖音上游错误"))
    # 参考只保证信封,业务字段随端点而定、以真实响应为准。
    return data.get("data", {}).get("objs", [])

下面是一个示例响应结构——字段名并非保证的 schema、以真实响应为准,请把字段和取值当作示例,并对照真实响应核对,因为负载会随时间变化:

{
  "id": "d4727a3b-18ea-403d-b90b-db093210ecac",
  "status": "completed",
  "model": "douyin/billboard/hot-total-high-like-list",
  "outputs": [
    {
      "data": {
        "code": 0,
        "data": {
          "objs": [
            { "item_id": "…", "item_title": "…", "nick_name": "…", "like_cnt": 0, "item_url": "…" }
          ]
        }
      }
    }
  ]
}

某个抖音 billboard 端点的 SandBase API 参考,展示带厂商前缀的 URL 和响应结构 端点 API 参考是每个参数名和响应路径的事实来源。

第 2 步 —— 解析稳定的 aweme_id

榜单条目带一个 item_id,但做监测你想要的是分享 URL 所解析出的规范 aweme_id——它才是你用来去重和存储的标识。把视频 URL 喂给 douyin/web/aweme-id,它以纯字符串返回该 ID:

def resolve_aweme_id(video_url: str) -> str:
    return call("douyin/web/aweme-id", {"url": video_url})

因为 call 已经按 status 分支过了,这个端点把 ID 字符串直接返回在 data 下。传一个抖音视频或分享 URL,你就拿回一个可以跨运行信赖的规范 ID。

某个抖音 aweme-id 端点的 SandBase API 参考,展示 URL 到 ID 的解析 aweme-id 端点把视频 URL 解析成一个规范的 aweme_id 字符串。

第 3 步 —— 跨运行去重与增量

现在是监测循环。在运行之间维护一个已见 ID 集合(这里放内存;生产里用存储托底)。每次轮询,解析 ID,丢掉见过的,只输出新增的条目:

SEEN: set[str] = set()


def poll_new() -> list[dict]:
    fresh = []
    # 参考只保证信封,业务字段随端点而定、以真实响应为准。
    for i, obj in enumerate(billboard()):
        item_url = obj.get("item_url")
        if not item_url:
            continue
        aweme_id = resolve_aweme_id(item_url)
        if aweme_id in SEEN:
            continue
        SEEN.add(aweme_id)
        fresh.append(
            {
                "aweme_id": aweme_id,
                "rank": i + 1,
                "title": obj.get("item_title"),
                "author": obj.get("nick_name"),
                "likes": obj.get("like_cnt"),
            }
        )
    return fresh


for entry in poll_new():
    print(f"新增 #{entry['rank']} {entry['title']} — {entry['author']}({entry['likes']} 赞)")

按计划跑,每次执行只打印发生变化的部分。因为一切都挂在规范的 aweme_id 上,一条在多次轮询之间都留在榜上的视频会被识别为同一项——不会重复告警、不会重复存储。

为什么这里统一信封很关键

两个端点回来时用的是相同的 { id, status, model, outputs } 信封,所以脆弱的那部分——鉴权、传输、status 处理、重试——只在 call 里写一次,两个都复用。榜单把行嵌在 data.data.objs 下、藏在上游 code 后面;aweme-id 端点把一个裸字符串返回在 data 下。这就是你代码需要知道的仅有两条每端点事实,而且两条都能在一次真实响应里看到。

正是这种分离让监测器容易扩展。想给每个新条目补更多细节?在同一个辅助函数后面再加一个调用,用你已经解析出的 aweme_id 作键,去重逻辑不用变。你的注意力停在监测信号上——什么刚开始流行——而不是停在解析怪癖上。当你需要的不止是榜单和 ID 时,去抖音总览里查到合适的端点,并在接入前确认它的参数。

局限与边界

  • 仅公开、只读数据。 不发帖、不关注、不涉及私有或仅账号可见的数据。
  • 负载结构与上游 code。 榜单嵌在 data.data.objs 下、藏在 code 后;aweme-id 在 data 下返回字符串。先看一次真实响应、读一遍结构。
  • 速率与量级。 把响应当作尽力而为的读取;遇到 HTTP 429 等瞬时错误时按退避策略重试,并控制轮询节奏。
  • 以线上参考核对端点。 可用性和字段可能变化;在依赖某个具体端点前先确认。
  • 这不是官方合作。 SandBase 提供对公开数据的统一访问;请尊重抖音条款和适用规则。

常见问题

我需要抖音登录吗? 不需要。你用自己的 SANDBASE_API_KEY 向 SandBase 鉴权。这些读取端点不需要你这边有抖音账号或 OAuth。

榜单已经有 item_id 了,为什么还要解析 aweme_id? 规范的 aweme_id 是分享 URL 所解析出的标识——用它去重和存储,能让你的键保持一致,无论你从哪种 URL 形态起步。

我该多久轮询一次? 按你的需要控制节奏,并用退避处理瞬时错误。把榜单当作尽力而为的读取,而不是实时流。

动手搭

创建一个 SandBase API 密钥,轮询榜单,解析 ID,跨运行做增量。准备好后: