Skip to content

WEBHOOKS

Twitter Webhook API: Real-Time Post Alerts, Free to Call

The Twitter Webhook API is a free feature: register an HTTPS delivery URL, then watch an X account. Once the two are linked, every new post from that handle arrives as an HMAC-signed POST within seconds of detection, bound by a shared ~60-second poll interval. Every management call is $0; a paid monitoring plan is only needed to watch more than one account at a time. No X developer account required.

Per our own spec, 24 of 121 endpoints are priced at $0, and every webhook management call sits in that group. The number is published in the OpenAPI document as an x-cost-usd field rather than only in prose, so "free" here is a value your client can read and assert on, not a marketing adjective that a later repricing could quietly contradict.

What is the Twitter Webhook API?

Two endpoints, both free. POST /twitter/webhook registers an https delivery URL and returns a signing secret shown exactly once. POST /twitter/monitor then watches an X handle. Point the monitor at your webhook's id and every new post from that account arrives as a tweet.created event, signed HMAC-SHA256 over the timestamp and raw body in an X-TwitterAPIs-Signature header. There is no backfill: only posts made after the monitor is created are delivered. An optional domain_filter narrows delivery to posts linking a given host, and include_replies (default true) can turn off reply delivery. Cost: $0 per call for every webhook and monitor endpoint. Watching a second account onward needs a monitoring plan.

What you can build with account webhooks

One webhook can receive events from every monitor you point at it. The same Bearer key and the same $0 call cost cover every pattern below.

Use caseWhat it watchesWhy it matters
Brand and mention alertingGet pushed the moment a watched competitor or partner handle postsReact in minutes, not on your next poll
Trading / alpha signalsWatch a project or founder account for launch and listing postsNo polling loop to run or rate-limit against
Support and PR triageRoute a watched account's new posts straight into a queueOne webhook receiver instead of a cron job
AI agent event feedFeed tweet.created events into an LLM classification stepPush-based, so the agent runs on new data, not a timer
Content and UGC pipelinesCatch a campaign or brand account's posts as they go liveKick off downstream clipping or repost jobs immediately

For a broader event set, mentions, replies, follows and DMs, not only new posts, see the Twitter account activity API, which covers the rest of that surface on the same polling architecture.

Pick the right endpoint for what you're monitoring

A webhook is the right tool when you already know which account you care about and want each new post the moment it is seen. When the thing you watch is a phrase, or posts by other people about an account, a polling read on the same key is the better fit. Pick the pattern before you write the receiver.

You wantUseModel
Every new post from a known accountMonitor + webhook (this page)Push, $0 per call, first account free
Only that account's posts that link to your domainMonitor with domain_filterPush, same monitor slot
Posts that mention an accountTwitter mentions APIYou poll, billed per call
A keyword, hashtag or cashtag across XTwitter search APIYou poll, billed per call
Mentions, replies, follows and DMs togetherTwitter account activity APIPolling architecture over the wider event set

Weighing push against a polling loop you run yourself? Our poll cadence cost breakdown for 50 accounts prices each interval, and the build vs buy guide for X monitoring covers what a homegrown watcher has to handle. To land deliveries in a workflow tool instead of your own server, see connecting X to n8n.

Register a webhook, create a monitor, verify the signature

Three calls to go live: create a webhook and save the secret it returns once, create a monitor pointed at that webhook's id, then verify the X-TwitterAPIs-Signature header on every delivery your endpoint receives.

Setup + a signature-verifying receiver

# 1. Register your delivery URL. Save "secret" now, it is never returned again.
curl -X POST "https://api.twitterapis.com/twitter/webhook" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/webhooks/twitterapis"}'
# -> { "id": "wh_9f2a1c", "secret": "whsec_...", "status": "active", ... }

# 2. Watch a handle and point it at the webhook you just made.
curl -X POST "https://api.twitterapis.com/twitter/monitor" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"handle": "jack", "webhook_id": "wh_9f2a1c"}'
# -> { "id": "mon_4b1e7a", "status": "active", "poll_interval_ms": ..., ... }

