X (Twitter) Account Monitor — Posts & Engagement
Pricing
from $0.06 / 1,000 posts
X (Twitter) Account Monitor — Posts & Engagement
Watch a list of X accounts: each one's newest posts with likes, reposts, replies and quotes, the engagement per 1,000 followers, and the account's own row (followers, posts per day, age). Only-new mode pays for what appeared since the last run. No login, no API key.
Pricing
from $0.06 / 1,000 posts
Rating
0.0
(0)
Developer
Berkan Kaplan
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
18 hours ago
Last modified
Categories
Share
X (Twitter) Account Monitor — Posts, Engagement & Profiles 𝕏
Give it a list of X accounts; get their newest posts with the numbers that matter — likes, reposts, replies, quotes — plus each account's own row. Built for watching accounts over time: competitors, clients, portfolio companies, journalists, your own brand. No login, no API key, no browser: one read per account, charged per row.
- 📊 The four engagement counts on every post — likes, reposts, replies, quotes — plus what we compute from them: engagement total, engagement per 1,000 followers (comparable across accounts of different sizes) and engagement per hour since the post went up
- 👤 The account itself — followers, following, posts, listed count, bio, website, location, verification, account age, posts per day, followers-to-following ratio
- 🔁 Only-new mode — keeps a list of what it has already delivered, so an hourly watchlist pays only for posts that appeared since the last run
- 🧾 Honest about its limits — X's embed backend hands out an account's newest page only (23–105 posts, no paging) and no keyword search. This Actor says so in the run's message instead of pretending otherwise
- 🤖 Agent-ready — MCP, and pay-per-event so an AI agent can run it and settle the bill itself
What does this Actor do?
It reads X's own embed (syndication) backend — the service that renders embedded timelines and tweets on other people's websites. For each account you name it fetches that account's newest page of posts, turns every post into a flat row, and adds one row for the account itself. Nothing is logged in, no cookie jar is kept, and no browser is started, which is why a run is fast and cheap and why it does not break when X changes its web app.
Use it when you need: a watchlist of accounts checked on a schedule; engagement benchmarking across accounts; a client or competitor report; the current numbers on posts you already know.
Use something else when: you need keyword or hashtag search, follower lists, reply texts, or an account's history beyond its newest page — X's embed backend does not serve those, so this Actor does not claim them. The Input tab says the same thing where it matters.
Quick start (API)
Watch three accounts, five newest posts each:
curl -X POST "https://api.apify.com/v2/acts/foxlabs~x-account-monitor/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{ "accounts": ["@NASA", "x.com/nike", "11348282"], "maxPostsPerAccount": 5 }'
Prefer no code? Open the Input tab, paste your accounts one per line, and click Start.
Getting started (3 steps)
- Paste the accounts. A handle (
@nasa), a profile address (x.com/nasa) or the account's numeric id (11348282) — all three work, mixed in one list. - Pick how many posts per account (default 25). X hands out one page per account, so anything above ~105 simply takes everything that page holds.
- Turn on "Only posts new since the last run" if this is a watchlist you will run on a schedule. The first run delivers the page; later runs deliver only what appeared since.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
accounts | array | — | The accounts to read: @handle, x.com/handle or numeric id. Required. |
maxPostsPerAccount | integer | 25 | Newest posts per account (1–110). With only-new mode on, this counts the posts looked at, so the run never pages backwards into history. |
includeProfileRow | boolean | true | One extra row per account with the account's own fields. |
onlyNewPosts | boolean | false | Deliver only posts not delivered before (keeps a list in your own key-value store). |
listName | string | default | Which list only-new mode uses. One list per watchlist. |
skipReplies | boolean | false | Leave out posts that start with a mention. |
postedWithinDays | integer | 0 | Only posts from the last N days (0 = no limit). |
minEngagement | integer | 0 | Only posts with at least this many likes + reposts + replies + quotes. |
maxPosts | integer | 0 | Cost cap across all accounts (0 = only the per-account limit). |
concurrency | integer | 8 | Accounts read side by side. |
rememberAccountIds | boolean | true | Keeps each handle's numeric id after the first read, so later runs take the cheap road. |
proxyConfiguration | object | Apify Proxy (datacenter) | Datacenter addresses answer every read made by numeric id. Handle lookups go through residential addresses, because a handle lookup from a datacenter address is rate-limited. |
Output
One row per post, one row per account. Download as JSON, CSV, Excel or read it from the API.
{"type": "tweet","id": "2109382322516799677","url": "https://x.com/Nike/status/2109382322516799677","text": "Zheng Qinwen claims her first WTA 1000 title in Beijing. A winner's name, etched on home soil.","createdAt": "2026-10-11T20:35:10.000Z","language": "en","likes": 6398,"reposts": 527,"replies": 92,"quotes": 63,"engagement": 7080,"engagementPer1kFollowers": 0.745,"engagementPerHour": 42.24,"ageHours": 167.6,"isReply": false,"media": [{ "type": "photo", "url": "https://pbs.twimg.com/media/HUYHTdWXQAAFWCq.jpg", "width": 2160, "height": 2700 }],"mediaTypes": ["photo"],"hashtags": [],"mentions": [],"links": [],"authorHandle": "Nike","authorFollowers": 9504725,"authorVerified": true,"scrapedAt": "2026-10-18T20:13:06.871Z"}
Post fields
| Field | Type | Description |
|---|---|---|
id / url | string | Post id and its address on x.com |
text | string | The post's text as X states it |
createdAt | string | ISO 8601, UTC |
likes / reposts / replies / quotes | integer | The four counts X states on an embedded post |
engagement | integer | The four counts added up |
engagementPer1kFollowers | number | engagement / followers × 1,000 — the figure that compares a small account with a large one |
engagementPerHour | number | engagement / hours since posted — how fast it is travelling |
ageHours | number | Hours since it went up |
isReply | boolean | The post starts with a mention |
conversationId | string | The thread it belongs to |
language | string | Language code X assigned |
media[] / mediaCount / mediaTypes | array / integer / array | Photos and videos: type, direct URL, width, height |
hashtags[] / mentions[] / links[] | array | As X states them (links are the expanded addresses) |
sensitive / edited | boolean | Flags X states; null when it states none |
authorHandle / authorName / authorId / authorFollowers / authorVerified | — | The author, flattened onto the row so an export needs no join |
scrapedAt | string | When this row was read |
Account fields (type: "profile")
handle, name, bio, location, website, followers, following, tweets, likesGiven, listedIn, mediaPosted, verified, verifiedType, protected, profileImage, bannerImage, createdAt, accountAgeDays, tweetsPerDay, followersPerFollowing, withheldInCountries.
A line that cannot be delivered is written as a free type: "status" row with the reason — a line that is not an X account, an account X has no public record of, an account with nothing to embed, or an account the run could not read. Status rows are never charged.
Use cases
- Competitive watch. A list of competitor accounts, run hourly with only-new mode: you pay for new posts and see their engagement next to yours.
- Engagement benchmarking.
engagementPer1kFollowersmakes a 9,000-follower account and a 90-million-follower account comparable in the same table. - Client and campaign reporting. Post text, time, media and the four counts — the columns a report needs, in CSV.
- Feeding a dashboard or an LLM. Flat rows, stable field names, ISO timestamps.
- Keeping an eye on your own brand. Your account plus the accounts that matter to you, on a schedule.
Integration
MCP / AI agents: ask for this Actor by name — foxlabs/x-account-monitor — on the Apify MCP server. It is pay-per-event, so an agent can run it and settle the bill per row.
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('foxlabs/x-account-monitor').call({accounts: ['@NASA', '@nike'],maxPostsPerAccount: 10,onlyNewPosts: true,listName: 'brands',});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.filter((row) => row.type === 'tweet').map((row) => `${row.authorHandle}: ${row.engagement}`));
Python
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("foxlabs/x-account-monitor").call(run_input={"accounts": ["@NASA", "@nike"],"maxPostsPerAccount": 10,})for row in client.dataset(run.default_dataset_id).iterate_items():if row["type"] == "tweet":print(row["authorHandle"], row["likes"], row["url"])
Written for apify-client 3 for Python, where call() returns a run object; with version 1 or 2 read run["defaultDatasetId"].
Automation (n8n / Zapier / Make): schedule or webhook → HTTP request to the Actor API with your account list → handle the JSON dataset → push to a sheet, CRM or dashboard.
Pricing
Pay-per-event: $0.06 per 1,000 delivered posts ($0.00006 each) and $1 per 1,000 account rows ($0.001 each). Status rows and posts left out by a filter are free — you pay for rows you receive.
| What you ask for | Rows | Approx. cost |
|---|---|---|
| 100 accounts × 25 newest posts, with account rows | 2,500 posts + 100 accounts | ~$0.25 |
| 1,000 accounts watched hourly, only-new mode, ~2 new posts each per hour | ~2,000 posts/hour | ~$0.12/hour |
| One account, 100 newest posts | 100 posts | ~$0.006 |
You also pay Apify for the compute the run uses, which is small: one HTTP read per account, no browser. Apify's free tier includes $5/month of credits.
Limits — what this Actor cannot do
These are not omissions; they are what X's embed backend serves (measured 2026-10-18):
- No paging. An account's newest page only: 23–105 posts depending on the account.
maxId,cursorand friends return the same page, so there is no way to walk history. - No search. No keyword, hashtag, list or moment timelines.
- No reply texts. A post's reply count yes, the replies themselves no.
- No follower lists.
- No view counts. X does not state the view count on an embedded post.
- Protected accounts are not readable, and an account X has no public record of comes back as a free status row.
If you need search or history, use an Actor built for it — ours is built to watch accounts cheaply and reliably.
FAQ
Do I need an X account, login or API key? No. This reads X's public embed service.
How fresh is the data? Every run reads live; scrapedAt says when.
Why do I get 23 posts for one account and 105 for another? That is how many X embeds for that account. The cap you set is an upper bound, not a promise.
Can I watch 1,000 accounts? Yes — one read per account, eight at a time by default. Use only-new mode so you pay for new posts only.
What does a handle cost extra? One lookup on a residential address the first time (a handle lookup from a datacenter address is rate-limited by X). The id is then remembered in your own key-value store and later runs take the cheap road. Give numeric ids and no lookup is ever needed.
Why is a post missing? It is older than the newest page, it was left out by a filter, or it is already in your only-new list.
Troubleshooting
| What you see | What it means |
|---|---|
X's embed backend is rate-limiting handle lookups from this address | The residential lookup for that handle did not get through. Run again, or give the account's numeric id. |
X embedded nothing for @… | No public account with that name, or it is suspended or protected. Free row. |
… was read but X embedded no post for it | The account exists and has nothing embeddable (only replies, or protected posts). Free row. |
Nothing new: … already in the list | Only-new mode working: no new posts since the last run. Nothing charged. |
Run ends FAILED with did not answer the opening check | X did not answer us at all. Nothing was read or charged; try again. |
Is it legal to scrape this data?
This Actor reads a public embed service with no login and collects what X itself publishes on public profiles. It stores no private data and no credentials. Always check your own use against X's Terms of Service and the data-protection rules that apply to you, especially if you keep personal data or use it for monitoring individuals.
Support & contact
Found a defect or need a field that is missing? Open an issue on the Actor's Issues tab with the run id — every run keeps a SOURCE_REPORT record with what it saw, read, delivered and left out, which makes a report quick to answer.
Changelog
0.1 — 2026-10-18
- First release. Accounts by handle, profile address or numeric id; the newest page of posts with likes, reposts, replies, quotes and the engagement figures we compute; one row per account; only-new mode with its own list; filters for replies, age and engagement; handle → id remembered between runs; free status rows for every line that cannot be delivered.
Part of the foXLabs data platform — public-data company, contact, social, procurement and AI-search intelligence scrapers.