> ## 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.

# 优化 API 使用

通过复用嵌入资料、批量查询和按稳定 ID 存储结果，减少不必要请求。以下示例介绍各方式的适用情况。

> **提示：** 每个账号均有 100 次免费请求，无需信用卡、永不过期。可用它们验证下方模式并测量真实请求量，再选择套餐。

***

## 示例环境设置

Python 示例在后端运行，使用 `requests`（通过 `python -m pip install requests` 安装）。运行前替换 API 密钥和示例 ID。`search_results` 指前一次请求返回的推文对象列表。

```python theme={null}
import requests

API_KEY = "YOUR_API_KEY"
```

## 原则 1：每个推文响应已包含用户数据

这是提高 Sorsa API 效率最重要的一点。所有返回推文的端点（`/search-tweets`、`/user-tweets`、`/list-tweets`、`/comments`、`/quotes`、`/mentions`）都会在每个推文对象内嵌入**完整作者资料**。

```json theme={null}
{
  "tweets": [
    {
      "id": "2029914600217473314",
      "full_text": "Great thread on API design patterns...",
      "likes_count": 142,
      "user": {
        "id": "1422280682240450563",
        "username": "dev_sarah",
        "display_name": "Sarah Chen",
        "description": "Staff engineer @stripe. APIs, distributed systems.",
        "followers_count": 12400,
        "followings_count": 890,
        "tweets_count": 4521,
        "verified": true,
        "location": "San Francisco",
        "created_at": "2021-02-01T09:15:22Z"
      }
    }
  ]
}
```

推文内的 `user` 对象包含与单独调用 `/info` 相同的数据：ID、用户名、显示名称、简介、粉丝数、关注数、推文数、认证状态、所在地、创建日期、头像等。

**实际意义：** 搜索某话题推文并构建讨论者列表时，无需再为每位作者单独调用 `/info`，直接提取响应中的用户数据即可：

```python theme={null}
# Collect unique users from a tweet search - zero extra API calls
seen_ids = set()
unique_users = []

for tweet in search_results:
    user = tweet["user"]
    if user["id"] not in seen_ids:
        seen_ids.add(user["id"])
        unique_users.append(user)

print(f"Found {len(unique_users)} unique users from {len(search_results)} tweets")
```

这一方式可为典型工作流省去数百或数千次多余的 `/info` 请求。

***

## 原则 2：优先使用现有批量端点

Sorsa 为常用查询提供批量版本。使用它们替代循环调用单条端点，可显著减少请求。

### 用 `/info-batch` 替代循环 `/info`

需要多个账号资料时，使用 `/info-batch` 一次获取最多 100 份：

```python theme={null}
# A separate /info call for each account would use 10 requests.

# Efficient: 10 accounts = 1 request
resp = requests.get(
    "https://api.sorsa.io/v3/info-batch",
    headers={"ApiKey": API_KEY},
    params={"usernames": ["NASA", "SpaceX", "Tesla", "OpenAI", "stripe",
                           "shopify", "vercel", "github", "notion", "linear"]},
)
resp.raise_for_status()
profiles = resp.json().get("users", [])
```

**节省：** 10 个账号只需一次请求，而非十次。节省幅度随数量线性增加，每次最多 100 个账号。

### 用 `/tweet-info-bulk` 替代循环 `/tweet-info`

已有推文 ID 列表，例如档案、提及导出或书签链接，需要当前互动指标和作者资料时，可通过批量端点一次补全最多 100 条：

```python theme={null}
# A separate /tweet-info call for each tweet would use up to 100 requests.

# Efficient: 100 tweets = 1 request
resp = requests.post(
    "https://api.sorsa.io/v3/tweet-info-bulk",
    headers={"ApiKey": API_KEY, "Content-Type": "application/json"},
    json={
        "tweet_links": [
            "https://x.com/user/status/111111",
            "https://x.com/user/status/222222",
            # ... up to 100 links
        ]
    },
)
resp.raise_for_status()
tweets = resp.json().get("tweets", [])
```