# 3. Fire a one-shot signed test delivery before you rely on it.
curl -X POST "https://api.twitterapis.com/twitter/webhook/wh_9f2a1c/test" \
  -H "Authorization: Bearer YOUR_API_KEY"
# -> { "delivered": true, "status_code": 200, "error": null }

Every call in that setup, the webhook, the monitor, and the test send, is $0. Read the full parameter list on the Monitoring reference docs.

What a delivery looks like: payload, headers and media

Every real delivery is a JSON body with four top-level fields. tweet is the full tweet, the same object the read endpoints return, and handle is the watched account lowercased without the @. A test send uses webhook.test and carries no tweet. Shape per the Webhook Delivery Payload reference (read 2026-10-15).

tweet.created body
{
  "event": "tweet.created",
  "monitor_id": "mon_4b1e7a",
  "handle": "jack",
  "tweet": {
    "id": "1899999999999999999",
    "text": "...",
    "media": [
      {
        "type": "video",
        "media_url_https": "<poster frame image URL>",
        "video_info": {
          "duration_millis": 30066,
          "variants": [
            { "content_type": "video/mp4", "url": "<mp4 file URL>", "bitrate": 2176000 }
          ]
        }
      }
    ]
  }
}
Headers on every delivery to an endpoint you control
HeaderWhat it carries
x-twitterapis-eventtweet.created (webhook.test on a test send)
x-twitterapis-event-idStable per event, identical on every retry. Deduplicate on this.
x-twitterapis-delivery-idUnique per HTTP attempt, changes on each retry.
x-twitterapis-attemptAttempt number, starting at 1.
x-twitterapis-signaturet=<unix seconds>,v1=<hex HMAC-SHA256 of "{t}.{raw body}">

Media arrives as URLs, never bytes: photos carry media_url_https, video and animated GIFs add video_info.variants, and a text-only post has no media key at all. Register a Slack or Discord webhook URL instead and the post is sent in that platform's native message format with its images, minus the signature headers neither platform can check.

Retries, dead letters and replay: the delivery contract

What happens when your receiver is slow, down or gone. Each row links the reference page it comes from.

BehaviourRuleSource
Delivery guaranteeAt least once. A tweet can arrive twice, so make your handler idempotent.Create Webhook notes
Retries8 attempts over 21 minutes with jitter when your endpoint does not answer 2xx.Create Webhook notes
Dead letterAfter the 8th failed attempt the delivery is held, not dropped, and is never sent again until you call redrive.Replay Dead Deliveries
ReplayPOST /twitter/webhook/{id}/redrive re-queues dead deliveries: max_age_hours default 24 (1 to 168), limit default 100 (1 to 1,000).Replay Dead Deliveries
410 GoneAnswering 410 disables the webhook at once. Dead deliveries from a 410 are reported as skipped_permanent, never replayed.Replay Dead Deliveries
OrderingNot guaranteed. Sort by tweet id if you need chronological order.Create Webhook notes
Deduplicationx-twitterapis-event-id is stable across every retry of one tweet; x-twitterapis-delivery-id changes per attempt.Webhook Delivery Payload
Destination URLHTTPS on a public address. localhost, private, link-local and metadata addresses are refused at registration.Create Webhook

A test send is the exception: it is never queued, retried or dead-lettered, returns HTTP 200 when your endpoint answered 2xx and 502 when it did not, and still works on a webhook a 410 has disabled, so you can confirm a fix before you re-register.

Three free endpoints to audit delivery, not just trust it

A push architecture is only as good as its observability. Monitoring health is exposed as data, not a support ticket, and every read below is free.

GET /twitter/monitor/{id}/health

One monitor's cursor: last tweet id seen, last poll time, and a degraded flag if it is running behind the published interval.

GET /twitter/monitor/health

Account-wide rollup, no id needed: active vs paused monitor counts, plus pending, delivered and failed deliveries over the last 24 hours.

GET /twitter/monitor/deliveries

Your most recent delivery events with real per-event detected_lag_ms and delivery_lag_ms, not a marketing average.

