作者:Sorsa 编辑部

2026 年 7 月更新:把成本对比围绕每 1,000 条推文费率重做、加入了 100 次免费请求的起步选项、刷新了官方 X API 的按读取定价,并澄清了 v2 接口静默丢弃哪些操作符。

要点

Twitter 搜索 API 让开发者用关键词和操作符过滤,以编程方式查询公开的 X 时间线。2026 年有两条现实路径:官方 X API v2 近期搜索接口(按量付费、操作符集有限),以及第三方 REST API(在带免费额度起步的固定月费套餐上,透传完整的网页操作符集)。

要做大规模的只读搜索,Sorsa API 这个 Twitter/X API 替代服务商,是我们推荐的选项,也是本文端到端覆盖的那个。其 /search-tweets 接口透传完整的网页操作符集,包括官方 v2 接口静默丢弃的 min_faves:min_retweets:min_replies: 互动过滤;所有套餐统一 20 次/秒,没有按接口窗口;注册即送 100 次免费请求(无需绑卡、覆盖全部 40 个接口),付费套餐固定月费,走批量时折合低至每 1,000 条推文 $0.02,没有开发者账号审批,几分钟就能配好。唯一不做的是写操作:发帖、点赞和私信留给官方 API。

X 的网页搜索栏用来打发时间还行。但真实场景往往更重:拉 5 万条匹配复杂布尔查询的推文、定期跑、把结果推进 Postgres 仓库、再让合规部门下季度审计整条数据管道。到这一步,网页搜索栏就撑不住了。那正是 Twitter 搜索 API 的用途。本指南走一遍搜索 API 在 2026 年怎么工作、真正生效的操作符、处理真实生产流量的代码,以及每个选项适合什么场景。

我们从 v1.1 时代起就在对着 Twitter 的搜索接口开发。穿过 2023 年定价大改、v2 迁移,以及 2026 年转到基于额度的按量付费,底层心智模型没怎么变:你发一个查询字符串,拿回 JSON,用一个游标分页。变的是你付给哪个服务商、付多少,以及哪些操作符在你的查询执行之前就被静默丢弃。最后这一点绊倒多数人。

目录

  1. 为什么以编程方式搜索推文?
  2. 2026 年 Twitter 搜索 API 格局
  3. /search-tweets 接口:请求剖析
  4. 响应里有什么
  5. 真正有效的搜索操作符
  6. 两套操作符:网页搜索 vs 官方 v2 API
  7. 你如何按话题标签搜索推文?
  8. 分页:采集数千条推文
  9. 可用代码:Python 和 JavaScript
  10. 真实世界查询模板
  11. 搜索推文 vs 追踪提及:用哪个接口?
  12. Sorsa 搜索如何对比官方 X API
  13. 常见错误与故障排查
  14. 常见问题
  15. 开始上手

