이벤트
엔벨로프
모든 이벤트는 같은 엔벨로프를 가지며, d에 아래에 설명하는 타입별 페이로드가 담깁니다.
| 필드 | 타입 | 의미 |
|---|---|---|
v | int | 엔벨로프 버전. 현재 1 |
t | string | 이벤트 타입. 아래 표 참조 |
op | create | update | delete | create, update(같은 id의 보강) 또는 delete |
id | string | 안정적인 식별자. 이 값으로 중복 제거 |
ts | int (ms) | X에서 발생한 시각, 에포크 이후 ms. X는 프로필 변경, 팔로우와 언팔로우, 인증된 팔로워 증가, 게시물 삭제가 일어난 시각을 알려 주지 않습니다. 이런 이벤트는 저희가 감지한 시각입니다 |
sid | string | 리플레이용 스트림 위치. 스트림과 히스토리에 존재 |
target.id | string | 모니터링 대상 계정의 숫자형 X id(search.match의 경우 게시물 작성자의 id) |
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는 저희가 변경을 감지한 시각입니다.
페이로드
다섯 가지 페이로드 형태가 열여덟 가지 타입 전부를 다룹니다.
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 | kind가 확정되면 true. false는 kind가 tweet인 가벼운 첫 프레임에만 나타납니다. 그 게시물은 아직 인용일 수 있으며, 같은 id로 이어지는 update가 확정합니다 |
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
}
}Delete 페이로드
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 페이로드
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 페이로드
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
}
}