A real measured event from the delivery log: detection took 33,984 ms (~34 s, bound by the shared poll interval) and delivery took 3,596 ms (~3.6 s) from detection to the webhook POST landing, for about 37.6 seconds end to end (event of 2026-08-13, published as the response example on the Monitor Deliveries reference). That is the honest number this feature ships with, not a best-case claim.

Pause, resume, repoint or remove a monitor

The rest of the lifecycle, every call $0. Resuming a paused monitor re-runs the same capacity check as creating one, so it can return 503 when monitoring capacity is full; an include_replies-only change never does, because it narrows delivery without adding polling load.

CallWhat it does
GET /twitter/webhookList your webhooks. The signing secret is never returned again.
DELETE /twitter/webhook/{id}Remove a webhook. Monitors pointed at it stop delivering until repointed.
GET /twitter/monitorList monitors with status, degraded and events_possibly_missed flags.
POST /twitter/monitor/{id}Pause, resume, repoint webhook_id, change domain_filter or include_replies, in one call.
DELETE /twitter/monitor/{id}Stop watching an account. There is no undelete.

What happens if I hit the rate limit?

There are three different limits here, and only one of them is a classic request rate limit. Each answers with its own status, so your client can tell them apart.

  • Request rate (429). Management calls are free but not unmetered in time: a burst can return 429 rate_limited. Back off exponentially and retry; nothing is billed either way, per the Create Monitor reference.
  • Monitoring capacity (503). Creating or resuming a monitor returns 503 capacity_unavailable when monitoring capacity cannot take one more monitor at the published interval. It is a capacity signal, not a rejection of your key. Retry later.
  • Plan slots. The number of accounts you can watch at once is the slot count of your monitoring plan, from 1 on Free to 2,000 on Scale. Move to a smaller plan while watching more accounts than it allows and the newest monitors pause, they are not deleted, until you upgrade again.

What you never manage is X's own rate limit on the watched account: polling runs on our side, so your key spends nothing per poll however often a watched handle posts. The monitor response includes poll_interval_ms, which the docs mark as not yet a published SLA.

Every call is free. Plans cover watching more accounts.

This is the one part of TwitterAPIs that is not priced per call. Creating, updating, deleting and reading webhooks and monitors costs $0 regardless of plan. The first monitored account is free forever; watching more requires a flat monthly slot plan.

PlanPriceMonitored accounts~2 s delivery badge
Free$01 accountNo
Starter$15/mo6 accountsNo
Growth$47/mo20 accountsYes
Professional$95/mo50 accountsYes
Enterprise$199/mo150 accountsYes
Enterprise Plus$399/mo500 accountsYes
Scale$799/mo2,000 accountsYes

Every new account also gets $0.50 free credit on signup, no credit card required, for the paid reads on the same key, such as the search or mentions polling the table above points to.

No API credits are ever deducted for monitoring. See live checkout and full plan detail on the pricing page's monitoring section, or explore the full API docs.

Webhook cost estimator

What watching 1 to 2,000 accounts costs a month. Monitoring is billed by how many accounts you watch, never by how many posts they make. Find the row that holds your list and read across: that plan covers every delivery for the month, whether the accounts post twice or two thousand times.

Accounts you watchPlanMonthlyPer account
1Free$0$0.00
6Starter$15$2.50
20Growth$47$2.35
50Professional$95$1.90
150Enterprise$199$1.33
500Enterprise Plus$399$0.80
2,000Scale$799$0.40

The only per-call cost is what you choose to read on top of a delivery. Fetching the replies to a delivered post is one standard read at $0.0008, so enriching 1,000 deliveries a month adds about $0.80. Watching 7 accounts means the 20-account Growth plan.

Brand monitoring with webhooks

Your accounts, the replies they draw, and the posts that link to your site.

A monitor watches one account and delivers every post that account makes, so the first step in brand monitoring is one monitor for the brand account and one for each founder or spokesperson whose posts your team answers for.

Replies are included by default (include_replies is true), which matters for a brand: a support reply from your account is the post most likely to be quoted back at you. Set it to false on an account where you only want top-level announcements.

domain_filter narrows a monitor to posts that link a domain. It matches by hostname boundary, so example.com matches blog.example.com and never notexample.com. Point it at a partner, reseller or press account to hear only the posts that link your site.

