作者:Sorsa 编辑部

更新于 2026 年 7 月:新增了 100 次免费请求的起步选项,并刷新了官方 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-tweetsPOST返回某个用户时间线的最新推文
一次最多 5,000 个账号/list-tweetsGET一次请求覆盖一个 X 列表的每个成员
一个关键词、话题标签或搜索查询/search-tweetsPOST完整操作符语法,支持 order: latest 获得时序结果
对某个用户名的 @提及/mentionsPOST专为提及追踪打造,带互动筛选

最让多数团队意外的接口是 /list-tweets。把 50 个账号放进单个列表、轮询列表接口,比逐个账号轮询把请求量削减了约 50 倍。同样的模式扩展到 500 或 5,000 个账号而请求量不变,列表本身你在 x.com 上创建。

第 1 级:追踪单个账号 {#level-1-track-a-single-account}

最简单的情况。你想在某个账号发帖的那一刻就知道:一个竞品、一位 CEO、一个监管机构、一位网红。适合低量监测,或在扩容前测试你的管道。这一级用 /user-tweets 接口

Python

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

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 列表

  1. 前往 X 列表并新建列表。
  2. 添加你想监测的账号(最多 5,000 个)。
  3. 把列表设为公开。私密列表无法通过 API 访问。
  4. 从 URL 复制 列表 ID。对于 https://x.com/i/lists/1234567890,ID 是 1234567890

第 2 步:轮询列表

python
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" 获得时序结果。

python
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)。例如,要追踪对你品牌的高互动英文提及、并跳过转推:

python
monitor_keyword('"your brand" min_faves:10 lang:en -filter:retweets', on_new_tweet)

几个在实时监测里物有所值的操作符:

  • min_faves:Nmin_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:5min_replies:2 的下限,在噪音到达你的回调之前就剥掉了大部分随手发的东西,让你留下至少有些关注度的帖子。如果你专门盯一个用户名的提及而非开放关键词,Twitter 提及 API为这种清理暴露了最丰富的筛选集(min_likesmin_repliesmin_retweets、日期界限)。

如果你的查询字符串开始显得笨重,搜索构建器 playground让你可视化地构造一个,并随手看到完整的操作符集。

把新推文推送到 Slack、Discord 或任意 HTTP 接口 {#push-new-tweets-to-slack-discord-or-any-http-endpoint}

轮询循环是生产者。回调是你决定每条新推文去向的地方。因为回调只是一个函数,同一个监测器可以路由到任何会说 HTTP 的东西。先讲 Slack,因为它是最常见的目的地,再讲几个快速变体。

通过 Incoming Webhook 发到 Slack

python
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

python
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

python
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 接口

python
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,60086,4002,592,000定制(高于 Enterprise)
5 秒72017,280518,400定制(略高于 Enterprise)
10 秒3608,640259,200Enterprise($899/月)
30 秒1202,88086,400Pro($199/月)
1 分钟601,44043,200Pro($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 或你的数据库。

python
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)都会发生。与其立即重试、把事情弄得更糟,不如带上限地逐步退避。

python
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 分类)。如果下游系统变慢,你的循环就会跟不上进度、延迟飙升。把新推文推进队列,在单独的工作进程里处理。

python
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}

要构建你自己的实时监测管道:

  1. 拿一个 API 密钥,从 Sorsa 仪表盘。一个密钥在所有接口上都能用,每个账号注册即送 100 次免费请求(一次性、无需绑卡),所以你可以在挑套餐之前先接好一个循环。
  2. 交互式测试,在 API playground 里:用已知用户名或列表 ID 打 /user-tweets/list-tweets,确认你看到实时结果。
  3. 复制本文里的一个循环(单账号、列表或关键词),换掉 API 密钥。
  4. 加一个回调,路由到你想要告警的任何地方(Slack、Discord、内部 API、队列)。
  5. 加上生产加固(状态持久化、退避、解耦处理),一旦基本循环稳定。

用间隔表估算你的月度用量、挑一个套餐、发布。快速上手指南端到端地走一遍第一次调用。如果你需要更高的速率限制、或超出标准套餐的用量,联系销售,我们会给你定一个定制额度。


审校:Keksich(Sorsa 创始人,X API 研究者)

本指南由构建并运营 Sorsa 的团队撰写和维护。Sorsa 是一个替代 Twitter/X API,自 2022 年起累计处理请求超 50 亿次。每个代码示例都对照实时的 /user-tweets/list-tweets/search-tweets 接口运行,轮询、退避和持久化模式取自我们在生产里跑的监测循环。成本对比反映 2026 年 4 月更新后生效的官方 X API 按量付费模型,定价和速率限制细节于 2026 年 7 月核实。更多关于团队的信息见我们的关于页面