用一个 API 做小红书选品调研:一套实用工作流 | SandBase
搭一套小红书选品调研工作流:搜商品、读商品、拉它的评价——一把 SandBase key,不用小红书登录。

在小红书做选品调研,归根到底是三个动作:在一个品类里搜商品、打开一个商品、读买家怎么评价它。这篇教程用 SandBase 小红书 API 把这三个动作串成一套小红书选品调研工作流——仍需一把 SandBase key,但不用小红书登录,也不用爬虫。端点参考只保证响应信封(id、status、model、outputs[0].data);下面展示的业务字段是示例结构,不是保证的 schema,请以真实响应为准核对。
如果你想先看完整的端点全景,从 小红书公开数据 API hub 开始。这一篇是落地的工作流。
先说结论
- 三步:
search-products(发现)→product-detail(商品)→product-reviews(评价)。- 每次都是
POST /v1/api/xiaohongshu/<path>,一把SANDBASE_API_KEY;响应共用{ id, status, model, outputs }信封。- 搜索接一个
keyword;商品端点接一个你从搜索结果里提取的sku_id。- 翻页是各端点自己的请求参数,不是把返回值再传回:搜索用
page(从 1 开始)加search_id;product-reviews用sku_id,page从 0 开始;product-recommendations用cursor_score。- 只是公开、只读数据;仍需一把 SandBase key,但你这边不用小红书登录,也不能发帖。
工作流全貌
| 步骤 | 端点 | 输入 | 你拿到 |
|---|---|---|---|
| 1. 搜商品 | xiaohongshu/app-v2/search-products | keyword(+ page、search_id) | 商品搜索布局 |
| 2. 打开商品 | xiaohongshu/app-v2/product-detail | sku_id | 结构化的商品详情 |
| 3. 读评价 | xiaohongshu/app-v2/product-reviews | sku_id | 该商品的评价条目 |
端点的 API 参考是每个参数名和响应路径的权威来源。
第 1 步 —— 搜商品
先写一个判 status 的帮助函数,再搜一个品类关键词:
import os
import requests
BASE = "https://api.sandbase.ai/v1/api/xiaohongshu"
HEADERS = {
"Authorization": f"Bearer {os.environ['SANDBASE_API_KEY']}",
"Content-Type": "application/json",
}
def call(path: str, payload: dict) -> dict:
resp = requests.post(f"{BASE}/{path}", headers=HEADERS, json=payload, timeout=90)
resp.raise_for_status()
body = resp.json()
if body.get("status") != "completed":
raise RuntimeError(body.get("error", {}).get("message", "request did not complete"))
return body["outputs"][0]["data"]
search = call("app-v2/search-products", {"keyword": "面膜", "page": 1})
# search 可能是一个结构化的商品布局(container/module);
# 看一份真实响应,提取你想要的 sku_id,若有 search_id 则一并读出
layout = search.get("data")
search_id = search.get("search_id")
search-products 的响应可能把一个结构化布局包在 data 下(含 container/module);具体结构是示例,依赖前先看一份真实响应。翻页是请求参数:传 page(从 1 开始),如果响应返回了 search_id,下一次请求就把它和 page 一起传回。迭代之前先提取你关心的商品的 sku_id——这个布局可能比一个扁平列表要丰富。
第 2 步 —— 打开商品
用从搜索里提取的 sku_id,读商品详情:
detail = call("app-v2/product-detail", {"sku_id": sku_id})
# detail 可能携带一个上游 { code, data, msg } 信封;
# 用 .get() 防御式访问——这些业务字段是示例结构,不是保证的 schema
if detail.get("code") == 0:
product = detail.get("data")
product-detail 通常返回一个上游的 { code, data, msg, success } 风格信封;请把这些字段名当作示例结构,并以一份真实响应为准核对。用一个从搜索结果里提取的有效 sku_id——一个未知 id 往往会返回非零 code。读 detail.get("data") 之前先判 code == 0。
product-detail 在上游 data 对象下返回商品;先判 code。
第 3 步 —— 读评价
对同一个 sku_id 拉评价,衡量口碑:
reviews = call("app-v2/product-reviews", {"sku_id": sku_id, "page": 0})
# 用 .get() 防御式访问——这些业务字段是示例结构,不是保证的 schema
if reviews.get("code") == 0:
items = reviews.get("data")
product-reviews 也接一个 sku_id,并用 page 翻页(这个端点的 page 从 0 开始)。它通常用一个上游的 { code, data, msg } 风格信封;只有外层信封(id、status、model、outputs[0].data)是保证的。下面是一个示例响应结构——请把字段名和数值当作示例,以一份真实响应为准核对:
{
"id": "…",
"status": "completed",
"model": "xiaohongshu/app-v2/product-reviews",
"outputs": [
{
"data": {
"code": 0,
"success": true,
"data": { "…": "…" }
}
}
]
}
要一个汇总视角,xiaohongshu/app-v2/product-review-overview 对同一个 sku_id 返回一个评价概览(评分分布、好评率、评价标签)。
product-reviews 在上游 data 对象下返回评价条目。
把它串起来
一次最小的选品调研过程长这样:
search = call("app-v2/search-products", {"keyword": "面膜"})
# 按 schema 从搜索布局里提取 sku_id
report = []
for sku_id in extract_sku_ids(search): # extract_sku_ids: 你为搜索布局写的解析器
detail = call("app-v2/product-detail", {"sku_id": sku_id})
if detail.get("code") != 0:
continue # 跳过商品端点解析不出的 id
overview = call("app-v2/product-review-overview", {"sku_id": sku_id})
report.append({"sku_id": sku_id, "detail": detail.get("data"), "reviews": overview.get("data")})
extract_sku_ids 是伪代码——等你看过一份真实响应后,按实际的搜索布局去实现它。因为每次调用共用同一个信封和同一个 call 帮助函数,加重试或速率退避是一处改动的事。当你需要的不止这些读取时,查线上小红书列表找到合适的端点,接入前先确认它的参数。
处理粗糙的边角
- 从搜索布局里提取
sku_id。search-products返回一个结构化的container/module布局,不是扁平列表——调商品端点之前,从真实响应里对准 sku id。 - 对上游
code分支。 商品端点透传一个{ code, data, msg }信封;非零code表示 id 没解析出来。读data之前先判code == 0。 - 也对
status分支。failed或timeout的请求带error而没有outputs。call帮助函数已经强制这一点。 - 尊重速率限制。 作为客户端韧性措施,遇到 HTTP 429 这类瞬时错误时用退避重试。
- 只是公开数据。 仍用一把 SandBase key 鉴权,但不用小红书登录、不发帖,也拿不到私密/仅账号可见的内容。
为什么在 API 层做这件事
你当然可以在浏览器里打开小红书手动抄商品数据,但那不 scale,也给不了你结构化数据。把这些调用排成定时任务,就把定性的浏览变成了可度量的信号:能按 sku_id 去重的商品、能做趋势的评价概览、以及能对比的品类。因为调用返回的是命名的 JSON 字段(在一个一致的信封后),每一轮都能干净地落进一张表,再和上一轮做 diff——某个关键词上新冒出的商品、评价口碑的变化,以及一个品类在推荐什么的变化。把评价文本变成洞察,是你在采集到的数据之上另跑的一个分析步骤。
组合这套工作流
同样的统一信封让它可组合。把品类关键词换成任意垂类,再加第四次读取——比如对一个 sku_id 调 product-recommendations(它用一个 cursor_score 请求参数翻页)——它就用同一个 call 帮助函数、同一套判 status 和 code 接进来。你也可以把漏斗放宽:一次过程里对多个品类关键词各跑一次 search-products,提取 sku id,先按评价概览排序,再拉完整评价,这样只对值得细看的商品花调用。因为这些读取共用一种结构,从一个快速脚本走到一个定时任务,基本上只是加个退避和一个存每轮结果的地方。
常见问题
我需要小红书登录或 OAuth 吗?
不需要。你用自己的 SANDBASE_API_KEY 向 SandBase 鉴权,搜商品、读商品以及拉评价,都不需要你这边有小红书账号或 OAuth。发起这些调用时你仍然要提供一把 SandBase API key。
这几个端点的翻页怎么做?
每个端点各有自己的请求参数——比如 product-reviews 接一个 page(从 0 开始),你每次调用递增它;而搜索的翻页值则带回到下一次 search-products 请求里。具体参数以各自的端点参考为准,迭代前先对着一份真实响应核对。
我能用这种方式读私密或仅账号可见的商品数据吗?
不能。这些是公开、只读的读取——拿不到私密或需登录才可见的数据。另外,端点参考只保证 { id, status, model, outputs } 这层信封(里面还套着上游的 { code, data, msg }),里面的业务字段会变,请以一份真实响应为准去对准。
下一步
你现在有了一套可复用的选品调研工作流,建立在公开、只读的调用上。