> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sorsa.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 从官方 X API 迁移

# 从官方 X API v2 迁移到 Sorsa API v3

本页介绍如何将现有集成从官方 Twitter/X API v2 迁移至 Sorsa API v3，涵盖身份验证、端点映射、响应格式变化、分页、HTTP 方法差异、搜索查询语法、错误处理，以及 curl、Python 和 JavaScript 代码示例。

Sorsa API 为只读 API。如果您的集成也会向 X 写入数据（发布推文、发送私信、点赞、关注），请保留官方 API 密钥用于写入，只迁移读取部分。每个新账号均包含 100 次免费请求，无需银行卡，因此可以先验证端点映射，再切换生产流量。

> **说明：** 如需包含成本比较和逐步示例的迁移教程，请阅读博客中的[从 Twitter/X API 迁移：开发者完整指南](https://api.sorsa.io/blog/migrate-from-twitter-api)。

## 变化概览

| 项目     | 官方 X API v2                                 | Sorsa API v3              |
| ------ | ------------------------------------------- | ------------------------- |
| 基础 URL | `https://api.x.com/2`                       | `https://api.sorsa.io/v3` |
| 身份验证   | OAuth 2.0 Bearer / OAuth 1.0a               | 通过 `ApiKey` 请求头传递 API 密钥  |
| 字段选择   | `tweet.fields`, `user.fields`, `expansions` | 默认返回所有字段                  |
| 响应封装   | `data` + `includes` + `meta`                | 扁平对象，内嵌关联数据               |
| 分页     | `pagination_token` / `meta.next_token`      | `next_cursor`（顶层）         |
| 速率限制   | 按端点、每 15 分钟窗口计算                             | 所有端点统一每秒 20 次请求           |
| 错误结构   | 包含 `type`、`title`、`detail` 的 `errors[]`     | `{ "message": "..." }`    |

## 身份验证

官方 API 对仅限应用的请求使用 OAuth 2.0 Bearer 令牌，对用户范围的请求使用 OAuth 1.0a User Context。

```bash theme={null}
# Official API (OAuth 2.0 App-Only)
curl "https://api.x.com/2/users/by/username/elonmusk" \
  -H "Authorization: Bearer $BEARER_TOKEN"
```

Sorsa API 通过 `ApiKey` 请求头传递单个 API 密钥。在[控制台](https://api.sorsa.io/overview/keys)生成密钥。

```bash theme={null}
curl "https://api.sorsa.io/v3/info?username=elonmusk" \
  -H "ApiKey: $API_KEY"
```

```python theme={null}
import requests

response = requests.get(
    "https://api.sorsa.io/v3/info",
    params={"username": "elonmusk"},
    headers={"ApiKey": API_KEY},
)
user = response.json()
```

```javascript theme={null}
const response = await fetch("https://api.sorsa.io/v3/info?username=elonmusk", {
  headers: { ApiKey: API_KEY },
});
const user = await response.json();
```

完整说明见[身份验证](https://docs.sorsa.io/zh-Hans/authentication)。

## 端点映射

### 用户

| 操作        | 官方 X API v2                          | Sorsa API v3                                |
| --------- | ------------------------------------ | ------------------------------------------- |
| 按用户名获取用户  | `GET /2/users/by/username/:username` | `GET /info?username=:username`              |
| 按 ID 获取用户 | `GET /2/users/:id`                   | `GET /info?user_id=:id`                     |
| 批量获取用户    | `GET /2/users?ids=...`               | `GET /info-batch?user_ids=...&user_ids=...` |
| 获取粉丝      | `GET /2/users/:id/followers`         | `GET /followers?user_id=:id`                |
| 获取关注      | `GET /2/users/:id/following`         | `GET /follows?user_id=:id`                  |
| 已认证粉丝     | 不提供                                  | `GET /verified-followers?user_id=:id`       |
| 账号“关于”元数据 | 不提供                                  | `GET /about?username=:username`             |

* `GET /info-batch` 每次最多接受 100 个用户名或 ID。重复查询参数，例如 `?usernames=a&usernames=b`。
* `GET /followers` 和 `GET /follows` 每页最多返回 **200 份完整个人资料**，包括简介、粉丝数量和认证状态。

### 推文

| 操作     | 官方 X API v2                      | Sorsa API v3                                            |
| ------ | -------------------------------- | ------------------------------------------------------- |
| 获取单条推文 | `GET /2/tweets/:id`              | `POST /tweet-info` 请求体： `{ "tweet_link": ":id" }`       |
| 批量获取推文 | `GET /2/tweets?ids=...`          | `POST /tweet-info-bulk` 请求体： `{ "tweet_links": [...] }` |
| 用户时间线  | `GET /2/users/:id/tweets`        | `POST /user-tweets` 请求体： `{ "user_id": ":id" }`         |
| 引用推文   | `GET /2/tweets/:id/quote_tweets` | `POST /quotes` 请求体： `{ "tweet_link": ":id" }`           |
| 转推用户   | `GET /2/tweets/:id/retweeted_by` | `POST /retweeters` 请求体： `{ "tweet_link": ":id" }`       |
| 回复（评论） | 没有专用端点                           | `POST /comments` 请求体： `{ "tweet_link": ":id" }`         |
| X 长文章  | 不提供                              | `POST /article` 请求体： `{ "tweet_link": ":id" }`          |

* `tweet_link` 接受完整推文 URL（`https://x.com/user/status/123`）或数字 ID（`"123"`）。
* `POST /tweet-info-bulk` 每次最多返回 100 条推文。使用它代替循环调用 `POST /tweet-info`，最多可将请求次数减少至原来的 1/100。
* `POST /user-tweets` 没有 3,200 条推文的上限。持续分页，直到响应中不再出现 `next_cursor`，即可获取回溯至账号首条推文的完整时间线。见[历史数据](https://docs.sorsa.io/zh-Hans/historical-data)。

### 搜索

| 操作       | 官方 X API v2                             | Sorsa API v3                                    |
| -------- | --------------------------------------- | ----------------------------------------------- |
| 搜索近期推文   | `GET /2/tweets/search/recent?query=...` | `POST /search-tweets` 请求体： `{ "query": "..." }` |
| 搜索完整历史档案 | `GET /2/tweets/search/all?query=...`    | `POST /search-tweets` （包含历史数据）                  |
| 搜索提及     | `GET .../search/recent?query=@user`     | `POST /mentions` 请求体： `{ "query": "user" }`     |
| 搜索用户     | v2 不提供                                  | `POST /search-users` 请求体： `{ "query": "..." }`  |

* Sorsa `/search-tweets` 使用 X 网页高级搜索语法。许多基本查询可以直接沿用，但复用查询前应检查 API v2 专用运算符。见[搜索运算符](https://docs.sorsa.io/zh-Hans/search-operators)。
* `POST /mentions` 还支持官方 API 未提供的筛选：`min_likes`、`min_replies`、`min_retweets`、`since_date`、`until_date`。

### 列表

| 操作    | 官方 X API v2                  | Sorsa API v3                        |
| ----- | ---------------------------- | ----------------------------------- |
| 列表成员  | `GET /2/lists/:id/members`   | `GET /list-members?list_id=:id`     |
| 列表关注者 | `GET /2/lists/:id/followers` | `GET /list-followers?list_link=:id` |
| 列表推文  | `GET /2/lists/:id/tweets`    | `GET /list-tweets?list_id=:id`      |

### 社群

官方 X API 没有开放社群端点。以下功能仅由 Sorsa 提供。

| 操作     | Sorsa API v3                                                                       |
| ------ | ---------------------------------------------------------------------------------- |
| 社群成员   | `POST /community-members` 请求体： `{ "community_link": ":id" }`                       |
| 社群动态   | `POST /community-tweets` 请求体： `{ "community_id": ":id", "order": "popular" }`      |
| 在社群内搜索 | `POST /community-search-tweets` 请求体： `{ "community_link": ":id", "query": "..." }` |

请求详情及当前可用性说明见[列表与社群](https://docs.sorsa.io/zh-Hans/lists-and-communities)；迁移社群工作流前，请确认当前是否支持。

### 验证

这些端点通过一次调用回答关注、互动或成员资格问题。官方 API 没有对应功能；通常需要获取完整列表，再在客户端查找。

| 问题                 | Sorsa API v3                                     |
| ------------------ | ------------------------------------------------ |
| 用户 A 是否关注用户 B？     | `POST /check-follow`                             |
| 用户 X 是否评论了推文 Y？    | `GET /check-comment?tweet_link=...&username=...` |
| 用户 X 是否引用或转推了推文 Y？ | `POST /check-quoted`                             |
| 用户 X 是否转推了推文 Y？    | `POST /check-retweet`                            |
| 用户 X 是否属于社群 Y？     | `POST /check-community-member`                   |

见[营销活动验证](https://docs.sorsa.io/zh-Hans/Marketing-Campaign-Verification)。

### 分析（仅 Sorsa 提供）

| 操作              | Sorsa API v3                         |
| --------------- | ------------------------------------ |
| 影响力评分           | `GET /score?username=...`            |
| 评分变化（7 天、30 天）  | `GET /score-changes?username=...`    |
| 按类别划分的粉丝分布      | `GET /followers-stats?username=...`  |
| 按评分排名前 20 的粉丝   | `GET /top-followers?username=...`    |
| 按评分排名前 20 的关注账号 | `GET /top-following?username=...`    |
| 新增粉丝（7 天）       | `GET /new-followers-7d?username=...` |
| 新增关注（7 天）       | `GET /new-following-7d?username=...` |

这些端点索引以加密货币为主的部分账号，包括意见领袖、项目和风投机构。见[Sorsa Score 与加密货币分析](https://docs.sorsa.io/zh-Hans/sorsa-score-and-crypto-analytics)。

### 实用工具

| 操作              | Sorsa API v3                  |
| --------------- | ----------------------------- |
| 用户名转数字 ID       | `GET /username-to-id/:handle` |
| 数字 ID 转用户名      | `GET /id-to-username/:id`     |
| 个人资料 URL 转数字 ID | `GET /link-to-id?link=...`    |
| API 密钥用量统计      | `GET /key-usage-info`         |

见[ID 转换](https://docs.sorsa.io/zh-Hans/ID-Conversion)。

## 响应格式变化

这是迁移中最显著的单项变化。官方 v2 使用 `data`、`includes` 和 `meta` 封装响应。Sorsa 返回扁平对象，作者资料直接内嵌在每条推文中。

### 用户资料

**官方 X API v2**（指定返回字段）：

```json theme={null}
{
  "data": {
    "id": "44196397",
    "name": "Elon Musk",
    "username": "elonmusk",
    "verified": false,
    "profile_image_url": "https://pbs.twimg.com/...",
    "public_metrics": {
      "followers_count": 100000000,
      "following_count": 500,
      "tweet_count": 30000,
      "listed_count": 12000
    }
  }
}
```

**Sorsa API v3：**

```json theme={null}
{
  "id": "44196397",
  "username": "elonmusk",
  "display_name": "Elon Musk",
  "description": "...",
  "location": "Austin, TX",
  "profile_image_url": "https://pbs.twimg.com/...",
  "profile_background_image_url": "...",
  "followers_count": 100000000,
  "followings_count": 500,
  "tweets_count": 30000,
  "favourites_count": 50000,
  "media_count": 1200,
  "verified": false,
  "protected": false,
  "can_dm": true,
  "possibly_sensitive": false,
  "created_at": "2009-06-02T20:12:29Z",
  "bio_urls": ["https://example.com"],
  "pinned_tweet_ids": ["17823..."]
}
```

### 推文

**官方 X API v2**（使用 `expansions=author_id`）：

```json theme={null}
{
  "data": {
    "id": "1234567890",
    "text": "Hello world",
    "created_at": "2024-01-15T12:00:00.000Z",
    "author_id": "44196397",
    "conversation_id": "1234567890",
    "lang": "en",
    "public_metrics": {
      "retweet_count": 100,
      "reply_count": 50,
      "like_count": 500,
      "quote_count": 25,
      "bookmark_count": 10,
      "impression_count": 50000
    }
  },
  "includes": {
    "users": [
      { "id": "44196397", "name": "Elon Musk", "username": "elonmusk" }
    ]
  }
}
```

**Sorsa API v3：**

```json theme={null}
{
  "id": "1234567890",
  "full_text": "Hello world",
  "created_at": "2024-01-15T12:00:00Z",
  "lang": "en",
  "conversation_id_str": "1234567890",
  "likes_count": 500,
  "retweet_count": 100,
  "reply_count": 50,
  "quote_count": 25,
  "view_count": 50000,
  "bookmark_count": 10,
  "is_reply": false,
  "is_quote_status": false,
  "is_replies_limited": false,
  "in_reply_to_tweet_id": null,
  "in_reply_to_username": null,
  "user": {
    "id": "44196397",
    "username": "elonmusk",
    "display_name": "Elon Musk",
    "followers_count": 100000000
  },
  "entities": [],
  "quoted_status": null,
  "retweeted_status": null
}
```

### 字段映射

#### 用户字段

| 官方 X API v2                      | Sorsa API v3                   | 说明         |
| -------------------------------- | ------------------------------ | ---------- |
| `id`                             | `id`                           | 相同         |
| `username`                       | `username`                     | 相同         |
| `name`                           | `display_name`                 | 已重命名       |
| `description`                    | `description`                  | 相同         |
| `location`                       | `location`                     | 相同         |
| `verified`                       | `verified`                     | 相同         |
| `protected`                      | `protected`                    | 相同         |
| `profile_image_url`              | `profile_image_url`            | 相同         |
| `created_at`                     | `created_at`                   | 相同         |
| `public_metrics.followers_count` | `followers_count`              | 已扁平化       |
| `public_metrics.following_count` | `followings_count`             | 已扁平化并重命名   |
| `public_metrics.tweet_count`     | `tweets_count`                 | 已扁平化并重命名   |
| `public_metrics.listed_count`    | 不提供                            |            |
| 不提供                              | `favourites_count`             | 仅 Sorsa 提供 |
| 不提供                              | `media_count`                  | 仅 Sorsa 提供 |
| 不提供                              | `can_dm`                       | 仅 Sorsa 提供 |
| 不提供                              | `bio_urls`                     | 仅 Sorsa 提供 |
| 不提供                              | `pinned_tweet_ids`             | 仅 Sorsa 提供 |
| 不提供                              | `profile_background_image_url` | 仅 Sorsa 提供 |
| 不提供                              | `possibly_sensitive`           | 仅 Sorsa 提供 |

#### 推文字段

| 官方 X API v2                       | Sorsa API v3                                        | 说明               |
| --------------------------------- | --------------------------------------------------- | ---------------- |
| `id`                              | `id`                                                | 相同               |
| `text`                            | `full_text`                                         | 已重命名             |
| `created_at`                      | `created_at`                                        | 相同               |
| `lang`                            | `lang`                                              | 相同               |
| `conversation_id`                 | `conversation_id_str`                               | 已重命名             |
| `in_reply_to_user_id`             | `in_reply_to_username`                              | 返回用户名，而非数字 ID    |
| `public_metrics.like_count`       | `likes_count`                                       | 已扁平化并重命名（注意复数形式） |
| `public_metrics.retweet_count`    | `retweet_count`                                     | 已扁平化             |
| `public_metrics.reply_count`      | `reply_count`                                       | 已扁平化             |
| `public_metrics.quote_count`      | `quote_count`                                       | 已扁平化             |
| `public_metrics.bookmark_count`   | `bookmark_count`                                    | 已扁平化             |
| `public_metrics.impression_count` | `view_count`                                        | 已扁平化并重命名         |
| `author_id` + `includes.users[]`  | `user`（内嵌完整对象）                                      | 内嵌               |
| 通过 `includes` 引用的推文               | `quoted_status`, `retweeted_status`                 | 内嵌对象             |
| 不提供                               | `is_reply`, `is_quote_status`, `is_replies_limited` | 仅 Sorsa 提供的布尔值   |
| 不提供                               | `in_reply_to_tweet_id`                              | 仅 Sorsa 提供       |
| `entities`（URL、提及、话题标签、媒体）        | 由 `{ type, link, preview }` 组成的 `entities` 数组       | 结构不同             |

## 分页

官方 API 使用查询参数 `pagination_token`，并返回 `meta.next_token`。Sorsa 在请求和响应中均使用同一个字段 `next_cursor`。

**对于 GET 端点**，将 `next_cursor` 作为查询参数传递：

```bash theme={null}
curl "https://api.sorsa.io/v3/followers?username=elonmusk&next_cursor=ABC123" \
  -H "ApiKey: $API_KEY"
```

**对于 POST 端点**，将 `next_cursor` 放入 JSON 请求体：

```bash theme={null}
curl -X POST "https://api.sorsa.io/v3/search-tweets" \
  -H "ApiKey: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "from:elonmusk", "next_cursor": "ABC123" }'
```

响应始终在顶层返回 `next_cursor`：

```json theme={null}
{
  "tweets": [...],
  "next_cursor": "XYZ789"
}
```

当 `next_cursor` 缺失或为 `null` 时，表示没有更多页面。详情见[分页](https://docs.sorsa.io/zh-Hans/pagination)。

## HTTP 方法差异

某些在官方 API 中使用 `GET` 的端点，在 Sorsa 中使用 `POST`。

| 操作      | 官方 API    | Sorsa API |
| ------- | --------- | --------- |
| 获取推文    | GET       | **POST**  |
| 搜索推文    | GET       | **POST**  |
| 用户时间线   | GET       | **POST**  |
| 引用推文    | GET       | **POST**  |
| 转推用户    | GET       | **POST**  |
| 回复（评论）  | （无直接对应端点） | **POST**  |
| 用户资料    | GET       | GET       |
| 粉丝 / 关注 | GET       | GET       |
| 列表      | GET       | GET       |

一般而言，推文内容、搜索和社群端点使用带 JSON 请求体的 `POST`；这也包括 `/user-tweets`，尽管它接收用户标识符。用户、列表和实用工具查询使用 `GET`，通过查询或路径参数传参。一个例外是 `/check-comment`：虽然接收推文链接，但使用 `GET`。如有疑问，请查阅具体端点参考。

## 代码迁移示例

### 获取用户资料

**迁移前（官方 API）：**

```bash theme={null}
curl "https://api.x.com/2/users/by/username/elonmusk?user.fields=description,public_metrics,profile_image_url,verified,created_at" \
  -H "Authorization: Bearer $BEARER_TOKEN"
```

```python theme={null}
import requests

response = requests.get(
    "https://api.x.com/2/users/by/username/elonmusk",
    params={"user.fields": "description,public_metrics,profile_image_url,verified,created_at"},
    headers={"Authorization": f"Bearer {BEARER_TOKEN}"},
)
user = response.json()["data"]
followers = user["public_metrics"]["followers_count"]
name = user["name"]
```

```javascript theme={null}
const url = "https://api.x.com/2/users/by/username/elonmusk" +
  "?user.fields=description,public_metrics,profile_image_url,verified,created_at";
const res = await fetch(url, {
  headers: { Authorization: `Bearer ${BEARER_TOKEN}` },
});
const { data: user } = await res.json();
const followers = user.public_metrics.followers_count;
const name = user.name;
```

**迁移后（Sorsa API）：**

```bash theme={null}
curl "https://api.sorsa.io/v3/info?username=elonmusk" \
  -H "ApiKey: $API_KEY"
```

```python theme={null}
import requests

response = requests.get(
    "https://api.sorsa.io/v3/info",
    params={"username": "elonmusk"},
    headers={"ApiKey": API_KEY},
)
user = response.json()
followers = user["followers_count"]
name = user["display_name"]
```

```javascript theme={null}
const res = await fetch("https://api.sorsa.io/v3/info?username=elonmusk", {
  headers: { ApiKey: API_KEY },
});
const user = await res.json();
const followers = user.followers_count;
const name = user.display_name;
```

### 搜索推文

**迁移前：**

```bash theme={null}
curl "https://api.x.com/2/tweets/search/recent?query=from%3Aelonmusk%20since%3A2024-01-01&tweet.fields=created_at,public_metrics,lang&expansions=author_id&user.fields=username,name&max_results=10" \
  -H "Authorization: Bearer $BEARER_TOKEN"
```

```python theme={null}
params = {
    "query": "from:elonmusk since:2024-01-01",
    "tweet.fields": "created_at,public_metrics,lang",
    "expansions": "author_id",
    "user.fields": "username,name",
    "max_results": 10,
}
response = requests.get(
    "https://api.x.com/2/tweets/search/recent",
    headers={"Authorization": f"Bearer {BEARER_TOKEN}"},
    params=params,
)
data = response.json()

tweets = data["data"]
users = {u["id"]: u for u in data.get("includes", {}).get("users", [])}
next_token = data.get("meta", {}).get("next_token")

for tweet in tweets:
    author = users.get(tweet["author_id"])
    print(tweet["text"], "by", author["username"])
```

```javascript theme={null}
const params = new URLSearchParams({
  query: "from:elonmusk since:2024-01-01",
  "tweet.fields": "created_at,public_metrics,lang",
  expansions: "author_id",
  "user.fields": "username,name",
  max_results: "10",
});
const res = await fetch(`https://api.x.com/2/tweets/search/recent?${params}`, {
  headers: { Authorization: `Bearer ${BEARER_TOKEN}` },
});
const data = await res.json();

const tweets = data.data || [];
const users = Object.fromEntries((data.includes?.users || []).map(u => [u.id, u]));

for (const t of tweets) {
  const author = users[t.author_id];
  console.log(t.text, "by", author.username);
}
```

**迁移后：**

```bash theme={null}
curl -X POST "https://api.sorsa.io/v3/search-tweets" \
  -H "ApiKey: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "from:elonmusk since:2024-01-01"}'
```

```python theme={null}
response = requests.post(
    "https://api.sorsa.io/v3/search-tweets",
    headers={"ApiKey": API_KEY},
    json={"query": "from:elonmusk since:2024-01-01"},
)
data = response.json()

for tweet in data["tweets"]:
    print(tweet["full_text"], "by", tweet["user"]["username"])
```

```javascript theme={null}
const res = await fetch("https://api.sorsa.io/v3/search-tweets", {
  method: "POST",
  headers: { ApiKey: API_KEY, "Content-Type": "application/json" },
  body: JSON.stringify({ query: "from:elonmusk since:2024-01-01" }),
});
const data = await res.json();

for (const t of data.tweets) {
  console.log(t.full_text, "by", t.user.username);
}
```

### 分页获取全部粉丝

**迁移前：**

```python theme={null}
def fetch_all_followers_official(user_id, token):
    url = f"https://api.x.com/2/users/{user_id}/followers"
    headers = {"Authorization": f"Bearer {token}"}
    followers = []
    pagination_token = None

    while True:
        params = {"max_results": 1000}
        if pagination_token:
            params["pagination_token"] = pagination_token

        r = requests.get(url, headers=headers, params=params)
        r.raise_for_status()
        data = r.json()

        followers.extend(data.get("data", []))
        pagination_token = data.get("meta", {}).get("next_token")
        if not pagination_token:
            break

    return followers
```

**迁移后：**

```bash theme={null}
CURSOR=""
while :; do
  RES=$(curl -s "https://api.sorsa.io/v3/followers?username=elonmusk${CURSOR:+&next_cursor=$CURSOR}" \
    -H "ApiKey: $API_KEY")
  echo "$RES" | jq '.users'
  CURSOR=$(echo "$RES" | jq -r '.next_cursor // empty')
  [ -z "$CURSOR" ] && break
done
```

```python theme={null}
def fetch_all_followers(user_id, api_key):
    url = "https://api.sorsa.io/v3/followers"
    headers = {"ApiKey": api_key}
    followers = []
    next_cursor = None

    while True:
        params = {"user_id": user_id}
        if next_cursor:
            params["next_cursor"] = next_cursor

        r = requests.get(url, headers=headers, params=params)
        r.raise_for_status()
        data = r.json()

        followers.extend(data.get("users", []))
        next_cursor = data.get("next_cursor")
        if not next_cursor:
            break

    return followers
```

```javascript theme={null}
async function fetchAllFollowers(userId, apiKey) {
  const followers = [];
  let nextCursor = null;

  do {
    const params = new URLSearchParams({ user_id: userId });
    if (nextCursor) params.set("next_cursor", nextCursor);

    const res = await fetch(
      `https://api.sorsa.io/v3/followers?${params}`,
      { headers: { ApiKey: apiKey } }
    );
    const json = await res.json();

    followers.push(...(json.users || []));
    nextCursor = json.next_cursor || null;
  } while (nextCursor);

  return followers;
}
```

Sorsa 每页最多返回 200 份完整个人资料。官方 API 通常只返回 ID 和精简用户数据，需要额外查询才能补全资料。

## 搜索查询语法

Sorsa 使用 X 网页高级搜索语法，与官方 API v2 的运算符集合有所不同。可以按需保留基本关键词、短语、`from:` 和 `to:`，再转换 API 专用筛选条件并测试结果。例如，使用 `-filter:nativeretweets` 排除原生转推。

| 运算符                 | 示例                                              |
| ------------------- | ----------------------------------------------- |
| `from:`             | `from:elonmusk`                                 |
| `to:`               | `to:elonmusk`                                   |
| `since:` / `until:` | `since:2024-01-01 until:2024-02-01`             |
| 话题标签                | `#bitcoin`                                      |
| 精确短语                | `"hello world"`                                 |
| `OR`                | `bitcoin OR ethereum`                           |
| 排除                  | `-filter:nativeretweets`                        |
| 组合                  | `from:elonmusk #bitcoin -filter:nativeretweets` |

完整参考：[搜索运算符](https://docs.sorsa.io/zh-Hans/search-operators)。

`/mentions` 还支持服务端筛选：`min_likes`、`min_replies`、`min_retweets`、`since_date`、`until_date`。见[追踪提及](https://docs.sorsa.io/zh-Hans/search-mentions)。

## 错误处理

官方 API 通过结构化 `errors` 数组返回错误：

```json theme={null}
{
  "errors": [
    {
      "message": "Not Found",
      "type": "https://api.x.com/2/problems/resource-not-found",
      "title": "Not Found Error",
      "detail": "Could not find tweet with id: [123].",
      "status": 404
    }
  ]
}
```

Sorsa 返回简化结构：

```json theme={null}
{ "message": "Tweet not found" }
```

所有端点的状态码一致：`400`、`401`、`403`、`404`、`429`、`500`。见[错误码](https://docs.sorsa.io/zh-Hans/error-codes)。

速率限制处理：收到 `429` 后，应退避并重试。所有端点统一限制为**每秒 20 次请求**，无需分别跟踪各端点的时间窗口。见[速率限制](https://docs.sorsa.io/zh-Hans/rate-limits)。

下面的重试封装可处理这两种 API：

```python theme={null}
import time
import requests

def call_with_retry(method, url, max_retries=3, **kwargs):
    for attempt in range(max_retries):
        r = requests.request(method, url, **kwargs)
        if r.status_code == 429:
            time.sleep(2 ** attempt)
            continue
        r.raise_for_status()
        return r.json()
    raise RuntimeError(f"Failed after {max_retries} retries")
```

## 迁移检查清单

* 将 `Authorization: Bearer ...` 替换为 `ApiKey: ...`。
* 移除 OAuth 1.0a 签名逻辑，包括 consumer key、访问令牌和签名生成。
* 将基础 URL 从 `https://api.x.com/2` 改为 `https://api.sorsa.io/v3`。
* 使用上面的表格映射每个端点路径。
* 将推文、搜索、评论、引用和转推用户端点从 GET 改为 POST。
* 移除 `tweet.fields`、`user.fields`、`media.fields` 和 `expansions` 参数。
* 更新响应解析器，移除 `data` / `includes` / `meta` 解包逻辑。
* 重命名字段，例如 `name` 改为 `display_name`，`text` 改为 `full_text`。
* 改为直接访问扁平化指标，移除 `public_metrics` 层。
* 将 `pagination_token` / `next_token` 替换为 `next_cursor`。
* 更新错误处理以适配简化的 `{ "message": "..." }` 格式。
* 调整限流逻辑：统一每秒 20 次请求，不再使用各端点独立窗口。
* 在 [API Playground](https://api.sorsa.io/playground) 测试关键端点。
* 通过 `GET /key-usage-info` 监测配额。
* 如仍需写操作（发帖、私信），保留官方 API 密钥。

## 官方 API 没有对应项的功能

| 功能                | 端点                                                  |
| ----------------- | --------------------------------------------------- |
| 无 3,200 条上限的推文时间线 | `POST /user-tweets`                                 |
| 单次调用检查关注关系        | `POST /check-follow`                                |
| 单次调用检查转推          | `POST /check-retweet`                               |
| 单次调用检查评论          | `GET /check-comment`                                |
| 单次调用检查引用 / 转推     | `POST /check-quoted`                                |
| 检查社群成员资格          | `POST /check-community-member`                      |
| 社群成员和动态           | `POST /community-members`, `POST /community-tweets` |
| 社群内搜索             | `POST /community-search-tweets`                     |
| X 长文章内容           | `POST /article`                                     |
| 仅保留已认证粉丝的筛选       | `GET /verified-followers`                           |
| 账号所在国家和用户名变更历史    | `GET /about`                                        |
| 影响力评分             | `GET /score`, `GET /score-changes`                  |
| 按影响力排名的主要粉丝和关注账号  | `GET /top-followers`, `GET /top-following`          |
| 按类别划分的粉丝明细        | `GET /followers-stats`                              |

## 相关参考

* [身份验证](https://docs.sorsa.io/zh-Hans/authentication)
* [基础 URL 与版本管理](https://docs.sorsa.io/zh-Hans/base-url-and-versioning)
* [速率限制](https://docs.sorsa.io/zh-Hans/rate-limits)
* [分页](https://docs.sorsa.io/zh-Hans/pagination)
* [错误码](https://docs.sorsa.io/zh-Hans/error-codes)
* [响应格式](https://docs.sorsa.io/zh-Hans/response-format)
* [搜索运算符](https://docs.sorsa.io/zh-Hans/search-operators)
* [优化 API 使用](https://docs.sorsa.io/zh-Hans/optimizing-api-usage)
* [API 参考](https://docs.sorsa.io/zh-Hans/api-reference-guide)
