WEIR

Twitter Webhook API in 2026: Account Activity vs Streams

How the Twitter webhook API works in October 2026: who gets Account Activity access, how the CRC check works, stream limits, and a tested verifier.


Published · Updated · 7 min read

Cover image: Which X webhook fits you

The Twitter webhook API is X's V2 Webhooks API: you register an HTTPS URL, pass a CRC check, and X posts events to it.

This guide is for developers who want X (Twitter) data pushed, not polled. You get what X offers as of October 2026, and a tested verifier for a signed alternative. We run Weir, that alternative.

Every X webhook must answer the CRC challenge first. This function builds the reply X expects:

import base64, hashlib, hmac

def crc_response(crc_token: str, client_secret: str) -> dict:
    digest = hmac.new(client_secret.encode(), crc_token.encode(), hashlib.sha256).digest()
    return {"response_token": "sha256=" + base64.b64encode(digest).decode()}

Here is how any X webhook gets its events:

  1. Register your URLPOST /2/webhooks with a bearer token.
  2. Pass the CRCAnswer X's GET with crc_token.
  3. Subscribe or linkAdd users, or link stream rules.
  4. Verify each POSTCheck the signature on the raw body.

What is the Twitter webhook API in 2026?

It's a delivery layer. Three X products send events through it, per X's docs on 22 October 2026:

Product Pushes Who can use it Price listed
Account Activity API Events on accounts that opt in Pay-per-use or Enterprise None on pricing page
X Activity API Events for user ids you pick Self-serve, 1,500 subs $0.005 per post event
Filtered stream webhooks Posts matching your rules Enterprise only Not listed

All three share one setup: POST /2/webhooks, the CRC, and a signature on each delivery.

Account Activity API: who can use it and what it sends

The Account Activity API sends nearly everything that happens to a subscribed account:

  • Posts and deletes, including replies and mentions.
  • Engagement: likes, follows, blocks and mutes.
  • Direct Messages, with typing and read receipts.
  • Revokes, when someone removes your app.

There's a catch. Subscribing a user takes that user's OAuth 1.0a tokens, so it covers only accounts that sign in to your app.

X's quickstart says access is by application, on the Pay Per Use and Enterprise tiers:

3user subscriptions on pay-per-use
1webhook on pay-per-use
24 hhow far back a replay job reaches

X's pricing page lists no price for it. Posts it delivers count toward the monthly Post cap.

How the CRC challenge and X's signatures work

X checks that you own the URL before it sends events, and keeps checking.

  • The CRC: a GET with crc_token, answered as the function at the top does.
  • Its key: your app's OAuth 2.0 client secret, or the legacy consumer secret. Never the bearer token.
  • Timing: at registration, on PUT /2/webhooks/:id, and on a timer.
  • Failure: a wrong token, a redirect or a TLS error marks the hook invalid.
  • Signatures: X-Twitter-Webhooks-Signature-OAuth2 holds a base64 HMAC of the raw body.

Answer each POST within 10 seconds, and expect repeats: X's quickstart says to deduplicate by event ID.

Two gaps stand out. The signature covers the body alone, with no timestamp to stop replays. And X's webhook pages promise retries but publish no schedule.

Filtered stream: the push alternative for keywords

For posts that match words, X's answer is the filtered stream. You add rules such as "breaking news" has:images, then hold GET /2/tweets/search/stream open.

X's docs give about 4-5 seconds of latency at P99. On pay-per-use you get:

  • 1,000 rules of up to 1,024 characters each.
  • One connection per app, which you must keep alive.
  • A blank line every 20 seconds. Reconnect if that much time passes in silence.

The same rules can post to a webhook, but X's docs say that delivery is Enterprise only.

X bills reads per resource, at $0.005 a post.

For one account's new posts, the X Activity API's post.create is simpler. Our account alert tutorial sets it against a Weir monitor.

Weir's webhooks: signed batches from a scheduled monitor

Weir pushes too, but on a schedule. A monitor runs, keeps only new results, and posts them to your webhook. For X, a monitor can watch three things:

  • search: a query on X, Reddit or both.
  • x_user: one account's posts.
  • x_thread: new replies to one post.

Free accounts get one monitor and one webhook, daily or weekly. Any $5 pack allows hourly runs. A delivery has this shape, from Weir's monitor docs:

{
  "id": "evt_…",
  "type": "monitor.result.created",
  "createdAt": "2026-07-30T11:04:22.000Z",
  "data": {
    "monitorId": "…",
    "runId": "…",
    "kind": "search",
    "itemType": "post",
    "newResults": 3,
    "results": []
  }
}

