如何用 TwexAPI 分页获取 X 帖子的回复
回复是帖子变成对话的地方。如果你要衡量发布反馈、追踪社区反应,或准备审核数据,只看根帖不够。你需要保存回复、请求时使用的排序方式,以及能说明数据如何采集的 cursor 链。
新的集成建议使用 TwexAPI 的分页回复端点:POST /twitter/tweets/{tweet_id}/replies/page。它按页返回回复,避免一次性拉取大线程时出现超时风险。
端点与排序方式
发送 POST 请求到:
https://api.twexapi.io/twitter/tweets/<tweet_id>/replies/page路径参数是根帖 ID。JSON 请求体支持:
| 字段 | 是否必填 | 用法 |
|---|---|---|
sort_by | 否 | Recency、Relevance 或 Likes。一次导出保持同一个排序方式。 |
next_cursor | 否 | 第一页留空。下一页传入上一页响应中的 next_cursor。 |
第一页可能包含 root_tweet 和第一批回复 data。后续页面继续返回回复,以及 has_next_page 和 next_cursor。
获取第一页
如果想按时间线导出,使用 Recency。只有当你的分析目标就是排序或热门度时,再使用 Relevance 或 Likes。
curl --request POST \
--url https://api.twexapi.io/twitter/tweets/<tweet_id>/replies/page \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"sort_by": "Recency"
}'简化后的响应结构如下:
1{
2 "code": 200,
3 "msg": "success",
4 "root_tweet": {
5 "tweet_id": "1803006263529541838",
6 "text": "<root tweet text>"
7 },
8 "data": [
9 {
10 "tweet_id": "1234567890123456789",
11 "text": "<reply text>",
12 "created_at": "Mon Jul 01 12:34:56 +0000 2025",
13 "user": {
14 "screen_name": "example_user"
15 }
16 }
17 ],
18 "has_next_page": true,
19 "next_cursor": "cursor_from_this_response"
20}使用 next_cursor 继续翻页
当 has_next_page 为 true 时,把 cursor 传入下一次请求。分页过程中保持同一个 tweet_id 和 sort_by。
curl --request POST \
--url https://api.twexapi.io/twitter/tweets/<tweet_id>/replies/page \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"sort_by": "Recency",
"next_cursor": "cursor_from_this_response"
}'分页中途修改排序方式,会让结果很难解释。如果需要另一个排序视角,请单独跑一次导出,并清楚标注。
Python 导出脚本
下面脚本会把回复写入 JSONL,单独保存根帖,按 tweet_id 去重,并保存 page 元信息,方便后续审计。
1import json
2import time
3from datetime import datetime, timezone
4from pathlib import Path
5
6import requests
7
8TOKEN = "<your_bearer_token>"
9TWEET_ID = "<tweet_id>"
10SORT_BY = "Recency"
11URL = f"https://api.twexapi.io/twitter/tweets/{TWEET_ID}/replies/page"
12REPLIES_OUT = Path(f"{TWEET_ID}-replies.jsonl")
13ROOT_OUT = Path(f"{TWEET_ID}-root.json")
14META_OUT = Path(f"{TWEET_ID}-replies-meta.json")
15
16headers = {
17 "Authorization": f"Bearer {TOKEN}",
18 "Content-Type": "application/json",
19}
20
21cursor = None
22page_number = 0
23seen_reply_ids = set()
24page_log = []
25
26with REPLIES_OUT.open("w", encoding="utf-8") as replies_file:
27 while True:
28 payload = {"sort_by": SORT_BY}
29 if cursor:
30 payload["next_cursor"] = cursor
31
32 response = requests.post(URL, headers=headers, json=payload, timeout=30)
33 response.raise_for_status()
34 body = response.json()
35
36 page_number += 1
37
38 if page_number == 1 and body.get("root_tweet"):
39 ROOT_OUT.write_text(
40 json.dumps(body["root_tweet"], ensure_ascii=False, indent=2),
41 encoding="utf-8",
42 )
43
44 replies = body.get("data") or []
45 for reply in replies:
46 reply_id = reply.get("tweet_id") or reply.get("id")
47 if not reply_id or reply_id in seen_reply_ids:
48 continue
49 seen_reply_ids.add(reply_id)
50 replies_file.write(json.dumps(reply, ensure_ascii=False) + "\n")
51
52 page_log.append({
53 "page": page_number,
54 "items": len(replies),
55 "has_next_page": body.get("has_next_page"),
56 "next_cursor": body.get("next_cursor"),
57 })
58
59 if not body.get("has_next_page") or not body.get("next_cursor"):
60 break
61
62 cursor = body["next_cursor"]
63 time.sleep(1)
64
65META_OUT.write_text(json.dumps({
66 "tweet_id": TWEET_ID,
67 "sort_by": SORT_BY,
68 "exported_at": datetime.now(timezone.utc).isoformat(),
69 "unique_replies": len(seen_reply_ids),
70 "pages": page_log,
71}, ensure_ascii=False, indent=2), encoding="utf-8")
72
73print(f"Saved {len(seen_reply_ids)} replies to {REPLIES_OUT}")如果是定时任务,建议每成功读取一页就持久化最后一个 cursor。这样失败后可以从已完成的位置恢复,不必丢掉已采集页面。
建议保存哪些字段
至少保存:
- 原始 reply 对象。
tweet_id、created_at、正文、作者字段,以及响应中存在的互动计数。- 响应中返回的
in_reply_to、reply_to、conversation_id或相关 thread 字段。 - 根帖
tweet_id、sort_by、导出时间、页码和 cursor 元信息。 - 标准化回复 URL,例如
https://x.com/i/web/status/<reply_id>。
建议同时保留原始数据和规范化字段。回复 schema 可能变化,下游分析也经常会用到导出时没想到的字段。
分析注意事项
回复导出适合:
- 衡量产品发布或营销活动的早期反馈。
- 复盘公告后的客户担忧。
- 围绕某条帖子建立审核队列。
- 对比按时间和按点赞排序时讨论有什么不同。
- 为情绪分析或主题建模抽样人工标注。
不要把回复数据当成完整民意调查。回复会受到排序、删除、账号可见性和参与意愿影响。请保存足够上下文,让数据集边界清楚可见。
常见坑
- 只拉第一页,就当成完整对话。
- 分页中途修改
sort_by。 - 丢掉
root_tweet,导致回复数据集失去上下文。 - 不做语言识别或人工复核,直接跑情绪分析。
- 覆盖互动数却不保存时间戳。
小结
采集回复时,使用 POST /twitter/tweets/{tweet_id}/replies/page,保持 sort_by 稳定,用 next_cursor 持续翻页,并保存根帖和原始回复行。相比一次性固定数量请求,这样更容易审计、重试和解释。