作者:Sorsa 编辑部
更新于 2026 年 7 月:新增了 100 次免费请求的起步选项,并刷新了官方 X API 按量付费的成本对比。
目录
- 核心要点
- 为什么 2023 年后实时 Twitter 监测变难了
- 轮询对比流式:哪种方式适合你的情况?
- 为你监测的东西挑对接口
- 第 1 级:追踪单个账号
- 第 2 级:一次请求追踪最多 5,000 个账号
- 第 3 级:追踪关键词、话题标签和搜索查询
- 把新推文推送到 Slack、Discord 或任意 HTTP 接口
- 该多久轮询一次,成本如何?
- 生产加固:上线前要修的五件事
- 实战:一次品牌监测迁移
- 这套方法物有所值的用例
- 实时监测:成本对比官方 X API
- 常见问题
- 如何开始
核心要点 {#key-takeaway}
实时 Twitter 监测的原理是:每 5 到 30 秒轮询 REST 接口、把返回的推文 ID 与最后见过的 ID 比对,并把新推文推送到一条告警管道。要一次追踪许多账号,就把多达 5,000 个账号打包进一个 X 列表,轮询单个列表接口。
为什么 2023 年后实时 Twitter 监测变难了 {#why-real-time-twitter-monitoring-got-harder-after-2023}
实时 Twitter 监测曾经意味着一件事:开一条到过滤流(filtered stream)的持久连接、定义几条规则,让推文在发布时流进你的应用。2023 年的定价大改把过滤流变成了每月 $5,000 的 Pro 层级功能。2026 年初 X 走得更远,切换到一个按量付费模型,每一条返回的帖子都计费。一条持续监测的负载在撞上 200 万读取上限之前,就已经每月数千美元。对多数团队(独立开发者、品牌监测小组、交易机器人、新闻编辑室、获客机构)来说,那个定价扼杀了项目。实用的替代方案是你自己从朴素的 REST API 拉数据。
本指南把那种基于拉取的模式建在 Sorsa API 之上,这个替代 Twitter/X API 服务商在每次请求上返回新鲜的推文数据、用单个 API 密钥运行、没有 OAuth 流程也没有审批队列、每个套餐允许固定每秒 20 次请求,并让你把多达 5,000 个账号折进一次 /list-tweets 调用。读取访问在批量接口上从每 1,000 条推文 $0.02(以及每 1,000 份资料 $0.01)起,你可以用 100 次免费请求(一次性、无需绑卡、覆盖全部 40 个接口)测试整套东西。在约 300 毫秒的响应时间和正确的接口选择下,你可以在一条新推文发布后几秒内检测到它。对读密集型监测,其成本只是官方 API 收费的零头。关于服务商的更广对比,见我们的 Twitter API 替代方案概览。
关于术语的一点说明:本文刻意用“轮询”。轮询不是“延迟”的委婉说法。做对了,一个轮询循环会在你选的间隔加上 API 响应时间之内返回一条推文。如果你的循环每 5 秒对一个 300 毫秒的接口运行一次,你的最坏情况延迟约为 5.3 秒。那匹配甚至胜过大多数消费级流式产品,关键在于,这一切跑在固定费率的 REST 预算上。
轮询对比流式:哪种方式适合你的情况? {#polling-vs-streaming-which-approach-fits-your-case}
从社交平台拿数据有两种方式:基于推送(流式、webhook)或基于拉取(轮询)。多数工程师默认选流式,因为流式听起来更快。实际上,选择取决于三件事:你监测多少东西、你的延迟预算,以及你对连接状态的容忍度。
| 因素 | 轮询(REST) | 流式(WebSocket / 过滤流) |
|---|---|---|
| 到首条推文的延迟 | 间隔 + API 响应时间 | 连接延迟,通常 1 到 3 秒 |
| 设置复杂度 | 循环里的一次 API 调用 | 持久连接、重连逻辑、背压处理 |
| 认证 | 请求头里的 API 密钥 | OAuth 或令牌轮换 |
| 崩溃后恢复 | 从保存的游标续跑 | 重连、重放缓冲、去重窗口 |
| 成本模型 | 按请求 | 按返回的资源,或一份企业合同 |
| 最适合 | 1 到 5,000 个监测目标,容忍 1 到 30 秒的告警 | 亚秒级延迟、全量 firehose 摄取、毫秒级交易 |
当你需要在大关键词空间上做毫秒级告警、且你有工程能力去处理重连、缺口恢复和规则管理时,流式胜出。对其他一切(品牌聆听、新闻监测、获客、合规告警、中频交易信号、内容审核),轮询更简单、更便宜,也足够好。
轮询另一个被低估的优势:如果你的脚本崩了,会在下一个周期从停下的地方接着来。流式管道需要单独的重放缓冲,才能在中断时不丢数据。我们刻意不推出托管 webhook 产品;拉取模式把控制面留在你手里,固定每秒 20 次请求的速率限制给生产规模的循环留出了足够的余量。
为你监测的东西挑对接口 {#pick-the-right-endpoint-for-what-youre-monitoring}
第一个设计决定是挑匹配你目标形态的接口。四个接口几乎覆盖所有实时监测需求。
| 监测目标 | 接口 | 方法 | 原因 |
|---|---|---|---|
| 单个账号 | /user-tweets | POST | 返回某个用户时间线的最新推文 |
| 一次最多 5,000 个账号 | /list-tweets | GET | 一次请求覆盖一个 X 列表的每个成员 |
| 一个关键词、话题标签或搜索查询 | /search-tweets | POST | 完整操作符语法,支持 order: latest 获得时序结果 |
| 对某个用户名的 @提及 | /mentions | POST | 专为提及追踪打造,带互动筛选 |
最让多数团队意外的接口是 /list-tweets。把 50 个账号放进单个列表、轮询列表接口,比逐个账号轮询把请求量削减了约 50 倍。同样的模式扩展到 500 或 5,000 个账号而请求量不变,列表本身你在 x.com 上创建。
第 1 级:追踪单个账号 {#level-1-track-a-single-account}
最简单的情况。你想在某个账号发帖的那一刻就知道:一个竞品、一位 CEO、一个监管机构、一位网红。适合低量监测,或在扩容前测试你的管道。这一级用 /user-tweets 接口。
Python
import requests
import time
API_KEY = "YOUR_API_KEY"
USERNAME = "elonmusk"
POLL_INTERVAL = 5 # 秒
URL = "https://api.sorsa.io/v3/user-tweets"
HEADERS = {"ApiKey": API_KEY, "Content-Type": "application/json"}
last_seen_id = None
print(f"Monitoring @{USERNAME}...")
while True:
try:
resp = requests.post(URL, headers=HEADERS, json={"username": USERNAME})
resp.raise_for_status()
tweets = resp.json().get("tweets", [])
if tweets:
# 推文 ID 是 Snowflake 字符串。转成 int 做安全比较,
# 因为字典序在 ID 长度边界处可能出错。
top_id = int(tweets[0]["id"])
if last_seen_id is None:
last_seen_id = top_id
print(f"Baseline set: {last_seen_id}")
else:
new_tweets = [t for t in tweets if int(t["id"]) > last_seen_id]
# 按时间顺序打印(最旧在前)。
for tweet in reversed(new_tweets):
print(f"[NEW] @{USERNAME}: {tweet['full_text'][:140]}")
if new_tweets:
last_seen_id = top_id
except requests.exceptions.RequestException as e:
print(f"Request error: {e}")
time.sleep(POLL_INTERVAL * 2)
continue
time.sleep(POLL_INTERVAL)
JavaScript
const API_KEY = "YOUR_API_KEY";
const USERNAME = "elonmusk";
const POLL_INTERVAL = 5000;
let lastSeenId = null;
console.log(`Monitoring @${USERNAME}...`);
while (true) {
try {
const resp = await fetch("https://api.sorsa.io/v3/user-tweets", {
method: "POST",
headers: { "ApiKey": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ username: USERNAME }),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const tweets = (await resp.json()).tweets || [];
if (tweets.length > 0) {
// BigInt 比较可避免 64 位 Snowflake ID 上的精度丢失。
const topId = BigInt(tweets[0].id);
if (lastSeenId === null) {
lastSeenId = topId;
console.log(`Baseline set: ${lastSeenId}`);
} else {
const newTweets = tweets.filter((t) => BigInt(t.id) > lastSeenId);
for (const t of [...newTweets].reverse()) {
console.log(`[NEW] @${USERNAME}: ${t.full_text.slice(0, 140)}`);
}
if (newTweets.length) lastSeenId = topId;
}
}
} catch (err) {
console.error(`Error: ${err.message}`);
await new Promise((r) => setTimeout(r, POLL_INTERVAL * 2));
continue;
}
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
}
上面代码里有两个细节要紧。第一,推文 ID 是 Snowflake 值、以字符串到达。把它们当字符串比较,在单个时间窗口内可行,但跨 ID 长度边界时很脆弱;在 Python 里转成 int、在 JavaScript 里转成 BigInt。第二,循环在第一次成功调用时建立一个基线,而不是把整条时间线倾倒出来。那避免了启动时的一波刷屏。
这能用,但扩展性很差。监测 50 个账号意味着 50 个独立的轮询循环和 50 倍的 API 请求。这就是 X 列表登场的地方。
第 2 级:一次请求追踪最多 5,000 个账号 {#level-2-track-up-to-5000-accounts-in-one-request}
X 列表是多账号监测最有用、也最被低估的工具。列表是公开的账号分组(最多 5,000 个),而 /list-tweets 接口在一次请求里返回所有成员合并后的最新推文。构建一次列表、把你的轮询器指向它,你就实际上建起了自己的定制 firehose,而无需为官方那个付费。
第 1 步:创建一个公开 X 列表
- 前往 X 列表并新建列表。
- 添加你想监测的账号(最多 5,000 个)。
- 把列表设为公开。私密列表无法通过 API 访问。
- 从 URL 复制 列表 ID。对于
https://x.com/i/lists/1234567890,ID 是1234567890。
第 2 步:轮询列表
import requests
import time
API_KEY = "YOUR_API_KEY"
LIST_ID = "YOUR_LIST_ID"
POLL_INTERVAL = 5
URL = f"https://api.sorsa.io/v3/list-tweets?list_id={LIST_ID}"
HEADERS = {"ApiKey": API_KEY, "Accept": "application/json"}
def monitor_list(callback, interval=POLL_INTERVAL):
"""轮询一个 X 列表,并为每条检测到的新推文调用 `callback`。"""
last_seen_id = None
print(f"Monitoring List {LIST_ID} (interval: {interval}s)")
while True:
try:
resp = requests.get(URL, headers=HEADERS, timeout=10)
resp.raise_for_status()
tweets = resp.json().get("tweets", [])
if not tweets:
time.sleep(interval)
continue
top_id = int(tweets[0]["id"])
if last_seen_id is None:
last_seen_id = top_id
print(f"Baseline set: {last_seen_id}")
else:
new_tweets = [t for t in tweets if int(t["id"]) > last_seen_id]
if new_tweets:
for tweet in reversed(new_tweets):
callback(tweet)
last_seen_id = top_id
except requests.exceptions.RequestException as e:
print(f"Request error: {e}. Retrying in {interval * 2}s")
time.sleep(interval * 2)
continue
time.sleep(interval)
def on_new_tweet(tweet):
user = tweet["user"]
print(f"[NEW] @{user['username']}: {tweet['full_text'][:120]}")
print(
f" Likes: {tweet.get('likes_count', 0)} | "
f"RTs: {tweet.get('retweet_count', 0)} | "
f"Views: {tweet.get('view_count', 'N/A')}\n"
)
if __name__ == "__main__":
monitor_list(on_new_tweet)
效率提升是惊人的。 以 10 秒间隔逐个监测 50 个账号,每天要花 43.2 万次请求(50 个循环,每个 8,640 次)。把这同样的 50 个账号放进一个 X 列表、轮询 /list-tweets,每天花 8,640 次请求。那是 50 倍的削减,而覆盖不损失。
一个坑:/list-tweets 每页返回最多 20 条推文。如果你的列表成员发帖如此频繁、以致单个轮询间隔内到达超过 20 条新推文,你就可能漏掉一些。两个修法:把间隔降到 2 到 3 秒,或通过 next_cursor 分页直到你到达先前见过的 ID。对多数用例(品牌监测、新闻编辑室、受众研究),每 5 到 10 秒 20 条推文的余量绰绰有余。
第 3 级:追踪关键词、话题标签和搜索查询 {#level-3-track-keywords-hashtags-and-search-queries}
基于账号的监测捕获已知来源说了什么。基于关键词的监测捕获任何人对你话题说了什么。用 /search-tweets 接口配合 order: "latest" 获得时序结果。
import requests
import time
API_KEY = "YOUR_API_KEY"
QUERY = '"your brand" OR @yourbrand lang:en'
POLL_INTERVAL = 10
URL = "https://api.sorsa.io/v3/search-tweets"
HEADERS = {"ApiKey": API_KEY, "Content-Type": "application/json"}
def monitor_keyword(query, callback, interval=10):
last_seen_id = None
print(f"Monitoring: {query} (interval: {interval}s)")
while True:
try:
resp = requests.post(
URL,
headers=HEADERS,
json={"query": query, "order": "latest"},
timeout=10,
)
resp.raise_for_status()
tweets = resp.json().get("tweets", [])
if tweets:
top_id = int(tweets[0]["id"])
if last_seen_id is None:
last_seen_id = top_id
print(f"Baseline set: {last_seen_id}")
else:
new_tweets = [t for t in tweets if int(t["id"]) > last_seen_id]
for tweet in reversed(new_tweets):
callback(tweet)
if new_tweets:
last_seen_id = top_id
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
time.sleep(interval * 2)
continue
time.sleep(interval)
真正的威力在查询字符串里。该 API 支持完整的 Twitter 高级搜索操作符集(一份非官方参考在 igorbrigadir/twitter-advanced-search)。例如,要追踪对你品牌的高互动英文提及、并跳过转推:
monitor_keyword('"your brand" min_faves:10 lang:en -filter:retweets', on_new_tweet)
几个在实时监测里物有所值的操作符:
min_faves:N、min_retweets:N过滤已在走红的内容。-filter:retweets、-filter:replies丢掉噪音。from:user1 OR from:user2不用列表就监测少数几个账号。(keyword1 OR keyword2) (problem OR issue OR broken)捕获带情绪的提及。near:"san francisco" within:25mi做地理范围监测。
2026 年的关键词流比过去更嘈杂:自动回复、诈骗账号和 AI 生成的垃圾内容堆在任何热门词上。互动阈值是你在源头最便宜的过滤器。一个像 min_faves:5 或 min_replies:2 的下限,在噪音到达你的回调之前就剥掉了大部分随手发的东西,让你留下至少有些关注度的帖子。如果你专门盯一个用户名的提及而非开放关键词,Twitter 提及 API为这种清理暴露了最丰富的筛选集(min_likes、min_replies、min_retweets、日期界限)。
如果你的查询字符串开始显得笨重,搜索构建器 playground让你可视化地构造一个,并随手看到完整的操作符集。
把新推文推送到 Slack、Discord 或任意 HTTP 接口 {#push-new-tweets-to-slack-discord-or-any-http-endpoint}
轮询循环是生产者。回调是你决定每条新推文去向的地方。因为回调只是一个函数,同一个监测器可以路由到任何会说 HTTP 的东西。先讲 Slack,因为它是最常见的目的地,再讲几个快速变体。
通过 Incoming Webhook 发到 Slack
import requests
SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
def send_to_slack(tweet):
user = tweet["user"]
text = (
f"*New tweet from @{user['username']}*\n"
f"{tweet['full_text']}\n"
f"Likes: {tweet.get('likes_count', 0)} | "
f"RTs: {tweet.get('retweet_count', 0)} | "
f"Views: {tweet.get('view_count', 'N/A')}\n"
f"https://x.com/{user['username']}/status/{tweet['id']}"
)
requests.post(SLACK_WEBHOOK_URL, json={"text": text})
# 接到任意监测器上:
monitor_list(send_to_slack)
# 或:monitor_keyword("bitcoin lang:en min_faves:50", send_to_slack)
在你的 Slack 应用设置里配置 Slack Incoming Webhook URL(官方 Slack 文档)。Discord、Telegram 或任何内部接口都是同样的模式。
Discord
DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/YOUR/WEBHOOK"
def send_to_discord(tweet):
user = tweet["user"]
content = (
f"**@{user['username']}** just tweeted:\n"
f"{tweet['full_text']}\n"
f"https://x.com/{user['username']}/status/{tweet['id']}"
)
requests.post(DISCORD_WEBHOOK_URL, json={"content": content})
Discord webhook 的设置文档见 discord.com/developers/docs/resources/webhook。
Telegram
TELEGRAM_BOT_TOKEN = "YOUR_BOT_TOKEN"
TELEGRAM_CHAT_ID = "YOUR_CHAT_ID"
def send_to_telegram(tweet):
user = tweet["user"]
text = (
f"New tweet from @{user['username']}\n\n"
f"{tweet['full_text']}\n\n"
f"https://x.com/{user['username']}/status/{tweet['id']}"
)
requests.post(
f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage",
json={"chat_id": TELEGRAM_CHAT_ID, "text": text},
)
任意自定义 HTTP 接口
def send_to_internal_api(tweet):
requests.post(
"https://internal.example.com/events/twitter",
json={
"tweet_id": tweet["id"],
"username": tweet["user"]["username"],
"text": tweet["full_text"],
"metrics": {
"likes": tweet.get("likes_count", 0),
"retweets": tweet.get("retweet_count", 0),
"views": tweet.get("view_count", 0),
},
"url": f"https://x.com/{tweet['user']['username']}/status/{tweet['id']}",
},
headers={"Authorization": "Bearer YOUR_INTERNAL_TOKEN"},
timeout=5,
)
你所建的,实际上是你自己的 webhook 中继。API 提供数据;你的回调决定谁听说每条新推文。自己拥有那个中继的好处是:所有路由规则都留在你的代码里,而非活在供应商的仪表盘里。这包括过滤、限速、扇出到多个渠道和重试策略。
该多久轮询一次,成本如何? {#how-often-should-you-poll-and-what-does-it-cost}
你循环的每个周期花一次请求,一次 Sorsa 请求无论打到哪个接口,都是你套餐里的一个单位。你选的间隔直接驱动你的月度用量,在按请求计费的定价下,用量干净地映射到一个套餐。
| 间隔 | 请求/小时 | 请求/天 | 请求/30 天 | 单个循环所需套餐 |
|---|---|---|---|---|
| 1 秒 | 3,600 | 86,400 | 2,592,000 | 定制(高于 Enterprise) |
| 5 秒 | 720 | 17,280 | 518,400 | 定制(略高于 Enterprise) |
| 10 秒 | 360 | 8,640 | 259,200 | Enterprise($899/月) |
| 30 秒 | 120 | 2,880 | 86,400 | Pro($199/月) |
| 1 分钟 | 60 | 1,440 | 43,200 | Pro($199/月) |
数字针对单个持续循环;并行跑多个循环会累加它们的请求数。套餐额度为 Starter 每月 1 万、Pro 10 万、Enterprise 50 万次请求,之上有定制额度。
从生产里跑这些循环得来的几条实用指引:
- 品牌聆听、新闻监测、获客: 10 到 30 秒足矣。你在发帖后半分钟内就捕获任何新推文,月度足迹很小。
- 金融信号检测、突发新闻机器人、交易工作流: 1 到 5 秒。你会烧掉更多请求,但延迟预算证明它值。
- 合规、审计、慢节奏研究: 1 到 5 分钟。对于行动窗口以小时计的用例,实时是杀鸡用牛刀。
单个列表上 30 秒到 1 分钟的节奏,落在每月 $199 的 Pro 套餐之内。收紧到 10 秒循环(约 25.9 万次请求)会把你挪到 $899 的 Enterprise。即便对高优先级目标做 1 秒循环,也仍远低于官方 X API 为可比实时访问所收的费。
生产加固:上线前要修的五件事 {#production-hardening-five-things-to-fix-before-going-live}
上面的示例刻意极简。在把其中一个指向生产流量之前,处理这五个关切。
1. 让 last_seen_id 跨重启持久化
如果你的脚本崩了、重启时不记得检查点,两件事会出错。脚本要么重新处理旧推文(给你的 Slack 频道发重复告警),要么设新基线、并静默地漏掉那个缺口。把检查点存到文件、Redis 或你的数据库。
import json
import os
STATE_FILE = "monitor_state.json"
def load_state():
if os.path.exists(STATE_FILE):
with open(STATE_FILE) as f:
return json.load(f).get("last_seen_id")
return None
def save_state(last_seen_id):
with open(STATE_FILE, "w") as f:
json.dump({"last_seen_id": last_seen_id}, f)
启动时加载,在每次更新游标的成功轮询之后保存。
2. 出错时指数退避
网络问题、瞬时 5xx 响应和撞上速率限制(HTTP 429)都会发生。与其立即重试、把事情弄得更糟,不如带上限地逐步退避。
retry_delay = POLL_INTERVAL
MAX_DELAY = 60
while True:
try:
resp = requests.get(URL, headers=HEADERS, timeout=10)
if resp.status_code == 429:
print(f"Rate limited. Backing off {retry_delay}s")
time.sleep(retry_delay)
retry_delay = min(retry_delay * 2, MAX_DELAY)
continue
resp.raise_for_status()
retry_delay = POLL_INTERVAL # 成功时重置
# 处理推文
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
time.sleep(retry_delay)
retry_delay = min(retry_delay * 2, MAX_DELAY)
continue
time.sleep(POLL_INTERVAL)
逐步退避、给延迟设上限,并在第一次成功时重置回你的正常间隔,好让一次短暂的 429 不会把你的监测器锁进慢节奏。
3. 把轮询与处理解耦
不要在轮询循环里同步跑昂贵的操作(情感打分、数据库写入、外部 API 调用、AI 分类)。如果下游系统变慢,你的循环就会跟不上进度、延迟飙升。把新推文推进队列,在单独的工作进程里处理。
from collections import deque
import threading
tweet_queue = deque()
def polling_loop():
"""快循环:轮询并入队。这里不做重活。"""
# 标准轮询代码,但不直接调用回调,而是:
# tweet_queue.append(tweet)
pass
def processing_worker():
"""单独线程:出队并分发。"""
while True:
if tweet_queue:
tweet = tweet_queue.popleft()
send_to_slack(tweet)
save_to_database(tweet)
else:
time.sleep(0.1)
threading.Thread(target=processing_worker, daemon=True).start()
polling_loop()
对更重的负载,把内存里的 deque 换成 Redis、RabbitMQ、SQS,或你技术栈里已经在跑的任何消息代理。
4. 健康检查,以及监测这个监测器
记录每个轮询周期:时间戳、新推文数、响应时间、错误。如果监测器在过去 N 分钟内没有完成一次成功轮询,就告警。静默失败是最昂贵的一种,尤其在告警管道里,那里“没有告警”意味着“什么都没发生”,直到有人注意到数据缺口。你可以在 Sorsa 状态页查看 API 的运营状态,在调试你自己的代码之前先排除平台问题。
5. 处理生产里会咬人的边缘情况
- 已删除的推文: 如果一条推文在你取回和你的回调触发之间被删除,那个 URL 会 404。把这当作预期之内,而非错误。
- 受保护账号: 如果被追踪用户转为私密,
/user-tweets会返回空列表。记录并继续。 - 置顶推文:
/user-tweets响应里的第一条推文往往是置顶推文,而非最新的。如果你在意严格的时间顺序,按created_at排序。 - 转推对比原创推文:
tweet["retweeted_status"]对转推会被填充。决定你想要两者还是只要原创。 - 回复受限:
is_replies_limited表示作者限制了回复。对某些监测用例是有用的信号。
实战:一次品牌监测迁移 {#in-practice-a-brand-monitoring-migration}
我们合作过的一家 SaaS 公司,自 2018 年起在官方过滤流上跑品牌监测。他们的设置追踪约 200 条关键词规则和 60 个优先账号,到 2024 年初,这套设置在 Pro 层级上每月要 $5,000。内部的说法很直白:砍掉这个流、省下这笔钱,然后祈祷别出岔子。
迁移花了两周。我们把关键词规则合并成两个复合的 /search-tweets 工作进程(规则用 OR 操作符收拢成布尔查询),每 30 秒轮询一次。那 60 个账号的关注则换成一个每 15 秒轮询的 X 列表。合并后的足迹约为每月 35 万次请求,舒服地落在每月 $899 的 Enterprise 套餐内,对比他们此前付的 $5,000。端到端告警延迟从过滤流上的约 2 秒,变成第 90 百分位约 15 秒。对在 Slack 里回应品牌提及的公关团队,这个延迟变化是看不见的。账单变化不是。
这套方法物有所值的用例 {#use-cases-where-this-approach-earns-its-keep}
我们最常见到的五个模式。
品牌聆听与社交 CRM。 每 15 到 30 秒对你的品牌用户名加产品名关键词轮询 /search-tweets。把带情感提示的消息路由到 Slack,公关团队就能在几分钟内回应。这是任何品牌与社交聆听设置的核心。
新闻与信号检测。 构建一个突发新闻用户名的列表(路透、美联社、彭博、地区媒体、条线记者),每 5 秒轮询。扇出到一个 Discord 服务器或交易仪表盘。这是 2026 年你能构建的最便宜版本的“新闻 firehose”。
竞争情报。 一个竞品账号加其 CEO 和产品负责人的列表,每 30 秒轮询。新推文落进一个共享频道,你的产品营销团队不用有人翻 40 个资料页就得到一条免费情报流。这是持续竞品追踪设置的实时层。
获客。 对问题陈述查询轮询 /search-tweets:"any recommendations for" (CRM OR analytics OR transcription)、"looking for an alternative to"、"we just churned from"。路由到一个由销售审阅的 Slack 频道。多数跑这个的团队,每个查询桶每周捕获 5 到 15 个合格线索。它是规模化 Twitter 获客背后的实时引擎。
加密 KOL 信号。 构建一个加密网红和项目账号的列表,以 2 到 5 秒轮询,并可选地用 Sorsa Score 接口按受众质量给信号加权。
实时监测:成本对比官方 X API {#cost-vs-the-official-x-api-for-real-time-monitoring}
披露:Sorsa 是我们的产品,所以请把这当作我们的看法,并用你自己的负载测试任何选项。两侧的数字都是真实的、截至 2026 年 7 月为最新。
两家服务商按完全不同的单位计费。官方 X API 按抓取的资源计费:在自 2026 年初生效的按量付费模型下,每条帖子读取花 $0.005,附在推文上的作者资料是单独的 $0.010 用户读取。Sorsa 按请求计费,一次请求返回约 20 条推文(或粉丝接口上最多 200 份资料)、含作者数据。那个结构性缺口正是驱动监测成本差异的东西。
| 官方 X API(按量付费,2026) | Sorsa | |
|---|---|---|
| 计费模型 | 按抓取的资源 | 按请求计费(1 次调用 = 1 次请求) |
| 帖子读取 | 每条帖子读取 $0.005 | 包含在请求里,无按帖收费 |
| 推文里的作者资料 | 单独计费,每次用户读取 $0.010 | 免费包含在推文响应里 |
| 全天候监测(每月约 170 万次帖子读取) | ~$8,600/月 | Enterprise 套餐,$899/月 |
| 每月读取上限 | 200 万次帖子读取,之后需 Enterprise | 按套餐额度,无按帖上限 |
| 超过上限 | Enterprise 合同,历史上约 $42,000+/月 | 提高额度的定制套餐 |
| 认证 | OAuth 2.0 + Bearer 令牌 | 请求头里单个 API 密钥 |
| 速率限制 | 因接口而异 | 每个套餐固定每秒 20 次请求 |
取舍是延迟:过滤流在一两秒内送达推文,轮询在你的间隔加约 300 毫秒响应时间内送达。对多数监测用例,那个差距看不见。对亚秒级交易机器人,它看得见,无论供应商是谁,一个真正的流才是对的工具。但对品牌、新闻或竞品规模的读密集型监测,按返回帖子付费累加得很快、并撞上 200 万读取的墙;按请求计费的套餐不会。关于按用例的完整定价拆解,见 2026 年 Twitter API 定价。
常见问题 {#faq}
REST 轮询真的算“实时”吗?
REST 轮询是近实时。延迟预算是轮询间隔加 API 响应时间,在快接口上约为 300 毫秒。在 5 秒间隔下,从一条推文发布到你的回调触发,最坏情况约为 5.3 秒。对绝大多数监测用例,那满足实时的工作定义;只有毫秒级交易和现场活动竞拍才需要一个真正的流。
一个 API 密钥能监测多少个账号?
用 Sorsa,一个 API 密钥可通过 X 列表监测实际上无限多的账号。单个列表容纳最多 5,000 个账号,每次轮询算一次 /list-tweets 请求。多个列表在固定每秒 20 次请求的限制内并行运行,这为单个密钥上数百个并发监测任务留出了空间。
撞上速率限制会怎样?
当你超出每秒 20 次请求的固定限制时,Sorsa 返回一个 HTTP 429 响应。退避一秒、重试,循环就继续;没有小黑屋或锁定。多数轮询节奏都远低于每秒 20 次请求,所以生产监测器很少会看到 429,更高的限制可按需提供。
脚本重启时如何避免重复告警?
要避免重复告警,在每次成功轮询之后,把最后见过的推文 ID 持久化到耐久存储(一个文件、Redis 或数据库)。启动时加载那个 ID 并用作基线,好让循环只分发比检查点更新的推文。没有持久化,一次重启要么重放旧推文,要么静默跳过缺口。
能监测私密或受保护账号吗?
没有工具能访问私密或受保护的 Twitter/X 账号,Sorsa 只呈现公开数据。如果被追踪账号在监测中途转为私密,接口返回空列表,轮询循环无错继续。这是一条平台级隐私规则,而非任何一家服务商特有的局限。
支持托管 webhook 吗?
Sorsa 不推出托管 webhook 产品;受支持的实时路径是本指南里的轮询模式,你自己的回调路由每条新推文。好处是在你自己的代码里完全掌控过滤、扇出和重试逻辑。专门想要供应商托管推送交付的团队,会看向官方 X API 在企业层级的 Account Activity webhook。
返回的数据有多新鲜?
数据在每次请求上都新鲜,你的调用和平台之间没有缓存层。如果一条推文半秒前发布,下一次轮询就取到它。加上约 300 毫秒的响应时间,正是这份新鲜度让轮询对监测(而非只是事后分析)可行。
能把实时监测和历史回填结合起来吗?
能,而且多数生产管道两者都做。用于监测的那些接口(/user-tweets、/search-tweets、/list-tweets)都接受一个 next_cursor 参数,向后翻历史做一次性回填,然后你切换到轮询循环去接收此后的新数据。回填那一侧见我们的历史 Twitter 数据指南。
如何开始 {#getting-started}
要构建你自己的实时监测管道:
- 拿一个 API 密钥,从 Sorsa 仪表盘。一个密钥在所有接口上都能用,每个账号注册即送 100 次免费请求(一次性、无需绑卡),所以你可以在挑套餐之前先接好一个循环。
- 交互式测试,在 API playground 里:用已知用户名或列表 ID 打
/user-tweets或/list-tweets,确认你看到实时结果。 - 复制本文里的一个循环(单账号、列表或关键词),换掉 API 密钥。
- 加一个回调,路由到你想要告警的任何地方(Slack、Discord、内部 API、队列)。
- 加上生产加固(状态持久化、退避、解耦处理),一旦基本循环稳定。
用间隔表估算你的月度用量、挑一个套餐、发布。快速上手指南端到端地走一遍第一次调用。如果你需要更高的速率限制、或超出标准套餐的用量,联系销售,我们会给你定一个定制额度。
审校:Keksich(Sorsa 创始人,X API 研究者)
本指南由构建并运营 Sorsa 的团队撰写和维护。Sorsa 是一个替代 Twitter/X API,自 2022 年起累计处理请求超 50 亿次。每个代码示例都对照实时的 /user-tweets、/list-tweets 和 /search-tweets 接口运行,轮询、退避和持久化模式取自我们在生产里跑的监测循环。成本对比反映 2026 年 4 月更新后生效的官方 X API 按量付费模型,定价和速率限制细节于 2026 年 7 月核实。更多关于团队的信息见我们的关于页面。