A monitor delivers the posts its own account makes. For posts addressed to your handle, poll the mentions endpoint on a schedule, and run a search for the brand name for everything else. Both feed the same handler as your webhook deliveries.

Accounts per monitor and per plan

Each monitor takes one handle, so the number of monitors on a key equals the number of accounts on its plan: 1 on the free slot, then 6, 20, 50, 150, 500 or 2,000. One account per monitor keeps each account's status, pause and cursor separate, so pausing a noisy account never touches the others.

Create them with POST /twitter/monitor, one call per handle, at no cost. At your plan's cap the call returns 403 with an upgrade message. If monitoring capacity cannot serve one more monitor at the published interval it returns 503 capacity_unavailable, and the same call can succeed on a later retry. Only posts made after a monitor is created are delivered.

TwitterAPIs webhooks vs the official X API and self-run polling

OptionPriceAuthCoverageDelivery
TwitterAPIs monitor + webhook$0 per call, free 1-account tier, plans from $15/mo for moreBearer token, 30 s signupShared 60 s poll interval; watch a handle or a domain-filtered subsetSigned POST within ~2 s of detection (Growth plan and up)
X V2 Webhooks API (official)Billed on X's own developer plansApproved X developer account, a Project and App, OAuth 2.0 App Only tokenAccount Activity, X Activity and filtered stream events, not only new postsPush, but your server must answer CRC checks and reply within 10 s
Poll GET /twitter/user/mentions yourself$0.0008 per call, no subscriptionBearer token, 30 s signupYou run the loop and pay per pollAs fast as you're willing to poll

The official row reflects X's V2 Webhooks API introduction (read 2026-10-15): an approved developer account and App are prerequisites, the URL must be HTTPS with no port, your server must reply within 10 seconds, and it must answer a Challenge-Response Check on registration and periodically after that or the webhook is marked invalid. X signs each POST with a base64 HMAC-SHA256 of the raw body keyed with your app's client secret. Here the secret is per webhook, and the signature also covers a timestamp, so a receiver can refuse a replayed request older than five minutes.

Need the wider event set, mentions, replies, follows and DMs, not just new posts, see the Twitter account activity API, which runs the same polling architecture over that broader surface.

Wire webhook events into an AI agent via MCP

Install the TwitterAPIs MCP server (@twitterapis/mcp) and webhook and monitor management become native tool calls any MCP-compatible model can invoke, create a monitor, check its health, or read recent deliveries, all in a single tool turn. Already integrated against an x_user_stream-shaped API? The oapi/x_user_stream/add_user_to_monitor_tweet compat route accepts that same request shape and resolves to the identical underlying monitor, so a migration can move at your own pace. For keyword or hashtag tracking instead of an account handle, the Twitter search API runs on the same key. To poll an account's mentions instead of pushing its new posts, use the Twitter mentions API. Need a key? Get a Twitter API key in 30 seconds.

Twitter webhook API FAQ

Two free endpoints. POST /twitter/webhook registers an HTTPS URL you control and returns an HMAC signing secret, shown once, that you use to verify deliveries. POST /twitter/monitor then watches an X account and, once you point it at a webhook, POSTs a signed tweet.created event to that URL every time the watched handle publishes. There is no backfill: only posts made after the monitor was created are delivered. Both endpoints are zero-rated, they never consume API credits.

Two separate numbers matter. Detection is bound by the shared poll interval, currently around 60 seconds across every plan and not a published SLA, so a new post can take up to about a minute to be seen. Delivery, the time from us detecting the post to our POST landing on your endpoint, is typically a couple of seconds on the Growth plan and above; on a real production event measured after shipping this feature, detection took about 34 seconds and delivery took about 3.6 seconds, for roughly 38 seconds end to end. This is a fast-polling architecture, not a firehose, and it is not marketed as sub-second push.

Every delivery is signed HMAC-SHA256 over the timestamp and the raw request body, carried in an X-TwitterAPIs-Signature header. Recompute the signature on your end using the secret returned once at webhook creation and compare it to the header before trusting the payload. A one-shot test delivery (POST /twitter/webhook/{id}/test) sends a real signed event with event set to webhook.test rather than tweet.created, so you can confirm your receiver and signature check both work before relying on it.

