ACCOUNT ACTIVITY
Twitter Account Activity API: Polling Alternative
Updated July 2026
How do you track X account activity without enterprise webhooks?
The Account Activity API is a first-party webhook product on X's enterprise tier: X posts account events to an HTTPS endpoint you host. The alternative is incremental polling, where a scheduled job calls ordinary REST endpoints and treats anything above a stored watermark as new. TwitterAPIs covers mentions, replies, reposts, follows and DMs that way at $0.0008 per call, with no webhook receiver to operate.
We bill each poll at $0.0008 (source: our published pricing), so a five-minute mention watch runs about $1.15 a month.
Which activity events polling can reach
Most of what the webhook product delivers is public data that a read can reach. The exceptions are private account state, and they are listed here rather than quietly omitted, because discovering a gap after you have built against it is the expensive way to learn it.
| Activity event | Status | How you watch it | Docs |
|---|---|---|---|
| Mentions of an account | Covered | GET /twitter/user/mentions, newest first, watermarked on the top post ID | View docs |
| Replies under a post | Covered | GET /twitter/tweet/replies against the post you are watching | View docs |
| New posts by an account | Covered | GET /twitter/user/tweets, comparing the top ID against your last run | View docs |
| Reposts of a post | Covered | GET /twitter/tweet/retweeters, diffed against the set you already stored | View docs |
| New followers | Covered | GET /twitter/user/followers, diffed against your stored follower set | View docs |
| Direct messages | Covered | GET /twitter/dm/list and /twitter/dm/conversation on a registered session | View docs |
| Blocks, mutes and typing indicators | Not covered | Private account state that X exposes only to its own first-party webhook product | None |
Webhooks and polling, compared honestly
Push wins on latency. Polling wins on everything you have to operate. Which matters depends entirely on whether a minute of delay changes your outcome.
| Dimension | X Account Activity webhooks | Polling on TwitterAPIs |
|---|---|---|
| Delivery | X posts each event to a public HTTPS endpoint you host | Your job asks for what is new on a schedule you control |
| Access | Enterprise access to X's Account Activity product | A Bearer token issued at signup, no application to file |
| Infrastructure | A public endpoint, a CRC challenge responder, signature validation, and replay handling | A cron entry and somewhere to store one watermark per watch |
| Latency | Seconds, pushed as the event happens | Bounded by your interval, so a 60-second cron means under a minute |
| Missed events | A failed delivery while your endpoint is down can be lost | Nothing is lost, because the next run reads from the stored watermark forward |
| Cost shape | Enterprise contract, priced per month | $0.0008 per call, so cost is your interval multiplied by your watches |
If you genuinely need signed sub-second push delivery, X's own product is the right tool and nothing here replaces it. For brand monitoring, support triage and agent inboxes, the delay is usually irrelevant and the operational saving is not.
The watermark loop, which is the whole pattern
Store one value per watch: the highest post ID you have already processed. Each run reads newest first, stops at the watermark, and handles everything above it. The loop is idempotent, so a double run processes nothing twice, and it self-heals after an outage because the watermark did not move while the job was down.
import requests
BASE = "https://api.twitterapis.com"
HEAD = {"Authorization": "Bearer YOUR_API_KEY"}
def poll_mentions(user_name, watermark):
"""Return (new_events, new_watermark). Pages until it reaches the watermark."""
fresh, cursor, top = [], None, watermark
while True:
params = {"username": user_name}
if cursor:
params["cursor"] = cursor
body = requests.get(
f"{BASE}/twitter/user/mentions", params=params, headers=HEAD
).json()
for post in body.get("tweets", []):
if watermark and int(post["id"]) <= int(watermark):
return fresh, top # caught up, stop paging
if top is None or int(post["id"]) > int(top):
top = post["id"]
fresh.append(post)
if not body.get("has_more"):
return fresh, top
cursor = body["next_cursor"]
# run this on a cron; persist the watermark between runs
events, watermark = poll_mentions("nasa", watermark=None)
print(len(events), "new mentions, watermark now", watermark)const BASE = "https://api.twitterapis.com";
const HEAD = { Authorization: "Bearer YOUR_API_KEY" };
async function pollMentions(userName, watermark) {
const fresh = [];
let cursor = null;
let top = watermark;
for (;;) {
const url = new URL(`${BASE}/twitter/user/mentions`);
url.searchParams.set("username", userName);
if (cursor) url.searchParams.set("cursor", cursor);
const body = await (await fetch(url, { headers: HEAD })).json();
for (const post of body.tweets ?? []) {
if (watermark && BigInt(post.id) <= BigInt(watermark)) {
return { fresh, watermark: top }; // caught up, stop paging
}
if (!top || BigInt(post.id) > BigInt(top)) top = post.id;
fresh.push(post);
}
if (!body.has_more) return { fresh, watermark: top };
cursor = body.next_cursor;
}
}
// run this on a schedule; persist the watermark between runs
const { fresh, watermark } = await pollMentions("nasa", null);
console.log(fresh.length, "new mentions, watermark now", watermark);The detail that matters is the inner paging loop. Reading only the first response is what makes naive polling drop events during a burst, because a spike can push more than one page of activity between two runs. Keep following next_cursor until you reach the watermark, and a burst arrives complete.
Make your first API call
Before you write the loop above, prove the one request it depends on. The whole setup is a key and a single GET, and it fits inside the free credit with room to spare.
Create an API key
Sign up with Google or email and the key is in your dashboard straight away: no X developer account, no use-case review and no card. Every new account starts with $0.50 in credit, which at $0.0008 a read is 625 polls. Public activity, meaning mentions, replies, new posts, reposters and followers, needs nothing more than that key in an Authorization: Bearer header. Direct messages are the one exception: they read against an X session you register once with a free call to POST /twitter/customer/session.
curl "https://api.twitterapis.com/twitter/user/mentions?username=nasa" \
-H "Authorization: Bearer YOUR_API_KEY"A 200 means the key works. A 401 means the header is wrong or missing, so re-copy the key and check that the word Bearer comes first. An empty result on a quiet account is a valid answer, not an error, so test against an account that is mentioned often. The real-time mention monitoring guide takes this same request from one call to a running service.
The first test to run
Run the watermark loop twice in a row against a busy account. The first run returns a page of events and stores a watermark; the second, seconds later, should return nothing or close to it. If the second run repeats events, the watermark is not being saved between runs, and that bug would have sent every alert twice in production. Then set the stored watermark back to an older post ID and run once more: the loop should page through the gap and stop at that ID, which is exactly what happens after a real outage. If you plan to reply to events, test that path with POST /twitter/draft/create, which saves a draft to the account and never publishes it. What a monitor should report when it is quietly failing, rather than merely quiet, is covered in what a monitoring API should tell you when it is failing.
What a continuous watch costs
$1.15
per month, one account polled every 5 minutes
$34.56
per month, one account polled every minute
$0
subscription, contract, and enterprise minimum
Both figures assume one call per run at the standard $0.0008 rate, which holds while activity fits a single page. Any realistic polling interval sits far under our 600 req/min ceiling, so interval is a cost decision: poll a high-value account every minute and sweep a long tail hourly on the same key. Size a mixed workload on the Twitter API cost calculator.
Flat pricing, no plan
There is no tier to pick before you know your volume, no monthly minimum and no seat. Every public activity read on this page, mentions, replies, new posts, reposters and followers, is $0.0008 a call. The two DM reads are $0.0016 a call, and so is posting a reply. Checking your balance through GET /account/me is free, and so is registering a webhook or a monitor. That makes the bill a straight product of three numbers you choose: how many accounts you watch, how often you poll each one, and how many pages a busy interval needs. The poll cadence cost breakdown for 50 accounts works that product through at every common interval.
Only completed calls are metered
A call that fails is not billed, so a retry loop with backoff around a timeout or a 429 costs time and not credit. A call that completes is billed even when it finds nothing new, and that is the part polling budgets tend to miss: a quiet account polled every minute costs the same as a busy one, because the empty answer took the same work. The lever is therefore interval, not activity. Poll the accounts where a minute matters every minute, sweep the long tail hourly, and the two groups bill at very different rates on the same key.
Respond to an event from the same key
Detecting a mention is half of most workflows; the other half is answering it. The write endpoints run as the X account behind your registered session, on the same key that runs the watch, so there is no second integration to wire up.
Create or schedule posts in reply
POST /twitter/tweet/create takes the reply text and the ID of the post you are answering in reply_to, so the event your loop just stored is all the context the reply needs. To answer at a set time instead, POST /twitter/scheduled/create takes the same fields plus execute_at in epoch seconds, and it will post publicly at that moment unless you cancel it with POST /twitter/scheduled/delete first. Both bill $0.0016 a call. For anything a person should approve, save a draft instead: it lands in the account's own composer and is never published by the API.
Upload media with a reply
Attaching an image is two steps. POST /twitter/media/upload takes the image as base64 in media_data and returns a media_id for $0.0008. Pass that ID in media_ids on the create or scheduled call, up to four per post. For a video, a GIF or a large file, read GET /twitter/media/status until processing has finished before you attach it, because an ID that is still processing is the usual reason an attachment fails.
Should I use the MCP server or the REST API?
Both reach the same endpoints at the same per-call rate on the same key, and the MCP server ships 116 tools a language model can call directly. The question is whether a model belongs in the loop at all.
For the watch itself, use REST. A polling job is deterministic and runs every interval whether or not anything happened, and putting a model in that path adds cost and a failure mode while adding no judgement, because deciding whether an ID sits above a watermark needs none. A cron entry, the loop above and a stored watermark are the whole system.
Use MCP for what happens after an event is found. An agent that receives a new mention can read the thread, pull the author's profile and draft a reply through the same tools, which is the part that does benefit from judgement. The Twitter MCP server guide covers connecting Claude, Cursor and other clients, and the MCP server page has the connection string.
Narrower surfaces for a single event type
If mentions are the only event you care about, the Twitter mentions API goes deeper on that one loop. Follower-change watches are covered on the Twitter followers API, and to react to an event rather than only record it, the Twitter engagement API covers replying, liking and reposting from the same key. The Twitter REST API overview documents the paging and error contract every watch depends on.
By the numbers
Activity monitoring, in numbers.
Sourced figures behind event access and cost.
X's Account Activity API is an enterprise product, sold under contract rather than self-serve sign-up. (X Developer Platform, 2026)
Official X API polling endpoints meter requests in 15-minute windows, which is what pushes most monitoring builds toward webhooks. (X API docs, 2026)
TwitterAPIs bills each poll at $0.0008 under one flat 600 req/min ceiling, so a one-minute mention watch is about $34.56 a month. (TwitterAPIs pricing, 2026)
Mentions, replies, reposters, followers and DM threads are each readable on a cursor-paged endpoint suitable for watermarked polling. (TwitterAPIs docs, 2026)
A new account starts with $0.50 in free credit and no card, enough to run a five-minute watch for roughly two weeks. (TwitterAPIs pricing, 2026)
Account activity, common questions
The Account Activity API is X's first-party webhook product. Rather than you asking for new data, X posts account events to an HTTPS endpoint you host, covering mentions, replies, follows, direct messages and similar activity for accounts that have authorised your app. It is enterprise-tier access, which means an application, an approved app, a subscription, and a public endpoint of your own that answers a CRC challenge and validates signatures on every delivery.
For one event type, yes. Register an HTTPS endpoint and we POST a tweet.created event to it whenever a handle you are monitoring publishes, so you do not poll for that. Each delivery is signed HMAC-SHA256 over the timestamp and the raw body in an X-TwitterAPIs-Signature header, and a failed delivery is retried on a schedule rather than dropped. Two bounds worth stating plainly. First, our side polls X on a 60-second interval, so an event typically reaches you within a minute of us seeing it, plus whatever lag X's own index adds. This is not sub-second push and we do not present it as streaming. Second, it covers new posts from a watched handle, not the rest of X's Account Activity set: mentions of you, replies, follows and DMs are still the polling loop described above.
Match the interval to how quickly a missed event actually hurts. Support triage and brand alerting are usually fine at 60 seconds, which is 43,200 calls a month per watch, roughly $34.56 at the standard rate for a single continuously polled account, or about $1.15 a month at a five-minute interval. Our own ceiling is 600 requests a minute per key, well above what any realistic polling schedule needs, so a tighter interval is a cost decision long before it is a rate one, and you can poll a high-value account fast while sweeping a long tail hourly.
Only for private surfaces. Mentions, replies, new posts, reposters and follower lists are public, so your Bearer token alone is enough. Direct messages are not public, so those read against a registered X session, which you set up once with a free call to POST /twitter/customer/session using auth_token and ct0 for the account you control. Everything else on this page needs no session at all.
Incremental polling covers most of the same ground at a fraction of the operational weight. Instead of hosting a webhook receiver, a scheduled job calls ordinary REST endpoints, stores the newest ID it has already seen, and treats anything above that watermark as a new event. TwitterAPIs bills those calls at $0.0008 each with no plan, so a mention watch polling once a minute costs roughly $1.15 a month. What you trade away is push latency: events surface within your interval rather than within seconds.
Store one value per watch: the highest post ID you have already processed. On each run, call GET /twitter/user/mentions for the account, walk the returned posts from newest down, stop as soon as you reach an ID at or below your stored watermark, and handle everything above it as new. Then save the new top ID. That loop is idempotent, so a duplicate run processes nothing twice, and it self-heals after downtime because the watermark has not moved while your job was off.
No, provided you page rather than only reading the first response. The watermark is what guarantees completeness: anything published between two runs still sits above your stored ID on the next call, so you catch it late rather than not at all. The one case that needs care is a burst larger than a single page. Keep following next_cursor until you reach an ID at or below your watermark, instead of assuming one page covers the gap.
Watch account activity without an enterprise contract
$0.0008 a poll, $0.50 in free credit. Mentions, replies, reposts, follows and DMs on one Bearer key.
Next read
Continue exploring related pages:
Twitter webhook API
Register an endpoint, watch a handle, get a signed POST the moment it posts. Every call is $0.
Twitter engagement API
Like, repost and bookmark from code, then read what moved.
Twitter followers API
Export any account's followers and following with cursor pagination, $0.0008 a call, $0.04 per 1,000 tweets on full 20-tweet pages.
Twitter REST API
The auth header, paging model and error contract in one place.