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.
Tarun Khatri
Published · Updated · 7 min read
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:
- Register your URLPOST /2/webhooks with a bearer token.
- Pass the CRCAnswer X's GET with crc_token.
- Subscribe or linkAdd users, or link stream rules.
- 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:
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-OAuth2holds 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
4xxother than429. 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}/testsends a signedwebhook.testwith emptydata.
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-timestampandweir-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.
- X V2 Webhooks API: https://docs.x.com/x-api/webhooks/introduction
- X webhooks quickstart: https://docs.x.com/x-api/webhooks/quickstart
- X Account Activity API introduction: https://docs.x.com/x-api/account-activity/introduction
- X Account Activity API quickstart: https://docs.x.com/x-api/account-activity/quickstart
- X Activity API introduction: https://docs.x.com/x-api/activity/introduction
- X filtered stream introduction: https://docs.x.com/x-api/posts/filtered-stream/introduction
- X filtered stream webhooks: https://docs.x.com/x-api/webhooks/stream/introduction
- X API pricing: https://docs.x.com/x-api/getting-started/pricing
- Standard Webhooks spec: https://github.com/standard-webhooks/standard-webhooks/blob/main/spec/standard-webhooks.md
- TwitterAPI.io filter rules and posting: https://docs.twitterapi.io/api-reference/endpoint/add_webhook_rule and https://docs.twitterapi.io/api-reference/endpoint/create_tweet_v2
- Weir monitors and webhooks: https://docs.weirapp.io/monitors
- Weir billing and tiers: https://docs.weirapp.io/billing
- Our local signing test of
verify_weir, 22 October 2026