results holds each post exactly as the REST API returns it. The delivery rules:

  • Up to 25 results per delivery, one delivery per run. Read any extra from /v1/monitors/{id}/results, free.
  • Retries back off over about nine hours, from 1 minute apart to 6 hours.
  • No retry after a 4xx other than 429. A redirect counts as a failure.
  • 20 failures in a row disable the webhook. One success clears the streak.
  • The test is free: POST /v1/webhooks/{id}/test sends a signed webhook.test with empty data.

There's no CRC. Weir signs every delivery with a secret it shows you once. No code? Our Zapier guide checks the signature in a Run Python step.

How do you verify a Weir webhook signature?

Weir's scheme looks like Standard Webhooks, but it isn't that spec. Standard Webhooks signs id.timestamp.body and sends base64 in webhook-* headers, so its libraries won't accept a Weir delivery. Weir's rules:

  • Headers: weir-signature, weir-timestamp and weir-event-id.
  • Signed string: {timestamp}.{eventId}.{rawBody}, hashed with HMAC-SHA256.
  • Encoding: lowercase hex, with no v1, prefix.
  • Key: the whole whsec_ secret as text. Don't strip the prefix or base64-decode it.

A verifier with only Python's standard library:

import hashlib, hmac, json, time

def verify_weir(raw: bytes, headers: dict, secret: str, tolerance: int = 300) -> dict:
    ts = headers.get("weir-timestamp", "")
    if not ts.isdigit() or abs(time.time() - int(ts)) > tolerance:
        raise ValueError("stale or missing weir-timestamp")
    signed = f"{ts}.{headers.get('weir-event-id', '')}.".encode() + raw
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected.encode(), headers.get("weir-signature", "").encode()):
        raise ValueError("bad weir-signature")
    return json.loads(raw)

On 22 October 2026 we signed eight deliveries in Node, the way Weir's sender builds them, and ran it on each:

PASS  good webhook.test              -> webhook.test
PASS  good monitor.result.created    -> monitor.result.created
PASS  one byte changed               -> bad weir-signature
PASS  re-serialized JSON (indent 2)  -> bad weir-signature
PASS  wrong secret                   -> bad weir-signature
PASS  timestamp 6 minutes old        -> stale or missing weir-timestamp
PASS  event id swapped               -> bad weir-signature
PASS  base64-decoded secret          -> bad weir-signature

Pass it the raw bytes, like await request.body() in FastAPI, and deduplicate on the event id.

Account Activity API vs filtered stream vs Weir: which to use?

Pick by what you watch and how fast you need it:

You need Account Activity API Filtered stream Weir monitor
DMs, likes, follows Yes, being retired No No
Any public account's posts No, users must opt in Yes, from: rule Yes, x_user
Keyword matches No Yes, about 4-5 s Yes, hourly or daily
Delivery to your webhook Yes Enterprise only Yes, signed
Price, October 2026 Not listed Per post read 1 credit a run + 1 a result
Setup Apply, CRC, OAuth Bearer token API key

Our verdict: for events on accounts, use the X Activity API, AAA's successor. Use the filtered stream for keywords within seconds, and a Weir monitor when an hour is fine.

When not to use Weir

  • Seconds matter. Weir runs hourly at best.
  • You need DMs, likes or follows. Weir reads public data only.
  • You want a vendor's live rules. As of October 2026, TwitterAPI.io's webhook filter rules can check every 0.05 seconds, and its API can post.
  • A contract with X is required. Weir isn't licensed by X, so access can change.

Weir is read-only, so it can't post or reply for you.

FAQ

Is the Twitter webhook API free?

No. X's webhooks need pay-per-use credits or an Enterprise plan. X lists $0.005 per X Activity API post event, as of October 2026.

Does X still have the Account Activity API?

Yes, but X's docs say it is being deprecated and point to the X Activity API. Pay-per-use access allows 3 subscriptions, and each user must authorize your app.

How do I get X webhooks for new posts?

For real time, subscribe to the X Activity API's post.create event. If an hour's delay is fine, a Weir x_user monitor sends new posts to your webhook. Our Twitter to Discord guide relays them to a channel.

What is the CRC check in the Twitter API?

It's X's proof that you own the URL. X sends a GET with crc_token, and you reply with its HMAC-SHA256, keyed with your client secret. A wrong reply marks the webhook invalid.

Sources

All checked on 22 October 2026. X's pages were read as Markdown from docs.x.com.