**节省：** 100 条推文只需一次请求，而非 100 次，减少 99%。

***

## 原则 3：用 `/list-tweets` 替代多次 `/user-tweets`

监测或收集多个账号的近期推文时，不要逐个轮询。将它们加入 X 列表，一次 `/list-tweets` 即可返回所有成员合并后的近期活动。

```python theme={null}
# Separate /user-tweets calls would use 30 requests per polling cycle.
LIST_ID = "YOUR_LIST_ID"

# Efficient: 1 request covers all 30 accounts
resp = requests.get(
    f"https://api.sorsa.io/v3/list-tweets?list_id={LIST_ID}",
    headers={"ApiKey": API_KEY},
    timeout=30,
)
resp.raise_for_status()
```

**仅首页估算：** 每 10 秒一次列表请求，30 天消耗 259,200 次；分别读取 30 个账号首页则需 7,776,000 次。额外页面和重试都会增加总量，活跃列表可能需要在每轮内分页。

详细说明见[实时监测](https://docs.sorsa.io/zh-Hans/real-time-monitoring)。X 列表的创建和管理见[列表与社群](https://docs.sorsa.io/zh-Hans/lists-and-communities)。

***

## 原则 4：将 `/info` 作为通用解析端点

`/info` 接受用户名、用户 ID 或资料链接，并返回包含永久用户 ID 的完整资料，是统一不同输入格式的灵活方式。

收到不同格式的账号引用并需要完整资料时，每个账号只调用一次 `/info`，无需先调用 `/username-to-id` 或 `/link-to-id` 再单独查询资料：

```python theme={null}
# Resolving an ID and then fetching its profile would use two requests.

# Efficient: 1 request per account
response = requests.get(
    "https://api.sorsa.io/v3/info",
    headers={"ApiKey": API_KEY},
    params={"username": "stripe"},       # accepts username, user_id, or user_link
    timeout=30,
)
response.raise_for_status()
profile = response.json()
# profile already contains the user ID, plus everything else
```

**何时单独使用转换端点：** 仅需要 ID 或用户名，而不需要完整资料时，例如将 1,000 个用户名转为 ID 存入数据库。如果之前步骤已有资料，直接复用 `id`，不再请求。转换模式见 [ID 转换](https://docs.sorsa.io/zh-Hans/ID-Conversion)。

***

## 原则 5：在数据库层去重

从搜索、粉丝、提及和时间线等多个来源收集数据时，同一用户会多次出现。使用永久用户 ID 去重，并更新已有记录，避免重复插入。

```python theme={null}
import sqlite3

db = sqlite3.connect("audience.db")
db.execute("""
    CREATE TABLE IF NOT EXISTS users (
        user_id TEXT PRIMARY KEY, username TEXT, display_name TEXT,
        description TEXT, followers_count INTEGER, tweets_count INTEGER,
        verified INTEGER, updated_at TEXT
    )
""")

def upsert_user(db, user):
    """Insert or update a user record keyed by permanent User ID."""
    db.execute("""
        INSERT INTO users (user_id, username, display_name, description,
                          followers_count, tweets_count, verified, updated_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
        ON CONFLICT(user_id) DO UPDATE SET
            username = excluded.username,
            display_name = excluded.display_name,
            description = excluded.description,
            followers_count = excluded.followers_count,
            tweets_count = excluded.tweets_count,
            verified = excluded.verified,
            updated_at = datetime('now')
    """, (
        user["id"], user["username"], user.get("display_name", ""),
        user.get("description", ""), user.get("followers_count", 0),
        user.get("tweets_count", 0), user.get("verified", False),
    ))
    db.commit()


# Every time you encounter a user in any API response, upsert:
for tweet in search_results:
    upsert_user(db, tweet["user"])
    # The user's profile data stays fresh without separate /info calls
```

这样，每次用户出现在搜索、提及、粉丝、评论或引用响应中，用户表都会更新为最新资料。没有再次出现在输入中的账号不会自动刷新。应用需要明确的数据新鲜度时，应定期批量刷新。

***

## 原则 6：不要重复获取已有数据

构建多步骤管道时，将数据向后传递，而非再次获取。

**示例：受众地理分析。** 流程是先获取粉丝，再逐个通过 `/about` 查询国家。第一步已包含完整粉丝资料，如简介、粉丝数和认证状态。第二步无需再次调用 `/info`，只需通过 `/about` 获取标准资料没有的国家数据。完整流程见[受众地理分布](https://docs.sorsa.io/zh-Hans/Audience-Geography)。

**示例：活动验证。** 检查关注、转推和评论时，`/check-comment` 在 `commented: true` 时已包含完整评论推文。需要检查正文质量时直接提取，无需另外调用 `/search-tweets` 或 `/comments` 查找。

**示例：从搜索结果构建用户列表。** 如果已从推文嵌入的 `user` 对象收集到 500 位不同用户，想筛选粉丝超过 10,000 的账号时，直接过滤已有数据，无需为每位用户调用 `/info`。

***

## 快速参考：选择正确端点

| 已有数据      | 需要的数据     | 推荐方式                                | 避免方式                          |
| :-------- | :-------- | :---------------------------------- | :---------------------------- |
| 用户名列表     | 全部完整资料    | `GET /info-batch`（一次请求）             | 循环调用 `/info`                  |
| 推文 URL 列表 | 完整推文和作者资料 | `POST /tweet-info-bulk`（每次最多 100 条） | 循环调用 `/tweet-info`            |
| 30 个待监测账号 | 全部近期推文    | `GET /list-tweets`（一次请求）            | `/user-tweets` × 30           |
| 用户名，需完整资料 | ID、简介、计数等 | `GET /info`                         | 先 `/username-to-id` 再 `/info` |
| 推文搜索结果    | 作者资料      | 提取 `tweet["user"]`                  | 为每位作者调用 `/info`               |
| 粉丝列表      | 每位粉丝资料    | 已在 `/followers` 响应中                 | 为每位粉丝调用 `/info`               |

***

## 估算请求预算

项目开始前先估算总请求数：

| 任务                   | 高效方式                     | 所需请求   |
| :------------------- | :----------------------- | :----- |
| 50 个账号资料             | `/info-batch`            | 约 1    |
| 某账号的 10,000 位粉丝      | `/followers` 分页，每页 200 位 | 50     |
| 这 10,000 位粉丝的国家      | 逐个调用 `/about`            | 10,000 |
| 为 100 条推文补全当前指标      | `/tweet-info-bulk`       | 1      |
| 每 10 秒监测 50 个账号，持续一天 | `/list-tweets`           | 8,640  |
| 验证 1,000 名参与者的 5 项任务 | 每人 5 次检查                 | 5,000  |

典型竞品情报和活动验证流程可能合计为：50（粉丝）+ 10,000（地理）+ 1（批量推文）+ 8,640（监测）+ 5,000（活动）≈ 23,700 次请求。按每秒 20 次计算，吞吐的理论最短耗时约 20 分钟。但监测本身仍持续一整天，串行分页、响应延迟和重试也会增加实际耗时。各套餐成本见[价格](https://api.sorsa.io/pricing)。

***

## 后续步骤

* [速率限制](https://docs.sorsa.io/zh-Hans/rate-limits)：处理 429 错误并优化吞吐
* [分页](https://docs.sorsa.io/zh-Hans/pagination)：大规模提取的游标分页方式
* [价格](https://api.sorsa.io/pricing)：了解每次请求成本并规划预算
* [API 参考](https://docs.sorsa.io/zh-Hans/api-reference-guide)：`/info-batch`、`/tweet-info-bulk` 和全部端点的完整规范
