Events

Envelope

Every event has the same envelope; d carries the type-specific payload described below.

FieldTypeMeaning
vintEnvelope version. 1 today
tstringEvent type, from the table below
opcreate | update | deletecreate, update (enrichment of the same id) or delete
idstringStable identity. Deduplicate on this
tsint (ms)When it happened on X, ms since the epoch. X does not say when a profile changed, a follow or unfollow happened, a verified follower arrived or a post was deleted: for those events it is when we detected it
sidstringStream position for replay; present on the stream and in history
target.idstringNumeric X id of the monitored account (for search.match, of the post's author)
target.screen_namestringHandle of the monitored account
target.namestringThe account's display name
dobjectType-specific payload

Event types

TypeCategoryMeaning
tweet.createPostsThe account posted something new
tweet.replyPostsIt replied to someone; reply_to carries the post being replied to, with its text and media
tweet.retweetPostsIt reposted someone; retweet_of carries the original post in full
tweet.quotePostsIt quoted someone; quoted carries the quoted post in full
tweet.editPostsIt edited a post; the event carries the new version, and edit_of the version it replaces
tweet.deletePostsA post the account had published is gone
profile.nameProfileDisplay name changed
profile.screen_nameProfileHandle changed
profile.bioProfileBio changed
profile.locationProfileLocation changed
profile.urlProfileProfile link changed
profile.avatarProfileAvatar changed; old and new are image URLs
profile.bannerProfileBanner changed; old and new are image URLs
profile.pinnedProfilePinned post changed; tweet and old_tweet carry the posts themselves
profile.verifiedProfileVerification badge appeared or disappeared
follow.addFollow graphThe account started following someone; user is that account's full profile
follow.removeFollow graphThe account stopped following someone
followers.verified.addFollow graphA verified account started following it; user carries bio, counts, location, join date
account.statusAccountThe account became protected, suspended or unavailable, or active again
search.matchSearchAnyone posted something matching one of your keywords; keyword says which, author is their full profile

X provides no timestamp for "started following" or "gained a follower", so for those events ts is the moment we detected the change.

You do not have to take every type from every account. Choosing event types shows how to receive only some of them, for example only new verified followers.

Payloads

Five payload shapes cover all eighteen types.

Tweet payload

The d of tweet.create, tweet.reply, tweet.retweet and tweet.quote, and the base of search.match.

FieldTypeMeaning
tweet_idstringThe post's id, as a string
kindtweet | reply | retweet | quoteWhat kind of post it is
textstringFull text (long posts included)
langstring | nullLanguage X detected
authorUserThe account that posted it, as a full User — the same object as the author of a search.match: handle, name, bio, avatar, banner, counts, verification, location, link and join date. On a thin first frame it is the account as last read; the update carries it as read with the post
reply_toBrief+ | nullFor replies: the post being replied to — a Brief plus screen_name, tweet_id and user_id of its author
retweet_ofBrief | nullFor reposts: the original post
quotedBrief | nullFor quotes: the quoted post
mediastring[]Plain image URLs, one per media item
media_itemsMedia[]Every media item with type, video variants and dimensions
urlsstring[]Expanded links in the post
articleArticle | nullFor posts that link an X Article: the article
counts{reply, rt, like, quote, bookmark}Reply, repost, like, quote and bookmark counts at the time we read it
viewsint | nullView count, when X exposes it
urlstringCanonical link to the post
fullboolfalse on a first, thin create frame; true once the event carries the whole post
kind_finalbooltrue when kind is settled. false only on a thin first frame whose kind is tweet: the post may still turn out to be a quote, and the update on the same id settles it
kind_wastweet | null (update only)Only on the update that settles a post's kind, and only when the first frame said otherwise: the kind that frame had (tweet for a post that turned out to be a quote)
edit_ofBrief | nullOn tweet.edit: the version this one replaces (its id, and its text and author when we had it)
example
{
  "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
  }
}

A first create frame is deliberately thin: it carries the id, the author (author, the account as last read) and the text, which may be shortened, so it can leave the moment the post is seen; url already points at the post. At that moment a quote cannot always be told from an original post, so such a frame says tweet.create with kind_final: false. The update that follows on the same id carries the full payload above and the settled type: tweet.quote with kind_was: "tweet" if it was a quote.

the same post as first delivered, before enrichment
{
  "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 payload

The d of tweet.delete. Only ever sent for a post we had seen and that is now gone. Its ts is when the deletion was detected; the post's own time is in its earlier create frame.

FieldTypeMeaning
tweet_idstringThe deleted post's id
urlstringWhere it used to be
example
{
  "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 change payload

The d of every profile.* event. op is update for these.

FieldTypeMeaning
fieldstringWhich profile field changed; the same word as after profile. in the type
oldanyThe previous value
newanyThe new value
tweetBrief (pinned only)For profile.pinned: the newly pinned post
old_tweetBrief (pinned only)For profile.pinned: the previously pinned post, if we had it
example
{
  "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 payload

The d of follow.add, follow.remove and followers.verified.add.

FieldTypeMeaning
userUserThe other account, as a full User
example
{
  "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 payload

The d of account.status. A status is one of active, protected (posts visible to approved followers only), suspended (by X) or unavailable (deactivated, deleted or otherwise not visible). ts is when we detected the change.

FieldTypeMeaning
oldactive | protected | suspended | unavailableThe status before
newactive | protected | suspended | unavailableThe status now
reasonstring | nullX's own wording, when it gives one (for example Suspended)
example
{
  "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"
  }
}

The d of search.match: a Tweet payload plus three fields about the match. The envelope's target is the post's author, who need not be one of your targets.

FieldTypeMeaning
keyword.idintId of the keyword that matched
keyword.querystringIts query
keyword.labelstring | nullIts label, if you set one
authorUserThe author's full profile
author_is_targetbooltrue if the author is also an account monitored on this system
…TweetPayloadEvery field of the Tweet payload
example
{
  "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
  }
}