> ## 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 上的每条推文会产生三类公开互动：评论（回复）、引用推文和转推。推文本身可显示累计数量，但这些数字背后的具体用户和内容需要进一步获取。Sorsa 提供专门端点提取三类互动：谁回复以及说了什么、谁引用以及补充了什么、谁进行了转推。

本指南介绍如何获取推文的完整互动数据，从整体指标快照到具体回复、引用和转推用户。

> **注意：** 更多分析示例和端到端工作流请参阅博客上的 [Twitter 互动 API：获取回复、引用和转推用户](https://api.sorsa.io/blog/twitter-engagement-api)。

***

## 起点：获取推文指标

深入具体互动前，通常需要先了解整体情况。`/tweet-info` 返回包含全部互动计数的完整推文对象。

### 最简单的示例

```bash theme={null}
curl -X POST https://api.sorsa.io/v3/tweet-info \
  -H "ApiKey: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tweet_link": "https://x.com/elonmusk/status/1234567890"}'
```

```python theme={null}
import requests

API_KEY = "YOUR_API_KEY"

def get_tweet(tweet_link):
    resp = requests.post(
        "https://api.sorsa.io/v3/tweet-info",
        headers={"ApiKey": API_KEY, "Content-Type": "application/json"},
        json={"tweet_link": tweet_link},
    )
    resp.raise_for_status()
    return resp.json()


tweet = get_tweet("https://x.com/elonmusk/status/1234567890")

print(f"Text: {tweet['full_text'][:100]}...")
print(f"Likes:    {tweet.get('likes_count', 0):,}")
print(f"Retweets: {tweet.get('retweet_count', 0):,}")
print(f"Quotes:   {tweet.get('quote_count', 0):,}")
print(f"Replies:  {tweet.get('reply_count', 0):,}")
print(f"Views:    {tweet.get('view_count', 0):,}")
print(f"Bookmarks:{tweet.get('bookmark_count', 0):,}")
```

`tweet_link` 接受完整推文 URL 或数字推文 ID。一次获取多条推文时，使用 `/tweet-info-bulk`，每次最多 100 个链接，见[优化 API 使用](https://docs.sorsa.io/zh-Hans/optimizing-api-usage)。

> **提示：** 新账号均有 100 次免费请求，无需信用卡、永不过期。也可在 [API Playground](https://api.sorsa.io/playground) 中无代码运行本页的任意端点。

***

## 评论（回复）

**端点：**`POST /v3/comments`

返回指定推文下的回复。每页最多 20 条评论，每条都是完整推文对象，包含自身互动指标和作者资料。

### 最简单的示例

```python theme={null}
def get_comments(tweet_link):
    resp = requests.post(
        "https://api.sorsa.io/v3/comments",
        headers={"ApiKey": API_KEY, "Content-Type": "application/json"},
        json={"tweet_link": tweet_link},
    )
    resp.raise_for_status()
    return resp.json()

data = get_comments("https://x.com/elonmusk/status/1234567890")
for comment in data.get("tweets", []):
    print(f"@{comment['user']['username']}: {comment['full_text'][:80]}")
```

### 参数

| 参数            | 类型     | 必需 | 说明                                            |
| :------------ | :----- | :- | :-------------------------------------------- |
| `tweet_link`  | string | 是  | 完整推文 URL 或推文 ID。                              |
| `order_by`    | string | 否  | 排序：`"Relevance"`（默认）、`"Recency"` 或 `"Likes"`。 |
| `next_cursor` | string | 否  | 获取更多评论的分页游标。                                  |

将 `order_by` 设为 `"Likes"`，API 就会在服务端按互动排序。只需要排名靠前的少数回复时，这比获取全部后在本地排序更高效，因为第一页已经包含点赞最多的评论。

### 分页获取所有评论

有数百条回复的推文需要分页，方式与其他 Sorsa 端点相同，使用游标循环：

```python theme={null}
import time

def get_all_comments(tweet_link, max_pages=20):
    """Fetch all comments under a tweet with pagination."""
    all_comments = []
    next_cursor = None

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

        resp = requests.post(
            "https://api.sorsa.io/v3/comments",
            headers={"ApiKey": API_KEY, "Content-Type": "application/json"},
            json=body,
        )
        resp.raise_for_status()
        data = resp.json()

        comments = data.get("tweets", [])
        all_comments.extend(comments)

        next_cursor = data.get("next_cursor")
        if not next_cursor:
            break
        time.sleep(0.1)

    return all_comments


comments = get_all_comments("https://x.com/elonmusk/status/1234567890")
print(f"Collected {len(comments)} comments")
```

每条评论均为完整推文对象，包含正文、互动指标和作者资料。常见用法包括按 `likes_count` 排序寻找互动最高的回复、按 `?` 筛选问题，以及将 `full_text` 输入情感分类器。

```python theme={null}
# Find the most-liked comments
top_comments = sorted(comments, key=lambda c: c.get("likes_count", 0), reverse=True)

for c in top_comments[:5]:
    print(f"@{c['user']['username']} ({c['likes_count']} likes): {c['full_text'][:80]}")
```

***

## 引用推文

**端点：**`POST /v3/quotes`

返回引用指定推文（带评论转推）的帖子。与评论类似，每条引用都是完整推文对象，包含附加评论、互动指标和作者资料。

### 最简单的示例

```python theme={null}
def get_quotes(tweet_link):
    resp = requests.post(
        "https://api.sorsa.io/v3/quotes",
        headers={"ApiKey": API_KEY, "Content-Type": "application/json"},
        json={"tweet_link": tweet_link},
    )
    resp.raise_for_status()
    return resp.json()

data = get_quotes("https://x.com/elonmusk/status/1234567890")
for quote in data.get("tweets", []):
    print(f"@{quote['user']['username']} quoted: {quote['full_text'][:80]}")
```

### 参数

| 参数            | 类型     | 必需 | 说明                |
| :------------ | :----- | :- | :---------------- |
| `tweet_link`  | string | 是  | 原始推文的完整 URL 或 ID。 |
| `next_cursor` | string | 否  | 分页游标。             |

### 分页获取所有引用

```python theme={null}
def get_all_quotes(tweet_link, max_pages=20):
    """Fetch all quote tweets of a specific tweet."""
    all_quotes = []
    next_cursor = None

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

        resp = requests.post(
            "https://api.sorsa.io/v3/quotes",
            headers={"ApiKey": API_KEY, "Content-Type": "application/json"},
            json=body,
        )
        resp.raise_for_status()
        data = resp.json()

        quotes = data.get("tweets", [])
        all_quotes.extend(quotes)

        next_cursor = data.get("next_cursor")
        if not next_cursor:
            break
        time.sleep(0.1)

    return all_quotes
```

每条引用都包含作者资料和附加文本，因此可按潜在传播范围对引用者排名：

```python theme={null}
quotes = get_all_quotes("https://x.com/brand/status/1234567890")

# Find quotes that reached the largest audiences
quotes.sort(key=lambda q: q["user"].get("followers_count", 0), reverse=True)

for q in quotes[:5]:
    u = q["user"]
    print(f"@{u['username']} ({u['followers_count']:,} followers)")
    print(f"  \"{q['full_text'][:80]}...\"\n")
```

***

## 转推用户

**端点：**`POST /v3/retweeters`

返回转推指定推文的**用户**，最新在前。与 `/comments` 和 `/quotes` 不同，该端点返回 `UsersResponse`（用户资料数组），而非 `TweetsResponse`。得到的是转推者资料，不是推文对象。

### 最简单的示例

```python theme={null}
def get_retweeters(tweet_link):
    resp = requests.post(
        "https://api.sorsa.io/v3/retweeters",
        headers={"ApiKey": API_KEY, "Content-Type": "application/json"},
        json={"tweet_link": tweet_link},
    )
    resp.raise_for_status()
    return resp.json()

data = get_retweeters("https://x.com/elonmusk/status/1234567890")
for user in data.get("users", []):
    print(f"@{user['username']} ({user['followers_count']} followers) retweeted")
```

### 参数

| 参数            | 类型     | 必需 | 说明               |
| :------------ | :----- | :- | :--------------- |
| `tweet_link`  | string | 是  | 完整推文 URL 或推文 ID。 |
| `next_cursor` | string | 否  | 分页游标。            |

### 响应格式差异

三个互动端点的主要区别：

| 端点            | 返回内容 | 响应键      | 包含数据            |
| :------------ | :--- | :------- | :-------------- |
| `/comments`   | 推文   | `tweets` | 完整推文对象（正文 + 作者） |
| `/quotes`     | 推文   | `tweets` | 完整推文对象（正文 + 作者） |
| `/retweeters` | 用户   | `users`  | 仅用户资料对象         |

转推没有独立正文，只是再次传播原始推文，因此端点返回各转推者的资料。

### 分页获取所有转推用户

```python theme={null}
def get_all_retweeters(tweet_link, max_pages=20):
    """Fetch all users who retweeted a tweet."""
    all_users = []
    next_cursor = None

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

        resp = requests.post(
            "https://api.sorsa.io/v3/retweeters",
            headers={"ApiKey": API_KEY, "Content-Type": "application/json"},
            json=body,
        )
        resp.raise_for_status()
        data = resp.json()

        users = data.get("users", [])
        all_users.extend(users)

        next_cursor = data.get("next_cursor")
        if not next_cursor:
            break
        time.sleep(0.1)

    return all_users
```

可以汇总转推者粉丝数，作为潜在受众指标。但它不是实测触达量：受众可能重叠，关注某账号也不意味着一定看到了推文。

```python theme={null}
retweeters = get_all_retweeters("https://x.com/brand/status/1234567890")

total_reach = sum(u.get("followers_count", 0) for u in retweeters)
verified_count = sum(1 for u in retweeters if u.get("verified"))

print(f"Retweeters: {len(retweeters)}")
print(f"Combined follower reach: {total_reach:,}")
print(f"Verified retweeters: {verified_count}")
```

***

## 单条推文的完整互动分析

结合三个端点，可以全面了解推文表现。由于每类互动都要分页，可能产生大量请求，每页扣除一次配额。因此仅对确实需要完整分析的推文使用：

```python theme={null}
def full_engagement_report(tweet_link):
    """Generate a complete engagement report for a single tweet."""

    tweet = get_tweet(tweet_link)
    print(f"Tweet by @{tweet['user']['username']}:")
    print(f"  \"{tweet['full_text'][:100]}...\"")
    print(f"  Likes: {tweet.get('likes_count', 0):,} | "
          f"Views: {tweet.get('view_count', 0):,}")
    print()

    comments = get_all_comments(tweet_link, max_pages=10)
    print(f"Comments: {len(comments)}")
    if comments:
        top_comment = max(comments, key=lambda c: c.get("likes_count", 0))
        print(f"  Most liked: @{top_comment['user']['username']} "
              f"({top_comment['likes_count']} likes)")
        print(f"  \"{top_comment['full_text'][:80]}...\"")
    print()

    quotes = get_all_quotes(tweet_link, max_pages=10)
    print(f"Quotes: {len(quotes)}")
    if quotes:
        biggest_quoter = max(quotes, key=lambda q: q["user"].get("followers_count", 0))
        print(f"  Highest reach: @{biggest_quoter['user']['username']} "
              f"({biggest_quoter['user']['followers_count']:,} followers)")
        print(f"  \"{biggest_quoter['full_text'][:80]}...\"")
    print()

    retweeters = get_all_retweeters(tweet_link, max_pages=10)
    total_reach = sum(u.get("followers_count", 0) for u in retweeters)
    print(f"Retweeters: {len(retweeters)}")
    print(f"  Combined reach: {total_reach:,} followers")
    if retweeters:
        top_rt = max(retweeters, key=lambda u: u.get("followers_count", 0))
        print(f"  Biggest amplifier: @{top_rt['username']} "
              f"({top_rt['followers_count']:,} followers)")

    return {
        "tweet": tweet,
        "comments": comments,
        "quotes": quotes,
        "retweeters": retweeters,
    }


report = full_engagement_report("https://x.com/brand/status/1234567890")
```

### 输出示例

```text theme={null}
Tweet by @brand:
  "We're excited to announce our Series B funding round of $50M..."
  Likes: 2,847 | Views: 892,000

Comments: 156
  Most liked: @tech_journalist (89 likes)
  "Congrats! What's the plan for international expansion?..."

Quotes: 43
  Highest reach: @vc_partner (284,000 followers)
  "This team has been on our radar for two years. Well deserved...."

Retweeters: 312
  Combined reach: 4,218,000 followers
  Biggest amplifier: @industry_leader (892,000 followers)
```

***

## 分析多条推文（批量模式）

需要一组推文的互动数据时，例如某活动的全部帖子，先通过 `/user-tweets` 或 `/search-tweets` 获取列表，再逐条深入分析：

```python theme={null}
def compare_tweet_engagement(tweet_links):
    """Compare engagement breakdown across multiple tweets."""
    results = []

    for link in tweet_links:
        tweet = get_tweet(link)
        comments = get_all_comments(link, max_pages=3)
        quotes = get_all_quotes(link, max_pages=3)
        retweeters = get_all_retweeters(link, max_pages=3)

        rt_reach = sum(u.get("followers_count", 0) for u in retweeters)

        results.append({
            "text": tweet["full_text"][:60],
            "likes": tweet.get("likes_count", 0),
            "comments": len(comments),
            "quotes": len(quotes),
            "retweets": len(retweeters),
            "retweet_reach": rt_reach,
        })
        time.sleep(0.5)

    print(f"{'Tweet':<62} {'Likes':>6} {'Cmts':>5} {'Qts':>4} {'RTs':>4} {'RT Reach':>10}")
    print("-" * 100)
    for r in results:
        print(f"{r['text']:<62} {r['likes']:>6} {r['comments']:>5} "
              f"{r['quotes']:>4} {r['retweets']:>4} {r['retweet_reach']:>10,}")

    return results
```

> **提示：** 如果只需要多条推文的汇总指标，无需具体评论、引用和转推者，可使用 `/tweet-info-bulk` 一次获取最多 100 条。更多批量方式见[优化 API 使用](https://docs.sorsa.io/zh-Hans/optimizing-api-usage)。

***

## 将互动数据导出到 CSV

```python theme={null}
import csv

def export_comments_to_csv(comments, output_file="comments.csv"):
    fields = [
        "comment_id", "created_at", "full_text", "likes", "retweets",
        "author_username", "author_followers", "author_verified",
    ]
    with open(output_file, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        writer.writeheader()
        for c in comments:
            u = c.get("user", {})
            writer.writerow({
                "comment_id": c["id"],
                "created_at": c["created_at"],
                "full_text": c["full_text"],
                "likes": c.get("likes_count", 0),
                "retweets": c.get("retweet_count", 0),
                "author_username": u.get("username", ""),
                "author_followers": u.get("followers_count", 0),
                "author_verified": u.get("verified", False),
            })
    print(f"Exported {len(comments)} comments to {output_file}")
```

相同方式也适用于引用，因为它们也是推文对象。转推者应导出用户字段，而非推文字段。

***

## 验证端点：特定用户是否参与互动？

如果需要检查特定用户是否评论、引用或转推了某条推文，例如活动或抽奖验证，Sorsa 提供检查具体行为的专用验证端点。`/check-retweet` 可能需要分页，`/check-quoted` 返回状态而非布尔值：

* `/check-comment`：特定用户是否回复了推文？
* `/check-quoted`：是否引用了推文？
* `/check-retweet`：是否转推了推文？

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

***

## 后续步骤

* [搜索推文](https://docs.sorsa.io/zh-Hans/search-tweets)：按关键词查找推文，再分析互动
* [追踪提及](https://docs.sorsa.io/zh-Hans/search-mentions)：监测品牌提及，再分析讨论最多的内容
* [竞品分析](https://docs.sorsa.io/zh-Hans/Competitor-Analysis)：比较竞争对手内容的互动模式
* [历史数据](https://docs.sorsa.io/zh-Hans/historical-data)：获取旧推文并分析其互动
* [营销活动验证](https://docs.sorsa.io/zh-Hans/Marketing-Campaign-Verification)：确认特定用户是否评论或转推
* [API 参考](https://docs.sorsa.io/zh-Hans/api-reference-guide)：`/comments`、`/quotes`、`/retweeters`、`/tweet-info` 和 `/tweet-info-bulk` 的完整规范
