事件
信封
每个事件都有相同的信封;d 携带下文描述的类型专属载荷。
| 字段 | 类型 | 含义 |
|---|---|---|
v | int | 信封版本。目前为 1 |
t | string | 事件类型,见下表 |
op | create | update | delete | create、update(同一 id 的补全)或 delete |
id | string | 稳定标识。按它去重 |
ts | int (ms) | 在 X 上发生的时间,自纪元起的毫秒数。X 不提供资料变更、关注与取关、新增认证粉丝和删帖发生的时刻:这些事件的值是我们检测到的时间 |
sid | string | 用于回放的流位置;出现在流中和历史记录中 |
target.id | string | 被监控账号的 X 数字 id(search.match 中为帖子作者的) |
target.screen_name | string | 被监控账号的用户名 |
target.name | string | 账号的显示名 |
d | object | 类型专属载荷 |
事件类型
| 类型 | 类别 | 含义 |
|---|---|---|
tweet.create | 帖子 | 该账号发布了新内容 |
tweet.reply | 帖子 | 它回复了某人;reply_to 携带被回复的帖子,含文本和媒体 |
tweet.retweet | 帖子 | 它转推了某人;retweet_of 携带完整的原帖 |
tweet.quote | 帖子 | 它引用了某人;quoted 携带完整的被引用帖子 |
tweet.edit | 帖子 | 它编辑了一条帖子;事件携带新版本,edit_of 是被它替换的版本 |
tweet.delete | 帖子 | 该账号发布过的一条帖子消失了 |
profile.name | 资料 | 显示名称已更改 |
profile.screen_name | 资料 | 用户名已更改 |
profile.bio | 资料 | 简介已更改 |
profile.location | 资料 | 位置已更改 |
profile.url | 资料 | 资料链接已更改 |
profile.avatar | 资料 | 头像已更改;old 和 new 为图片 URL |
profile.banner | 资料 | 横幅已更改;old 和 new 为图片 URL |
profile.pinned | 资料 | 置顶帖子已更改;tweet 和 old_tweet 携带帖子本身 |
profile.verified | 资料 | 认证标识出现或消失 |
follow.add | 关注关系 | 该账号开始关注某人;user 是对方的完整资料 |
follow.remove | 关注关系 | 该账号取消关注了某人 |
followers.verified.add | 关注关系 | 一个认证账号开始关注它;user 携带简介、计数、位置、注册日期 |
account.status | 账号 | 账号变为受保护、被封禁或不可见,或恢复正常 |
search.match | 搜索 | 有人发布了匹配你某个关键词的内容;keyword 说明是哪个,author 是其完整资料 |
对于“开始关注”或“新增粉丝”,X 不提供时间戳,所以这些事件的 ts 是我们检测到该变化的时刻。
你不必接收每个账号的每一种类型。选择事件类型介绍了如何只收其中几种,例如只收新增蓝V粉丝。
载荷
五种载荷结构覆盖全部十八种类型。
Tweet 载荷
tweet.create、tweet.reply、tweet.retweet 和 tweet.quote 的 d,也是 search.match 的基础。
| 字段 | 类型 | 含义 |
|---|---|---|
tweet_id | string | 帖子 id,字符串 |
kind | tweet | reply | retweet | quote | 帖子的种类 |
text | string | 全文(含长帖) |
lang | string | null | X 检测到的语言 |
author | User | 发布它的账号,完整的 User 对象,与 search.match 的作者相同:用户名、显示名、简介、头像、横幅、各项计数、认证、位置、链接和注册时间。精简第一帧里是最近一次读到的账号资料;update 携带与帖子一同读到的资料 |
reply_to | Brief+ | null | 回复时:被回复的帖子——一个 Brief 加上其作者的 screen_name、tweet_id 和 user_id |
retweet_of | Brief | null | 转推时:原帖 |
quoted | Brief | null | 引用时:被引用的帖子 |
media | string[] | 纯图片 URL,每个媒体项一个 |
media_items | Media[] | 每个媒体项,含类型、视频变体和尺寸 |
urls | string[] | 帖子中展开后的链接 |
article | Article | null | 帖子链接了 X 文章时:该文章 |
counts | {reply, rt, like, quote, bookmark} | 我们读取时的回复、转推、点赞、引用和书签计数 |
views | int | null | 浏览量,X 提供时才有 |
url | string | 帖子的规范链接 |
full | bool | 在第一帧精简的 create 上为 false;事件携带整条帖子后为 true |
kind_final | bool | true 表示 kind 已确定;false 只出现在 kind 为 tweet 的精简第一帧上:这条帖子仍可能是引用,随后的 update(同一 id)会给出定论 |
kind_was | tweet | null (update only) | 只出现在确定帖子种类的那个 update 上,且仅当第一帧给出的种类不同:第一帧当时的种类(结果是引用的帖子为 tweet) |
edit_of | Brief | null | 仅 tweet.edit:被这一版替换的版本(它的 id,以及我们有记录时的正文和作者) |
示例
{
"v": 1,
"t": "tweet.create",
"op": "create",
"id": "tweet:2097879002992070835",
"ts": 1789012345678,
"sid": "1789012346012-0",
"target": {
"id": "1652541",
"screen_name": "Reuters",
"name": "Reuters"
},
"d": {
"tweet_id": "2097879002992070835",
"kind": "tweet",
"text": "Ash-covered shoes and a president in crisis…",
"lang": "en",
"author": {
"id": "1652541",
"screen_name": "Reuters",
"name": "Reuters",
"bio": "Top and breaking news, pictures and videos from Reuters.",
"avatar": "https://pbs.twimg.com/profile_images/1874154135869616128/nJDmubGJ_normal.jpg",
"banner": "https://pbs.twimg.com/profile_banners/1652541/1573687397",
"followers": 25900000,
"following": 1150,
"tweets": 1080000,
"location": "Around the world",
"url": "https://www.reuters.com",
"verified": true,
"verified_type": "Business",
"protected": false,
"created_at": "2007-03-20T17:46:05Z",
"category": null
},
"reply_to": null,
"retweet_of": null,
"quoted": null,
"media": [
"https://pbs.twimg.com/media/….jpg"
],
"media_items": [
{
"type": "photo",
"url": "https://pbs.twimg.com/media/….jpg",
"expanded_url": "https://x.com/Reuters/status/2097879002992070835/photo/1",
"width": 1200,
"height": 675
}
],
"urls": [
"https://reut.rs/…"
],
"counts": {
"reply": 12,
"rt": 40,
"like": 131,
"quote": 3,
"bookmark": 9
},
"views": 20411,
"url": "https://x.com/Reuters/status/2097879002992070835",
"full": true,
"kind_final": true
}
}第一帧 create 刻意精简:它携带 id、作者(author,即最近一次读到的账号资料)以及文本(可能被截短),因此能在看到帖子的那一刻就发出;url 已指向该帖子。此刻引用帖未必能与原创帖区分,这样的帧会标为 tweet.create 并带 kind_final: false。随后同一 id 上的 update 携带上面的完整载荷和确定的类型:如果是引用,则为 tweet.quote 并带 kind_was: "tweet"。
同一条帖子首次投递时的样子,尚未补全
{
"v": 1,
"t": "tweet.create",
"op": "create",
"id": "tweet:2097879002992070835",
"ts": 1789012345678,
"sid": "1789012346012-0",
"target": {
"id": "1652541",
"screen_name": "Reuters",
"name": "Reuters"
},
"d": {
"tweet_id": "2097879002992070835",
"kind": "tweet",
"text": "Ash-covered shoes and a president in crisis…",
"author": {
"id": "1652541",
"screen_name": "Reuters",
"name": "Reuters",
"bio": "Top and breaking news, pictures and videos from Reuters.",
"avatar": "https://pbs.twimg.com/profile_images/1874154135869616128/nJDmubGJ_normal.jpg",
"banner": "https://pbs.twimg.com/profile_banners/1652541/1573687397",
"followers": 25900000,
"following": 1150,
"tweets": 1080000,
"location": "Around the world",
"url": "https://www.reuters.com",
"verified": true,
"verified_type": "Business",
"protected": false,
"created_at": "2007-03-20T17:46:05Z",
"category": null
},
"url": "https://x.com/Reuters/status/2097879002992070835",
"full": false,
"kind_final": false
}
}删除载荷
tweet.delete 的 d。只针对我们见过且现已消失的帖子发送。它的 ts 是我们检测到删除的时间;帖子本身的时间在它之前的 create 帧里。
| 字段 | 类型 | 含义 |
|---|---|---|
tweet_id | string | 被删帖子的 id |
url | string | 它原来的位置 |
示例
{
"v": 1,
"t": "tweet.delete",
"op": "delete",
"id": "tweet:2097879002992070835:delete",
"ts": 1789012345678,
"sid": "…",
"target": {
"id": "1652541",
"screen_name": "Reuters",
"name": "Reuters"
},
"d": {
"tweet_id": "2097879002992070835",
"url": "https://x.com/Reuters/status/2097879002992070835"
}
}资料变更载荷
所有 profile.* 事件的 d。这些事件的 op 为 update。
| 字段 | 类型 | 含义 |
|---|---|---|
field | string | 哪个资料字段变了;与类型中 profile. 之后的词相同 |
old | any | 之前的值 |
new | any | 新的值 |
tweet | Brief (pinned only) | profile.pinned 时:新置顶的帖子 |
old_tweet | Brief (pinned only) | profile.pinned 时:之前置顶的帖子,如果我们有的话 |
示例
{
"v": 1,
"t": "profile.bio",
"op": "update",
"id": "profile:1652541:bio:9c1d2e3f4a5b",
"ts": 1789012400000,
"sid": "…",
"target": {
"id": "1652541",
"screen_name": "Reuters",
"name": "Reuters"
},
"d": {
"field": "bio",
"old": "Top news and analysis from around the world.",
"new": "Top news, analysis and video from around the world."
}
}关注载荷
follow.add、follow.remove 和 followers.verified.add 的 d。
| 字段 | 类型 | 含义 |
|---|---|---|
user | User | 对方账号,完整的 User 对象 |
示例
{
"v": 1,
"t": "follow.add",
"op": "create",
"id": "follow:44196397:12:add:496948",
"ts": 1789012400000,
"sid": "…",
"target": {
"id": "44196397",
"screen_name": "elonmusk",
"name": "Elon Musk"
},
"d": {
"user": {
"id": "12",
"screen_name": "jack",
"name": "jack",
"bio": "…",
"avatar": "https://pbs.twimg.com/profile_images/…",
"banner": null,
"followers": 6400000,
"following": 4200,
"tweets": 29000,
"location": "",
"url": null,
"verified": true,
"verified_type": "blue",
"protected": false,
"created_at": "2006-03-21T20:50:14Z",
"category": null
}
}
}账号状态载荷
account.status 的 d。状态取值为 active(正常)、protected(帖子仅对批准的关注者可见)、suspended(被 X 封禁)或 unavailable(已停用、已删除或因其他原因不可见)。ts 是我们检测到变化的时间。
| 字段 | 类型 | 含义 |
|---|---|---|
old | active | protected | suspended | unavailable | 之前的状态 |
new | active | protected | suspended | unavailable | 现在的状态 |
reason | string | null | X 给出的原文说明(如有,例如 Suspended) |
示例
{
"v": 1,
"t": "account.status",
"op": "update",
"id": "account:9000002:status:active>suspended:497010",
"ts": 1789236000000,
"sid": "…",
"target": {
"id": "9000002",
"screen_name": "some_account",
"name": "Some Account"
},
"d": {
"old": "active",
"new": "suspended",
"reason": "Suspended"
}
}搜索命中载荷
search.match 的 d:一个 Tweet 载荷加上关于命中的三个字段。信封中的 target 是帖子作者,不必是你的监控目标之一。
| 字段 | 类型 | 含义 |
|---|---|---|
keyword.id | int | 命中的关键词的 id |
keyword.query | string | 它的查询 |
keyword.label | string | null | 它的标签,如果你设置了的话 |
author | User | 作者的完整资料 |
author_is_target | bool | 如果作者同时也是本系统监控的账号,则为 true |
… | TweetPayload | Tweet 载荷的所有字段 |
示例
{
"v": 1,
"t": "search.match",
"op": "create",
"id": "search:12:2097900000000000000",
"ts": 1789012500000,
"sid": "…",
"target": {
"id": "9000001",
"screen_name": "some_trader",
"name": "Some Trader"
},
"d": {
"tweet_id": "2097900000000000000",
"kind": "tweet",
"text": "$SOL breaking out…",
"lang": "en",
"author": {
"id": "9000001",
"screen_name": "some_trader",
"name": "Some Trader",
"bio": "…",
"followers": 1200,
"verified": false,
"…": "…"
},
"keyword": {
"id": 12,
"query": "($SOL OR \"Solana\") -filter:retweets",
"label": "solana"
},
"author_is_target": false,
"media": [],
"media_items": [],
"urls": [],
"url": "https://x.com/some_trader/status/2097900000000000000",
"full": true
}
}