为什么以编程方式搜索推文? {#why-search-tweets-programmatically}

一个搜索 API 存在,因为下面的用例无法靠手动浏览器刷新存活:

社媒聆听和品牌监测。 用交付进 Slack、看板或告警流程的结构化 JSON,追踪你产品或竞品的每一次公开提及。信号在量和趋势里,而不在任何单条推文里,这就是大规模社媒聆听的全部前提。

竞品和市场情报。 从竞品账号拉按互动过滤的推文、识别哪些帖子表现好,并为持续的竞品追踪建一个内容基准。把 from:competitor min_faves:100 -filter:replies 与日期窗口结合,逐季对比。

情感分析。 把推文文本喂进一个 transformer 模型打分。搜索 API 给你原始文本加指标(点赞、回复、浏览),这些指标在聚合情感时兼作置信度权重。完整管道在我们的 Twitter 舆情分析指南里。

获客。"looking for" (api OR tool) twitter 这样的搜索,能浮现出正在主动寻找你所售之物的人。一个精心构造的查询就是一份免费的潜客名单,这是 X 上获客的基础。

学术和新闻研究。 学术研究需要针对界定时间窗口的可复现、可审计查询。带 since:until: 的搜索接口是一手数据源,历史存档可回溯到 2006 年的第一条推文。

趋势检测。 在一个 5 分钟的 cron 上跑同一个查询、存下结果计数,并检测峰值。事件检测系统和加密情绪看板底层就是这么工作的,也是建在轮询循环上的实时监测背后的模式。

2026 年 Twitter 搜索 API 格局 {#the-twitter-search-api-landscape-in-2026}

不再有单一的“Twitter 搜索 API”了。现在有三个类别,在价格和能力上的差距比多数开发者以为的更大。

官方 X API v2

接口:/2/tweets/search/recent/2/tweets/search/all。截至 2026 年,X API 走基于额度的按量付费:帖子读取每条约 $0.005,用户(作者)读取每次约 $0.010 单独计费。没有免费版,也没有免费额度补贴,所以任何请求通过之前你都得先买额度。认证走 OAuth 2.0 bearer 令牌。

更难的问题是操作符覆盖。官方 v2 接口接受的操作符子集,比 x.com/search 的网页搜索栏小得多。生产团队依赖的互动过滤,包括 min_faves:min_retweets:min_replies:within_time:,只要你写进 v2 查询就会被静默忽略。我们见过团队在 min_faves: 过滤上搭好整条数据管道,直到发现这一点,才不得不重构。

官方存档搜索可用,但按企业预算定价。对多数只读研究和监测负载,这笔账算不过来。想看 X 定价一路怎么走到今天,我们的 Twitter API 定价拆解走了一遍每一次变更。

开源爬虫

Twikit、TweeterPy 和 XActions 截至 2026 年中仍可用。Twint 已经废了。twscrape 和 snscrape 在多数环境里坏了。这些还在维护的库对小的一次性任务管用,但它们建在公开网页搜索界面之上,X 每次微调前端就会坏。对需要可用性保证的生产环境数据管道,没一个靠得住。

第三方搜索 API

这是 Sorsa 所处的类别:在一个干净的 REST API 后面运营自己的爬取基础设施、暴露完整网页操作符集、并收可预测费率的服务。这个领域里还有别的服务商,如果你要的只是绝对最低的每调用价格、哪怕牺牲可靠性或完整性,其中某一家或许适合某个狭窄场景。而要以公道的固定价格拿到可靠、完整、只读的访问,Sorsa 是我们自己构建、运营并推荐的那个。

选我们的 /search-tweets 接口而非替代方案,理由归结为四件具体的事:一个不按接口类型把额度翻倍的固定月费套餐、原样透传、不静默丢弃的完整网页操作符集、每个套餐都一样的 20 次/秒,以及只用一个请求头(ApiKey: YOUR_KEY)、没有 OAuth 流程的认证。离开官方 API 的端到端走查,包括对只读选项的更全面考察,见我们的 Twitter API 迁移指南

/search-tweets 接口:请求剖析 {#the-search-tweets-endpoint-request-anatomy}

发一个 POST 请求到:

POST https://api.sorsa.io/v3/search-tweets

认证只需一个请求头:ApiKey: YOUR_API_KEY(区分大小写)。没有 bearer 令牌、没有 OAuth 折腾、没有要注册的回调 URL。

请求体

参数类型必需说明
querystring搜索关键词。支持完整的一套原生 X 搜索操作符。
orderstring"popular"(默认)匹配 X 搜索里的“热门”标签。"latest" 返回时间顺序、最新在前。
next_cursorstring来自前一次响应的分页游标。第一次请求时省略。

最小 cURL 示例

bash
curl -X POST https://api.sorsa.io/v3/search-tweets \
  -H "ApiKey: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "artificial intelligence",
    "order": "latest"
  }'

为什么用 POST 而非 GET?

搜索查询会变长。一旦加上布尔分组、排除、语言过滤和互动阈值,一个真实的品牌监测查询很容易超过 200 个字符。把它们放进 URL,就意味着要对每个操作符做 URL 编码,还得祈祷上游代理不截断。放进 JSON 请求体则完全避开了 URL 长度和编码问题。官方 v2 搜索接口反其道而行,用一个 GET 请求带一个 URL 编码的 query 参数。这也是长的官方 API 查询比人们预期更快撞上访问级字符上限(自助近期搜索为 512 个字符)的部分原因。

想在写代码之前可视化地构建查询,Sorsa 搜索生成器把同样的操作符渲染成一个表单,并输出你可以粘进脚本的查询字符串;在线试用工具 Playground 则对着你的密钥跑完整请求、不用写任何客户端代码。两者都在下面的“如何开始”一节里给了链接。

响应里有什么 {#whats-in-the-response}

接口返回一个带两个顶层字段的 JSON 对象:一个推文对象数组和一个分页游标。

json
{
  "tweets": [
    {
      "id": "2029914600217473314",
      "full_text": "The latest breakthroughs in AI are reshaping automation.",
      "created_at": "2026-03-06T13:38:49Z",
      "lang": "en",
      "likes_count": 142,
      "retweet_count": 38,
      "reply_count": 12,
      "quote_count": 5,
      "view_count": 28400,
      "bookmark_count": 19,
      "is_reply": false,
      "is_quote_status": false,
      "conversation_id_str": "2029914600217473314",
      "entities": [],
      "user": {
        "id": "1422280682240450563",
        "username": "tech_insider",
        "display_name": "Tech Insider",
        "description": "Breaking tech news and analysis.",
        "followers_count": 84200,
        "verified": true
      }
    }
  ],
  "next_cursor": "DAABCgABGSmiaxkAAgoAAgjEJ..."
}

这里有几件事要紧。

完整作者资料随每条推文一起返回。 在我们处理过的迁移里,最大的开发工时节省很少来自降价,而是来自不必再为每个推文作者做一次单独的 users/by/ids 查找。官方 v2 接口要求你加一个 expansions=author_id 参数,再走一遍 includes.users 数组把作者 ID 匹配回推文,而每一次作者读取都单独计费。在这个响应里,用户对象直接内嵌,没有额外成本。一次请求,内容和作者身份都有了。

互动指标不是可选字段。 点赞、转发、回复、引用、浏览和收藏,总是出现在每条推文上,不用记 tweet.fields=public_metrics

next_cursor 是你需要的唯一分页信号。 这个字段是字符串时,说明还有更多结果;它为 null 或缺失时,你已经到达结果集末尾。

Tweet 和 User 对象的完整字段参考,见 API 文档里的响应格式参考。

真正有效的搜索操作符 {#search-operators-that-actually-work}

X 的网页搜索支持一大套操作符,而下面这个高杠杆子集覆盖了大约 90% 的真实负载。我们有意把这份列表控制在实用范围;完整目录,包括地理操作符、来源过滤、卡片过滤和语言代码边缘情况,见我们的完整 Twitter 高级搜索语法速查表

关键词和短语

  • artificial intelligence 匹配含这些词中任意一个的推文
  • "artificial intelligence" 匹配精确短语
  • 词干提取默认开启:bear 也会匹配 bears

基于用户

  • from:elonmusk 由一个账号发布的推文
  • to:openai 回复一个账号的推文
  • @sorsa_app 提及一个账号的推文

互动过滤

  • min_faves:100 至少 100 个赞
  • min_retweets:50 至少 50 次转推
  • min_replies:10 至少 10 条回复

这三个是最值得知道的操作符。它们也正是官方 X API v2 会静默丢弃的那些,所以如果你从别的服务商迁移代码,可能会看到“低质量”过滤悄悄失效,却没有任何报错。

内容过滤

  • filter:mediafilter:imagesfilter:videosfilter:links
  • - 前缀来排除:-filter:retweets-filter:replies-filter:links

语言和日期

  • lang:en(任何 ISO 639-1 代码:esfrdeja 等等)
  • since:2026-01-01 在此日期当天或之后
  • until:2026-03-01 在此日期之前(不含)

布尔逻辑

  • (bitcoin OR ethereum) min_faves:100 lang:en 用括号分组
  • crypto -scam -airdrop 用一个减号前缀排除

关于操作符行为参考,GitHub 上社区维护的 igorbrigadir/twitter-advanced-search 仓库是最彻底的公开来源。

两套操作符:网页搜索 vs 官方 v2 API {#two-operator-sets-web-search-vs-the-official-v2-api}

X 上有两套不同的操作符,把它们混淆,是一个在一处“有效”的查询在另一处返回空的最常见原因。写查询之前,先明确你针对的是哪一套。

网页搜索操作符是 twitter.com/search、TweetDeck 和基于爬取的 REST API 所接受的。就是上一节那套,也是 Sorsa /search-tweets 接口原样透传的那套。

官方 X API v2 操作符是一个语法不同的更小子集。v2 接口用 has:mediahas:linksis:retweetis:reply,而不是 filter:media-filter:retweets。它还加了针对地理的 place_country:US 和针对话题与实体标注的 context:,但完全不接受互动过滤。在官方接口上,min_faves:min_retweets:min_replies:within_time:filter:blue_verified 都会被忽略,且不返回任何错误。

在 2026 年,有几个操作符在任何提供方上都被广泛误解或不可靠:

操作符人们弄错什么
filter:verified vs filter:blue_verifiedfilter:verified 匹配传统认证账号;filter:blue_verified 匹配付费的 X Premium 账号。对编辑信号你通常想要前者、而非后者。
within_time:7d一个从查询时间起测量的滚动窗口,所以同一个查询明天返回一个不同的集。对可复现数据集,改用显式的 since:until: 日期。
from:user vs @userfrom:user 返回该账号所写的推文;@user 返回任何提及该账号的推文,包括别人的回复和引用。
near:within:geocode:精确坐标地理标记基本已弃用,所以地理操作符现在覆盖减少、不应为完整性依赖它们。

清楚自己在用哪一套,能省下你几个小时,不用去调试一个语法没问题、却悄悄返回空的查询。

你如何按话题标签搜索推文? {#how-do-you-search-tweets-by-hashtag}

要通过 API 按话题标签搜索推文,把话题标签作为查询里的一个独立操作符传入,比如 #worldcup。话题标签单独就能用,也能和互动、语言、日期过滤结合来收窄结果集,这就是把一个嘈杂的话题标签变成可用数据集的办法。

在 Sorsa /search-tweets 接口上,体看起来像这样:

json
{
  "query": "#worldcup min_faves:50 lang:en -filter:retweets since:2026-06-01",
  "order": "latest"
}

这会返回携带 #worldcup、至少 50 个赞、在 6 月 1 日当天或之后发布的英语原创(非转发)推文。去掉互动过滤就取到原始量,抬高下限则只浮现传播开的帖子。

官方 X API v2 也把 #hashtag 当作一个查询词匹配,但有两点会绊住话题标签追踪项目。一是那个能让热门话题标签不至于把你淹没在噪音里的互动下限(min_faves:)用不了;二是近期搜索封顶在大约最近七天,除非你在一个更高承诺的存档档位上。如果你的目标是“每一条带这个话题标签、互动高于 N 的推文,回溯数月”,那么按请求计费的网页操作符路径才是真正能做到的那个。话题标签相关操作符的完整集(filter:hashtags、cashtag,以及仅媒体的语言代码)在前面链接的高级搜索语法速查表里。

分页:采集数千条推文 {#pagination-collecting-thousands-of-tweets}

单次搜索请求返回一页约 20 条推文。要拉更大的数据集,你用基于游标的分页。

逻辑是四步:

  1. 第一次请求。 发查询和 order。不要包含 next_cursor
  2. 读游标。 响应包含一个 next_cursor 字符串。
  3. 下次请求。 发同一个查询、同一个 order,加上你刚收到的 next_cursor 值。
  4. 重复、直到 next_cursornull、空或缺失。

这比基于偏移的分页更可靠,因为在你请求之间发布的新推文不会造成重复或跳过的结果。游标编码结果集里的一个位置、而非一个数字偏移。

深度分页的按日期范围分块

在你把 10 万条推文的采集托付给单个游标之前,有一个值得知道的边缘情况。

在一次匹配 bitcoin lang:en、共 20 万条推文的市场研究拉取里,单游标循环在大约第 70 页开始对更早的页返回重复,并在大约第 90 页完全停止推进。这不是我们 API 特有的:任何对着移动时间线的搜索索引,在你翻得足够深时都有这个毛病。

解决办法是把查询拆成按日期范围的块。不用一个跨全部时间的查询,而是为每一周跑同一个查询:

python
import datetime as dt
import time
import requests

API_KEY = "YOUR_API_KEY"
URL = "https://api.sorsa.io/v3/search-tweets"

def search_in_window(base_query, since, until, max_pages=50):
    """在一个 since/until 窗口内分页。"""
    full_query = f"{base_query} since:{since} until:{until}"
    cursor = None
    out = []
    for _ in range(max_pages):
        body = {"query": full_query, "order": "latest"}
        if cursor:
            body["next_cursor"] = cursor
        resp = requests.post(
            URL,
            headers={"ApiKey": API_KEY, "Content-Type": "application/json"},
            json=body,
        )
        resp.raise_for_status()
        data = resp.json()
        out.extend(data.get("tweets", []))
        cursor = data.get("next_cursor")
        if not cursor:
            break
        time.sleep(0.1)
    return out

def search_chunked(base_query, start, end, days_per_chunk=7):
    """按块走过一个日期范围。"""
    all_tweets = []
    cursor_date = start
    while cursor_date < end:
        next_date = min(cursor_date + dt.timedelta(days=days_per_chunk), end)
        chunk = search_in_window(
            base_query,
            cursor_date.strftime("%Y-%m-%d"),
            next_date.strftime("%Y-%m-%d"),
        )
        all_tweets.extend(chunk)
        print(f"{cursor_date} -> {next_date}: {len(chunk)} tweets")
        cursor_date = next_date
    return all_tweets

tweets = search_chunked(
    "bitcoin lang:en",
    dt.date(2026, 1, 1),
    dt.date(2026, 4, 1),
    days_per_chunk=7,
)

bitcoin lang:en 这类嘈杂查询上,每周一块通常能让游标干净地走到完成、不出现重复漂移。更安静的查询可以拉长到每月一块。极高量的查询(想想没有其他过滤的 lang:en)可能要每天一块。

关于更多分页模式和感知速率限制的重试逻辑,见我们的 Twitter API 速率限制指南

可用代码:Python 和 JavaScript {#working-code-python-and-javascript}

下面的示例是我们对着线上接口跑的生产模式:游标分页、带指数退避的 429 重试,以及一个尊重 20 次/秒上限的小批处理间隔。

Python

python
import requests
import time

API_KEY = "YOUR_API_KEY"
URL = "https://api.sorsa.io/v3/search-tweets"

def search_tweets(query, order="popular", max_pages=5, max_retries=3):
    """
    带游标分页和 429 退避搜索推文。

    Args:
        query: 搜索字符串(支持 X 操作符)。
        order: "popular" 或 "latest"。
        max_pages: 要取的最大页数。
        max_retries: 放弃一页之前对 429 的重试次数。

    Returns:
        推文 dict 的列表。
    """
    all_tweets = []
    next_cursor = None

    for page in range(max_pages):
        body = {"query": query, "order": order}
        if next_cursor:
            body["next_cursor"] = next_cursor

        for attempt in range(max_retries):
            resp = requests.post(
                URL,
                headers={"ApiKey": API_KEY, "Content-Type": "application/json"},
                json=body,
            )
            if resp.status_code == 429:
                wait = 2 ** attempt
                print(f"Rate limited. Sleeping {wait}s.")
                time.sleep(wait)
                continue
            resp.raise_for_status()
            break
        else:
            print(f"Page {page + 1} failed after {max_retries} retries.")
            break

        data = resp.json()
        tweets = data.get("tweets", [])
        all_tweets.extend(tweets)
        print(f"Page {page + 1}: {len(tweets)} tweets (total {len(all_tweets)})")

        next_cursor = data.get("next_cursor")
        if not next_cursor:
            print("End of results.")
            break

        time.sleep(0.1)

    return all_tweets


# 用法
tweets = search_tweets('"Sorsa API" min_faves:5 lang:en', max_pages=10)
for t in tweets:
    u = t["user"]
    print(f"@{u['username']} ({u['followers_count']} followers)")
    print(f"  {t['full_text'][:120]}")
    print(f"  L:{t['likes_count']} RT:{t['retweet_count']} V:{t.get('view_count', 'N/A')}")

JavaScript(Node.js)

javascript
const API_KEY = "YOUR_API_KEY";
const URL = "https://api.sorsa.io/v3/search-tweets";

async function searchTweets(query, order = "popular", maxPages = 5, maxRetries = 3) {
  const allTweets = [];
  let nextCursor = null;

  for (let page = 0; page < maxPages; page++) {
    const body = { query, order };
    if (nextCursor) body.next_cursor = nextCursor;

    let data;
    for (let attempt = 0; attempt < maxRetries; attempt++) {
      const resp = await fetch(URL, {
        method: "POST",
        headers: { "ApiKey": API_KEY, "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      if (resp.status === 429) {
        const wait = Math.pow(2, attempt) * 1000;
        console.log(`Rate limited. Sleeping ${wait}ms.`);
        await new Promise((r) => setTimeout(r, wait));
        continue;
      }
      if (!resp.ok) throw new Error(`API error: ${resp.status}`);
      data = await resp.json();
      break;
    }
    if (!data) break;

    const tweets = data.tweets || [];
    allTweets.push(...tweets);
    console.log(`Page ${page + 1}: ${tweets.length} tweets (total ${allTweets.length})`);

    nextCursor = data.next_cursor;
    if (!nextCursor) break;

    await new Promise((r) => setTimeout(r, 100));
  }

  return allTweets;
}

(async () => {
  const tweets = await searchTweets("bitcoin lang:en min_faves:50", "latest", 5);
  for (const t of tweets) {
    console.log(`@${t.user.username}: ${t.full_text.slice(0, 100)}`);
  }
})();

CSV 导出流程

一个常见的下游模式是搜索到 CSV、然后加载进一个笔记本或 BI 工具:

python
import requests, time, csv

API_KEY = "YOUR_API_KEY"
URL = "https://api.sorsa.io/v3/search-tweets"

def search_to_csv(query, order="popular", max_pages=10, out="tweets.csv"):
    fields = [
        "tweet_id", "created_at", "full_text", "lang",
        "likes", "retweets", "replies", "quotes", "views",
        "username", "display_name", "followers_count", "verified",
    ]
    with open(out, "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=fields)
        w.writeheader()
        cursor, total = None, 0
        for _ in range(max_pages):
            body = {"query": query, "order": order}
            if cursor:
                body["next_cursor"] = cursor
            r = requests.post(
                URL,
                headers={"ApiKey": API_KEY, "Content-Type": "application/json"},
                json=body,
            )
            r.raise_for_status()
            data = r.json()
            for t in data.get("tweets", []):
                u = t.get("user", {})
                w.writerow({
                    "tweet_id": t["id"],
                    "created_at": t["created_at"],
                    "full_text": t["full_text"],
                    "lang": t.get("lang", ""),
                    "likes": t.get("likes_count", 0),
                    "retweets": t.get("retweet_count", 0),
                    "replies": t.get("reply_count", 0),
                    "quotes": t.get("quote_count", 0),
                    "views": t.get("view_count", 0),
                    "username": u.get("username", ""),
                    "display_name": u.get("display_name", ""),
                    "followers_count": u.get("followers_count", 0),
                    "verified": u.get("verified", False),
                })
                total += 1
            cursor = data.get("next_cursor")
            if not cursor:
                break
            time.sleep(0.1)
        print(f"Exported {total} tweets to {out}")

search_to_csv(
    '(bitcoin OR ethereum) lang:en min_faves:10 -filter:retweets',
    order="latest",
    max_pages=20,
    out="crypto_tweets.csv",
)

每页约 20 条推文,max_pages=50 给你约 1,000 条。和按日期范围分块结合,你不用重写循环就能扩展到六七位数,组装一个用于机器学习的 Twitter 数据集正是这么做的。

真实世界查询模板 {#real-world-query-templates}

复制这些、换掉变量、发货。

品牌监控(仅自然提及)

("yourbrand" OR "@yourbrand") -from:yourbrand -filter:retweets lang:en

捕捉人们关于你说什么,排除你自己的帖子和转推,仅英语。在一个 5 分钟的 cron 上跑、导进 Slack。

竞品内容基准

(from:competitor1 OR from:competitor2 OR from:competitor3) min_faves:100 -filter:replies since:2026-01-01

它们在日期窗口里表现最好的原创帖子。放进电子表格、按互动排序、学什么管用。我们的 Twitter 竞品分析指南把这搭成一个可重复的工作流。

情绪追踪

(bitcoin OR $BTC) (bullish OR bearish OR moon OR crash OR pump OR dump) min_faves:20 lang:en

带情绪、且在一个质量下限之上的推文。与上面链接的情感分析管道配合来打分。

产品反馈挖掘

"yourproduct" (bug OR broken OR issue OR love OR amazing OR hate) -filter:retweets

自然反馈、两种口味。对支持和产品路线图输入有用。

线索生成

("looking for" OR "anyone recommend" OR "best tool for") (api OR scraping OR twitter data) -filter:retweets lang:en

正主动索求的人。用 min_faves:1 进一步过滤来丢掉机器人流量。

事件反应窗口

"product launch" OR "announcement" from:yourbrand since:2026-05-01 until:2026-05-08

在一个界定窗口内对你自己发布的反应。把 from:yourbrand(你的帖子)与一个针对周边对话的单独查询配对。

搜索推文 vs 追踪提及:用哪个接口? {#search-tweets-vs-track-mentions-which-endpoint}

两个接口在提及追踪的负载上有重叠。怎么选:

/search-tweets 是通用接口,接受任何操作符组合、任何查询形状。需要灵活性、查询不只是围绕一个用户名、或者想在单个布尔表达式里把提及和互动过滤混在一起时,用它。

/mentions 专为追踪单个用户名的 @ 提及打造,暴露我们 API 里最丰富的过滤集:min_likesmin_repliesmin_retweetssince_dateuntil_date,全都是一等参数,而不是内联操作符。当工作流就是“@brand 出现互动高于 X 的新提及时发告警”时,用它。

快速判断:查询以 @handle 开头、以互动过滤结尾,用 /mentions;涉及多个词、布尔分组或非提及操作符,用 /search-tweets

想更近地看提及接口和品牌监测工作流,见我们的 Twitter 提及 API 指南

Sorsa 搜索如何对比官方 X API {#how-sorsa-search-compares-to-the-official-x-api}

Sorsa 是我们自己的产品,所以下面是诚实的并排对比,两侧都带真实数字,也包括官方 API 仍然胜出的地方。对纯只读搜索,操作符被静默丢弃、以及作者按读取计费,是团队离开 v2 的两个实际原因。

维度Sorsa /search-tweets官方 X API v2 /2/tweets/search/recent
定价模型固定月费套餐;批量时每 1,000 条推文 $0.02 起按量付费,帖子读取每条约 $0.005(约每 1,000 条 $5.00)
起步免费100 次免费请求、无需绑卡、覆盖全部接口无免费额度;第一次调用前先买额度
响应里的作者资料默认内嵌、无额外收费单独用户读取、约每个 $0.010、经 expansions=author_id
认证ApiKey 请求头里的 API 密钥OAuth 2.0 Bearer Token
账号审批即时注册、无审批队列开发者控制台注册
互动操作符(min_faves:min_retweets:min_replies:被静默忽略
网页操作符对等完整集透传仅子集、不同语法(has:is:
历史存档回溯到 2006约 7 天近期;全量存档仅在更高承诺层级
速率限制每秒 20 次请求、所有套餐基于额度、各异
实时监测每秒 20 次请求轮询有过滤流
写动作(发帖、点赞、私信)无(只读)

读取成本上差距很大。官方 X API 上读 1,000 帖约 $5.00,而这还没算每作者 $0.010 的单独收费。在 Sorsa 上,同样的 1,000 条推文,走分页的 /search-tweets 接口(每请求约 20 条)约 $0.10;同样这些 ID 走 /tweet-info-bulk 批量接口拉取则降到约 $0.02,作者资料两种方式都包含。在批量基准上,这是每 1,000 条推文最多便宜 50 倍,而且 X 会当作第二次读取收费的作者数据,这里不额外收费。

官方 API 在这些地方才是对的选择:真正基于推送的实时过滤流,以及我们根本不做的写操作(发帖、点赞、关注)。而只读的一切,按请求计费的路径在任何有意义的量上都更便宜、也更省事。

实战。 我们合作过的一个约 12 人的社交分析团队,正卡在那个尴尬的中间地带:他们每月拉的读取量在 5 万到几百万帖之间,早过了按量付费还便宜的那个点,却远不到能证明企业合同合理的量。在官方 API 上,作者资料让他们每次搜索的成本翻倍,因为每条推文的作者都单独计一次读取。把读取负载迁到一个内嵌作者数据的固定套餐后,他们的月度数据账单降了一个数量级还多,而且这个数字变得可预测,对他们的财务团队来说,这比省下的绝对金额更要紧。驱动这次改变的迁移,正是前面链接的迁移指南里一步步映射的那套。

常见错误与故障排查 {#common-errors-and-troubleshooting}

下面这些问题最常绊住人,大致按我们在支持里见到的频率排序。

一个你知道应该有结果的查询却返回空。 常见嫌疑:操作符里拼错(min_likes 而不是 min_faves)、一个需要双引号的短语("black cat" 而不是 black cat),或一个排除太多的 -filter:。把查询剥到只剩一个关键词、确认有结果,然后一次加一个地把操作符加回去。

429 Too Many Requests 你在这个密钥上超过了 20 次/秒。退避一秒、重试。上面的 Python 和 JavaScript 示例都实现了指数退避。20 次/秒的限制在我们各套餐间通用;如果你需要持续的更高吞吐,联系我们定制限制。

深度分页后游标停止推进或返回重复。 这就是按日期范围分块那一节讲的问题。任何搜索索引在嘈杂查询上翻过某个深度就变得不稳定。切到 since:/until: 的每周块。

操作符被静默忽略。 如果某个过滤好像没起作用,你很可能是在向官方 v2 API 发网页搜索操作符,而 v2 会丢弃互动过滤。在断定查询写错之前,先确认你的服务商接受哪一套操作符。

操作符数量上限。 X 的搜索索引似乎会静默地让带 22 到 23 个以上操作符的查询失败,而官方 API 上的自助近期搜索把查询字符串封顶在 512 个字符。如果你的查询分组比这更多还返回空,就简化查询,或者拆成多次请求。

user 对象缺失或不完整。 作者在推文创建和你请求之间被封、被删或把账号设为受保护。推文还在索引里,但作者不再能被公开枚举。代码里用 tweet.get("user", {}) 默认值处理这种情况。

加了 lang:en 却出现意外语言的结果。 X 的语言检测器不完美,尤其在带话题标签或混合文字的短推文上。对分析负载,用语言检测库(比如 langdetectfasttext-langdetect)对 full_text 字段做后过滤。

私密和被限流的账号不出现。 受保护账号不在搜索索引里。被封和被锁的账号也被隐藏,没有操作符能把它们浮现出来。限流是和搜索可见性无关的另一个诊断问题,也不是搜索索引能回答的。

常见问题 {#frequently-asked-questions}

如何不用官方 Twitter API 搜索推文?

用第三方搜索 API 或开源爬虫。第三方 REST API 把自己的爬取基础设施封装在一个 API 密钥后面,没有 OAuth,且支持完整网页操作符;Sorsa 就是这样一个 Twitter/X API 替代方案,带一个透传每个操作符、按请求计费的 /search-tweets 接口。Twikit 这类开源库对小的一次性任务管用,但对生产环境不够稳定。

如何用 API 按话题标签搜索推文?

把话题标签作为一个查询词传入,比如 #worldcup,再和过滤结合来控制量:#worldcup min_faves:50 lang:en -filter:retweets 返回携带那个话题标签的热门英语原创推文。官方 X API v2 也匹配 #hashtag,但施加不了互动下限,还把近期搜索限在约七天,所以要拿更深的话题标签历史,按请求计费的网页操作符 API 是更强的路线。

能搜索超过 7 天的推文吗?

能,在第三方 API 上。Sorsa /search-tweets 接口用 since:until: 操作符支持可回溯到 2006 年的完整历史存档。官方 X API v2 近期搜索限在约 7 天;官方 API 上的全量存档搜索限在更高承诺的档位。

每次搜索请求能拿到多少条推文?

单次 /search-tweets 请求返回一页约 20 条推文。要拉更多,用带 next_cursor 的游标分页。几千条推文以上的数据集,把游标分页和按日期范围分块结合,避免长行走上的游标漂移。

对开发者最有用的 Twitter 搜索操作符是什么?

生产代码里高价值的操作符是 from:to:min_faves:min_retweets:since:until:lang:,以及 -filter:retweets-filter:replies 排除。互动过滤尤其有价值,因为它们在不丢相关内容的前提下去掉低质量噪音,也因为在官方 X API v2 上不生效。

Twitter 搜索 API 支持布尔逻辑(AND、OR、NOT)吗?

支持。空格分隔的词隐含 AND。大写的 OR 是显式 OR。括号给表达式分组。减号前缀排除词:crypto -scam。一个完整示例是 (bitcoin OR ethereum) min_faves:100 -filter:retweets lang:en

2026 年通过 API 搜索推文要花多少钱?

官方 X API v2 按量付费下,帖子读取每条约 $0.005,外加作者资料读取每份约 $0.010,这在作者数据之前就合到约每 1,000 条推文 $5.00。Sorsa 是固定月费,注册即送 100 次免费请求,无需绑卡。走 /tweet-info-bulk 批量,推文数据在 Pro 套餐上折合约每 1,000 条推文 $0.02(Starter 的 $0.049 降到 Enterprise 的 $0.018),含作者资料;走分页的 /search-tweets 接口、每请求约 20 条,则更接近每 1,000 条 $0.10。

能实时搜索推文吗?

实际上能,靠轮询。用 20 次/秒的速率限制加 order: "latest",你能每几秒轮询一个查询,在发布后几秒内拿到新推文。要真正基于推送的流式,官方 X API 过滤流是唯一不用轮询就能交付的选项。对多数监测负载,每 30 到 60 秒轮询一次就够,运营起来也更便宜。

开始上手 {#getting-started}

你可以在写一行客户端代码之前先测这个接口。在线试用工具 Playground 从浏览器对着你的密钥跑真实请求,可视化搜索生成器给你一个操作符表单,并输出要发送的精确 JSON 请求体。

准备好拿密钥时:注册即送 100 次免费请求,无需绑卡,覆盖全部 40 个接口,注册只需几分钟、无开发者账号审批,每个套餐都跑同样的统一 20 次/秒。走批量时,固定套餐折合低至每 1,000 条推文 $0.02。在 Sorsa 控制台注册,跟着快速上手指南做五分钟配置。如果你在离开官方 API,前面链接的迁移指南把每个 v2 接口映射到 Sorsa 的对应项,两边都有代码。


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

本指南是怎么写成的:内容取材于我们构建并运营 Sorsa 搜索基础设施的一线工作、对着 /search-tweets 接口的线上测试,以及和官方 X API v2 近期搜索接口的直接对比。操作符行为对照社区维护的 igorbrigadir/twitter-advanced-search 参考和官方 X API 文档核查;定价反映截至 2026 年 7 月 8 日的官方 X API 按读取费率。接口细节来自 Sorsa API 文档。谁在发布这个博客,更多信息见我们的关于页面