Creating or resuming a monitor can return 503 capacity_unavailable if monitoring capacity cannot currently serve one more monitor at the published interval. Retry later; this is a capacity signal, not a rejection of your account or your webhook.

Each delivery gets 8 attempts over 21 minutes with jitter. If every attempt fails, the delivery is dead-lettered: we still hold the event, but it is not sent again until you call POST /twitter/webhook/{id}/redrive, which re-queues dead deliveries from the last 24 hours by default (up to 168) and up to 100 per call by default (up to 1,000). Delivery is at least once, so deduplicate on the x-twitterapis-event-id header, which stays the same across every retry of one tweet.

Yes. Register a Slack incoming webhook URL (hooks.slack.com) or a Discord webhook URL (discord.com/api/webhooks) and the delivery is sent in that platform's own message format, with the author, the text, a link to the post and its images. Neither platform verifies an HMAC, so the signature headers are omitted for those two destinations. Point the monitor at an endpoint you control if you want the full JSON payload.

X's V2 Webhooks API sends a Challenge-Response Check GET to your URL on registration and periodically after that, and your server must answer it with an HMAC of X's token or the webhook is marked invalid; X also requires a reply within 10 seconds and a URL with no port. TwitterAPIs has no CRC step: you prove the endpoint works with a one-shot POST /twitter/webhook/{id}/test, and you verify each delivery with the timestamped HMAC-SHA256 signature.

Every call to create, list, update or delete a webhook or a monitor is $0, including the health and delivery-log endpoints. What is not free is watching more than one account at a time: the first monitor on any account is a free slot, and watching more requires a monitoring plan, Starter at $15/mo for 6 slots up to Scale at $799/mo for 2,000, billed as a flat monthly subscription rather than the per-call model the rest of the API uses. See the full breakdown on the pricing page's monitoring section.

No. You need only a TwitterAPIs Bearer token, issued after a 30-second signup. There is no X developer application, no app review, and no enterprise sales process, which is the gate that keeps X's own first-party Account Activity webhooks out of reach for most builders.

Not yet through the webhook path. A monitor watches one X handle, optionally narrowed with a domain_filter (deliver only posts linking to a given host) or include_replies (turn reply delivery off). For keyword or hashtag tracking, run GET /twitter/search/tweet-advanced-search on a polling loop instead, using the same Bearer key at $0.0008 per call, see the Twitter search API.

Three read endpoints, all free. GET /twitter/monitor/{id}/health returns one monitor's cursor: its last seen tweet id, last poll time, and a degraded flag. GET /twitter/monitor/health is an account-wide rollup with no id needed, active and paused counts plus pending, delivered and failed deliveries over the last 24 hours. GET /twitter/monitor/deliveries lists your most recent delivery events with real measured detected_lag_ms and delivery_lag_ms per event, so you can audit actual latency rather than trust a published number.

Yes, as URLs, never as bytes. When a post carries media, the tweet object has both tweet.extended_entities.media and a top-level tweet.media alias holding the same array; a text-only post has neither key. Video and animated GIF entries add video_info.variants with an mp4 url and bitrate per variant, and media_url_https on those is the poster frame, not the video. Treat the URLs as untrusted input and validate the host before you fetch one.

No. The secret is returned only in the response to POST /twitter/webhook and no endpoint returns it again, including GET /twitter/webhook. Delete the webhook, create a new one to get a fresh secret, and repoint your monitors at the new webhook id with POST /twitter/monitor/{id}.

No. add_user_to_monitor_tweet, remove_user_to_monitor_tweet and get_user_to_monitor_tweet are compat drop-ins that translate an x_user_stream-shaped request onto the same underlying monitor system, so a migration can point at the compat routes first and move to monitor/webhook directly on your own schedule. Both paths bill the same, which is to say nothing.

Get pushed the moment an account posts

Every webhook and monitor call is $0. First account free, plans from $15/mo to watch more. No X developer account required.