小红书 API Python 教程:笔记、博主和评论(2026)
小红书官方开放平台是给店铺用的,不能读公开笔记,所以这篇教程用 requests 直接调 okdata 的五个接口:小红书笔记搜索、笔记详情、博主信息、博主笔记列表和笔记评论。示例一共 5 次调用,花费 $0.10;失败的调用不扣费。
更新于
小红书有没有官方 API 能干这件事?小红书确实有开放平台,但那是电商开放平台:一类是店铺授权后使用的工具型应用(ERP、打单、上货、进销存),一类是店铺给自己系统做对接的自用型应用。蒲公英和广告工具服务的是投放自己项目的广告主。这些渠道都没有提供“搜索所有人的笔记”或“读别人笔记下的评论”的接口,每个渠道具体能做什么,见小红书有没有官方 API。
另一个坎是 token。小红书自己的网页接口会给展示出来的每篇笔记附一个 xsec_token,打开笔记时会校验它,所以脚本手里只有笔记 ID 时经常被拒。原理见 xsec_token 那篇问答。用下面这些接口,只传 ID 或链接,不用管 token、Cookie 和签名。
最后你会得到一个能复用的 Python 小客户端:一个负责报错和重试的函数、一个带页数上限的翻页器、五个接口,以及一份 JSON 报告。
分步操作
第一步:准备环境
需要 Python 3.9 以上、requests,以及放在环境变量里的 key。没有 SDK 要装。
pip install requests
export OKDATA_API_KEY=okd_...
每次调用都是对 https://api.okdata.online 加接口路径发 GET 请求,key 放在 Bearer 头里。成功时 "code" 为 "ok",数据在 data 里(列表接口是 data.items 加 page,单个对象是 data.item)。出错时返回 code、message、request_id 和 retry 提示:yes 表示稍后可以重试,有 retry_after_seconds 就按它等。
第二步:搜索笔记
小红书笔记搜索接收 query、sort(general、latest 或 popular)和 type(all、video 或 image)。一次调用返回约 20 篇笔记,每次 $0.02,每篇有 id、title、text 的开头部分、author、published_at、hashtags 和 metrics(点赞、收藏、评论、分享)。
curl -G "https://api.okdata.online/v1/xiaohongshu/notes/search" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'query=防晒霜' \
--data-urlencode 'sort=popular'
第三步:拉一篇笔记的全文
小红书笔记详情接收 id(24 位十六进制)或 url(笔记链接或 xhslink 分享链接),返回全文、全部图片(视频笔记只给封面)、话题和最新数据,每次 $0.02。加 view=full 时,ext.xsec_token 里是小红书给这篇笔记附的 token,想拼一个能在浏览器里打开的链接时用得上。
curl -G "https://api.okdata.online/v1/xiaohongshu/notes/get" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'url=https://www.xiaohongshu.com/explore/672a4f1d000000001b03c58e'
第四步:作者的主页信息
小红书博主信息的 id 传用户 ID(就是笔记的 author.id),或者 url 传主页链接,每次 $0.02。返回 followers、following、posts(笔记数)、likes(获赞与收藏,和主页上的口径一致)、bio、verified,以及小红书号 username。加 view=full 时,ext.ip_location 是主页上显示的属地。
第五步:作者的笔记列表
小红书博主笔记列表必须传用户 ID 作为 id,一次返回约 20 篇,从新到旧,每次 $0.02。这里每篇笔记的 author 只有 ID,因为查的就是这个账号。
第六步:笔记下的评论
小红书笔记评论必须传笔记 id,一次返回约 10 条一级评论,每次 $0.02,每条有 text、author、likes、replies、published_at 和 ip_location。
curl -G "https://api.okdata.online/v1/xiaohongshu/notes/comments/list" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'id=672a4f1d000000001b03c58e'
第七步:完整脚本
脚本跑的就是上面的示例,结果写进 xhs_report.json。call 对同一个请求的多次重试沿用同一个 Idempotency-Key,网络断了重发也不会扣两次钱。pages 给每个列表设了页数上限,也就等于给花费设了上限。
import json
import os
import time
import uuid
import requests
API = "https://api.okdata.online"
SESSION = requests.Session()
SESSION.headers["Authorization"] = f"Bearer {os.environ['OKDATA_API_KEY']}"
COMMENT_PAGES = 1 # 每页一次调用
AUTHOR_PAGES = 1
class ApiError(Exception):
def __init__(self, body):
super().__init__(f"{body['code']}: {body['message']} (request {body['request_id']})")
self.code = body["code"]
def call(path, **params):
key = str(uuid.uuid4()) # 重试沿用同一个 key:重放不会再次扣费
for attempt in range(3):
body = SESSION.get(API + path, params=params, headers={"Idempotency-Key": key}, timeout=60).json()
if body["code"] == "ok":
return body
if body.get("retry") == "yes" and attempt < 2:
time.sleep(body.get("retry_after_seconds") or 5)
continue
raise ApiError(body)
def pages(path, max_pages, **params):
"""逐页返回结果;到 max_pages 或 has_more 为 false 时停。"""
cursor = None
for _ in range(max_pages):
body = call(path, **params, **({"cursor": cursor} if cursor else {}))
yield from body["data"]["items"]
if not body["page"]["has_more"]:
return
cursor = body["page"]["next_cursor"]
found = call("/v1/xiaohongshu/notes/search", query="防晒霜", sort="popular")["data"]["items"]
if not found:
raise SystemExit("no notes for that query")
top = max(found, key=lambda n: n["metrics"]["likes"] or 0)
note = call("/v1/xiaohongshu/notes/get", id=top["id"])["data"]["item"]
author = call("/v1/xiaohongshu/users/get", id=note["author"]["id"])["data"]["item"]
recent = list(pages("/v1/xiaohongshu/users/notes/list", AUTHOR_PAGES, id=author["id"]))
comments = list(pages("/v1/xiaohongshu/notes/comments/list", COMMENT_PAGES, id=note["id"]))
report = {
"note": {"id": note["id"], "title": note["title"], "text": note["text"], "images": note["images"],
"likes": note["metrics"]["likes"], "saves": note["metrics"]["favorites"],
"comments": note["metrics"]["comments"], "published_at": note["published_at"]},
"author": {"id": author["id"], "name": author["name"], "followers": author["followers"],
"notes": author["posts"], "likes_and_saves": author["likes"]},
"recent_notes": [{"id": n["id"], "title": n["title"], "published_at": n["published_at"],
"likes": n["metrics"]["likes"]} for n in recent],
"comments": [{"text": c["text"], "likes": c["likes"], "replies": c["replies"],
"ip_location": c["ip_location"]} for c in comments],
}
with open("xhs_report.json", "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
要扩大规模,就调高上限、多循环几篇笔记。20 篇笔记拉全文、每篇读 3 页评论,是 $1.60。
注意事项
- 没有浏览量。 小红书公开笔记不显示浏览数,所以
metrics.views为 null,meta.unavailable里的原因是platform。排序时看收藏(favorites)和评论数。 - 一个人有两种 ID。 账号类接口要的是用户 ID(24 位十六进制),大家互相分享的是
username里的小红书号。主页链接和分享链接都可以当url传。 - 删掉或私密的不收钱。 已经不存在的笔记或账号返回
not_found,不扣费。脚本会抛出带这个 code 的ApiError,批量跑时捕获后跳过即可。 - 评论数对不上。 笔记上的评论数包含楼中楼回复,评论接口列的是一级评论,每条附带回复数。
- 时间是 UTC。 所有
published_at都是 UTC 的 ISO 8601 格式。 - 不读的字段可以不要。
fields=id,title,metrics.likes只保留这几个字段,view=compact每个对象返回更少字段。两者都不影响价格,但交给模型读时返回更短。
要按关键词批量导出评论,看分步教程按关键词批量导出小红书评论,从一次搜索直接到 CSV。搜索量大时怎么算钱,见小红书笔记搜索 API 多少钱。
示例花费
搜一次“防晒霜”,拉点赞最多那篇笔记的全文、作者信息和最新笔记,再读这篇笔记下第一页评论
| 接口 | 调用次数 | 单价 | 小计 |
|---|---|---|---|
| 小红书笔记搜索 | 1 | $0.02 | $0.02 |
| 小红书笔记详情 | 1 | $0.02 | $0.02 |
| 小红书博主信息 | 1 | $0.02 | $0.02 |
| 小红书博主笔记列表 | 1 | $0.02 | $0.02 |
| 小红书笔记评论 | 1 | $0.02 | $0.02 |
| 合计 | 5 | $0.10 |
按本页生成时的价格计算。失败的调用不扣费。
在你的 AI 里用(MCP)
先接好 MCP 服务,然后直接说你要什么。智能体会自己找到这些接口、看清价格,再用你的余额调用。
可以这样说在小红书按最热搜“防晒霜”,打开点赞最多的那篇笔记看全文,查一下作者的主页信息和最近发的笔记,再读这篇笔记下第一页评论,告诉我评论区大家在问什么。
智能体第一次调用 call 工具时发送的内容
{
"endpoint": "xiaohongshu.notes.search",
"params": {
"query": "防晒霜 推荐"
},
"idempotency_key": "<新的 UUID>"
}