Twitter (X) Tweets Scraper avatar

Twitter (X) Tweets Scraper

Pricing

$19.99/month + usage

Go to Apify Store
Twitter (X) Tweets Scraper

Twitter (X) Tweets Scraper

Scrapes posts from Twitter X profiles, hashtags, or searches, capturing tweet text, media, timestamps, metrics, authors, hashtags, and URLs. Ideal for trend research, competitor analysis, sentiment studies, influencer insights, and large-scale Twitter X post extraction.

Pricing

$19.99/month + usage

Rating

5.0

(3)

Developer

Scraper Engine

Scraper Engine

Maintained by Community

Actor stats

0

Bookmarked

49

Total users

4

Monthly active users

9 days ago

Last modified

Share

X (Twitter) Post Scraper — Text, Engagement, Media and Poll Data

Extract public X (Twitter) posts from any profile or single post URL and get one structured JSON row per post: full untruncated text, likes, replies, reposts, quotes, bookmarks and views, every photo and video, hashtags, mentions, quoted posts, and live poll results — plus the author's follower count, bio and verification status. No parsing, no HTML, no official Twitter API key. Point it at a handle or a post URL below and run it.


🐦 What is X (Twitter) Post Scraper?

X (Twitter) Post Scraper collects public posts from X profiles and individual post URLs and returns them as typed, normalized JSON — one row per post. It reads X's own GraphQL timeline directly rather than rendering pages in a browser. No X account or login is required for profile timelines or single post URLs; an X session (two cookie values) is only needed for the optional "profile's own replies" surface. It's built for social-media analysts, marketers, researchers and developers who need structured post data without maintaining scraping infrastructure themselves.


🔓 What X post data is publicly available to scrape?

A profile's own posts, reposts, quotes and pinned post are visible to anyone on x.com without logging in, and so is any single post reached by direct URL — including its poll results and the post it quotes. The one surface X gates behind a login is a profile's replies to other people's posts (the "Posts and replies" tab); everything else below is open.

Data categoryPublic (no login)Behind an X session
Profile's own posts, reposts and quotes
Single post by URL
Poll choices and live vote counts
Quoted post text, author and URL
Author profile stats (followers, bio, avatar)
Profile's replies to other accounts' posts✅ requires auth_token + ct0
A protected account's posts❌ not accessible to this scraper at all — only approved followers see them
Who liked a given post❌ not exposed by X to any caller, logged in or not

X (Twitter) Post Scraper only returns publicly visible data — what any visitor sees. Nothing behind a login wall, beyond the one optional replies surface above.


📦 What data can I extract with X (Twitter) Post Scraper?

Every post row carries the post's own content, its engagement and author metrics, and any attached media, poll or quoted post — 39 fields in total, listed below exactly as the actor writes them.

Post content and identity fields

FieldDescription
type"tweet" for a post row, "error" for an uncharged accounting row
idThe post's X id
urlCanonical post URL
user_postedAuthor's handle. On a repost, this is the original author, not the account that reposted it
nameAuthor's display name
descriptionFull post text, untruncated — long-form "note" posts are read in full, not the 280-character preview
date_postedISO 8601 UTC timestamp
langX's detected language code
conversation_idId of the thread this post belongs to
is_repost / reposted_byWhether this timeline entry is a repost, and the handle that reposted it
is_quoteWhether the post quotes another post
is_reply / in_reply_to_user / in_reply_to_post_idWhether the post is a reply, and its target
is_pinnedtrue/false for a profile timeline row; null for a post fetched by URL, since pin state is a profile-level fact
hashtags / tagged_usersHashtag texts and mentioned handles used in the post
biography / external_url / profile_image_linkAuthor's bio, website (expanded, not the t.co shortener) and avatar
input{"url": <canonical post URL>}, carried through for joining rows back to their source

Engagement and author metrics

FieldDescription
likes, replies, reposts, quotes, bookmarksEngagement counts. null when X does not report one — never a fabricated 0
viewsInteger view count, or null when X reports a view state without a number
followers, following, posts_countAuthor's follower count, following count and total post count
is_verified / is_blue_verifiedBoth mirror X's paid blue-check flag (is_verified is kept for backward compatibility)
verified_typeX's account class when set, e.g. "Government", "Business" — else null

Media, polls and nested objects

FieldDescription
photosEvery photo URL on the post, or null
videosEvery video/GIF MP4 URL, best bitrate first, or null
mediaOne object per attachment: type, image_url, video_url, alt_text, width, height, duration_ms, expanded_url
quoted_postThe quoted post's data_posted (a shipped, intentionally-kept typo — not date_posted), description, post_id, profile_id, profile_name, profile_username, url, videos; null if nothing is quoted
pollchoices (label + live vote count), total_votes, counts_are_final, duration_minutes, end_datetime_utc, last_updated_datetime_utc; null if the post has no poll
link_previewcard_type, title, description, domain, url for a link card; null if none

An uncharged error row replaces the post rows for a target that returned nothing: type: "error", id: null, target, errorReason (one of user_not_found, account_suspended, protected_account, user_unavailable, post_not_found, post_unavailable, no_posts_returned, auth_required, rate_limited, request_failed), message, input.

🤖 Add-on: Need additional X or cross-platform data?

If you also need X profile-centric fields beyond what a post row carries, pair this actor with Twitter (X.com) Tweets & Profiles Scraper. Monitoring the same conversation on Meta's Threads app in parallel? Threads Search Post Scraper: Posts, Quotes & Replies returns a comparable post-level shape for that platform.


⚖️ How does X (Twitter) Post Scraper differ from the official X API?

X does publish an official API (docs.x.com), but it requires creating a developer account and app at console.x.com and authenticating with a bearer token before any request — X (Twitter) Post Scraper needs none of that for public timelines and post URLs. As of the pricing page checked 2026-08-04, the X API runs on pay-per-usage credits with no free tier for new signups ("The X API uses pay-per-usage pricing. No subscriptions—pay only for what you use."); this actor is billed through Apify's per-result pricing instead.

FeatureOfficial X APIX (Twitter) Post Scraper
Access setupDeveloper account + app registration + bearer tokenProvide profile URLs or handles in the Apify Console
Pricing modelPay-per-usage credits (docs.x.com, checked 2026-08-04)Apify pay-per-result (row_result event)
Approval requiredDeveloper account creationNone
Rate limitingEnforced by X per app/bearer tokenEnforced by X per guest token; this actor reads the live headers and retries or reports a typed error
Output shapeRaw GraphQL/REST payload matching X's internal schemaNormalized flat JSON, one row per post, stable field names
Access to a profile's replies to othersAvailable with the right access levelRequires only your own X session cookies, no developer approval

Use the official API when you need a contractual, developer-account-backed integration; use X (Twitter) Post Scraper when you want structured post data in the Apify Console without registering an app or managing credits.


▶️ How to use X (Twitter) Post Scraper

Run it from the Apify Console — no separate API signup, no browser to install.

  1. Open the actor's page on the Apify Store and click Try for free (or Run, if you already have it).
  2. Add at least one entry to the required startUrls field — a profile URL, @handle, numeric user id, or a single post URL.
  3. Optionally set maxTweets (1–1000, default 10), sortOrder, or turn on includeReplies (with an X session — see Input below).
  4. Start the run.
  5. Download the results as JSON, CSV, Excel, XML or HTML from the dataset's Export panel, or stream them via the API.

Scaling to bulk post extraction

startUrls is an array — list any number of profiles and post URLs in one run, mixed freely, and each target is processed and charged independently. There is no separate "bulk mode" input: adding more lines to startUrls is the bulk mechanism. A target that returns nothing (suspended account, deleted post) gets its own uncharged error row rather than silently vanishing from the output.


💡 What can you do with X post data?

  • 📊 Social media analysts tracking a brand's account use likes, reposts, views and date_posted to chart engagement per post over a campaign window without opening each thread by hand.
  • 🔬 Researchers studying how claims spread use is_repost, reposted_by and quoted_post to trace an original post through the reposts and quote-posts that carried it further.
  • 🏛 Public affairs and compliance teams monitoring official accounts use verified_type and is_blue_verified to separate government or business accounts from unverified handles at scale.
  • 📈 Growth marketers benchmarking competitors use followers, posts_count and biography to profile competing accounts before planning a campaign.
  • 🤖 AI engineers feed description, hashtags and poll results directly into an LLM summarization or trend-detection pipeline — the output is typed JSON, so no scraping or HTML-parsing step sits between X and the agent.

🚦 How does X (Twitter) Post Scraper handle rate limits and blocking?

The actor discovers X's current public web bearer token and GraphQL query ids at runtime (no bundled credentials, no browser launched), then activates a keyless guest token for public timelines and single posts. It reads X's live x-rate-limit-* response headers on every call rather than assuming a fixed number. On an HTTP 429 for a keyless call, it rotates to a fresh guest token and retries once; if the rate-limit window resets within 90 seconds it waits that out and retries; otherwise the target gets a typed rate_limited error row instead of an empty result. A non-429 request failure gets one retry on a freshly rotated Apify Proxy exit, if proxy is enabled, before the target is recorded as failed. The actor does not solve CAPTCHAs — none is implemented in the source.


⬇️ Input

ParameterRequiredTypeDescriptionExample value
startUrlsYesarray of stringsProfile URLs/handles/numeric ids, or single post URLs, one per line["https://x.com/NASA", "@NASAWebb"]
maxTweetsNointeger (1–1000, default 10)Posts to collect per profile. Ignored for single post URLs25
sortOrderNostring enum: recent (default), oldest, popularRe-orders the posts collected in this run; does not page further back"recent"
includeRepliesNoboolean, default falseAlso collect the profile's replies to other accounts. Requires auth_token + ct0false
auth_tokenNostring (secret)auth_token cookie value from an x.com session. Only used for includeReplies"a1b2c3..."
ct0Nostring (secret)ct0 cookie value (CSRF token) from the same session. Required together with auth_token"d4e5f6..."
proxyConfigurationNoobject, default {"useApifyProxy": false}Apify Proxy configuration; off by default{"useApifyProxy": false}

Example input

{
"startUrls": [
"https://x.com/NASA",
"@NASAWebb",
"https://x.com/Interior/status/2083180599066374311"
],
"maxTweets": 25,
"sortOrder": "recent",
"includeReplies": false,
"auth_token": "",
"ct0": "",
"proxyConfiguration": { "useApifyProxy": false }
}

⬆️ Output

Results are written to the Apify dataset as one typed JSON row per post, with a consistent field set across every run. Export directly to JSON, CSV, Excel, XML or HTML from the Console, or read the dataset through the API.

Example output

{
"type": "tweet",
"id": "2083281738525028863",
"url": "https://x.com/NASALangley/status/2083281738525028863",
"user_posted": "NASALangley",
"name": "NASA Langley Research Center",
"description": "Full, untruncated post text describing today's launch window.",
"date_posted": "2026-08-02T18:41:03.000Z",
"likes": 702,
"replies": 37,
"reposts": 89,
"quotes": 4,
"bookmarks": 71,
"views": 246519,
"lang": "en",
"conversation_id": "2083281738525028863",
"is_repost": false,
"reposted_by": null,
"is_quote": false,
"is_reply": false,
"in_reply_to_user": null,
"in_reply_to_post_id": null,
"is_pinned": false,
"is_verified": true,
"is_blue_verified": true,
"verified_type": "Government",
"followers": 3519786,
"following": 63,
"posts_count": 11542,
"profile_image_link": "https://pbs.twimg.com/profile_images/1234567890/avatar.jpg",
"biography": "Exploring the universe and our home planet.",
"external_url": "https://nasa.gov/langley",
"hashtags": ["Artemis"],
"tagged_users": ["NASA"],
"photos": ["https://pbs.twimg.com/media/example123.jpg"],
"videos": [],
"media": [
{ "type": "photo", "image_url": "https://pbs.twimg.com/media/example123.jpg", "alt_text": "Launch pad at sunrise", "width": 2048, "height": 1365, "video_url": null, "duration_ms": null, "expanded_url": "https://x.com/NASALangley/photo/1" }
],
"quoted_post": null,
"poll": null,
"link_preview": null,
"input": { "url": "https://x.com/NASALangley/status/2083281738525028863" }
}

Field-by-field descriptions are in the "What data can I extract" section above. Error rows use a different shape (type: "error", target, errorReason, message) and are pushed without a charged_event_name, so they never bill — filter them out with type == "tweet" when consuming the dataset.


⚙️ How does it work?

X (Twitter) Post Scraper calls X's own GraphQL API directly — the same endpoints x.com's web client uses — rather than rendering pages in a browser. It discovers the current public bearer token and operation ids from X's live client bundle at runtime, so it isn't tied to a hardcoded credential that goes stale when X ships a new frontend build. Public timelines and post lookups run on a keyless guest token; the optional replies surface uses the X session cookies you supply. Requests can optionally go through Apify Proxy. Only data visible to a logged-out (or, for replies, logged-in) visitor is ever returned, and the output field names stay the same run over run regardless of changes to X's own page layout.


🔌 Integrations

X (Twitter) Post Scraper runs on the Apify platform, so it works with anything that can call Apify's API or client libraries.

Calling it programmatically

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_API_TOKEN>")
run = client.actor("twitter-x-tweets-scraper-scraper").call(run_input={
"startUrls": ["https://x.com/NASA"],
"maxTweets": 25,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["url"], item["likes"])

Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request against the Apify API.

No-code tools (n8n, Make)

In n8n, use the official Apify node and point it at this actor's id to start a run and pull the resulting dataset into your workflow. In Make, the Apify app's "Run an Actor" module does the same, and you can chain the dataset output straight into a spreadsheet or CRM module.


Scraping publicly available data on X is generally lawful in most jurisdictions, since X (Twitter) Post Scraper returns only what any logged-out visitor already sees on x.com. That said, post rows include personal data about identifiable people — the author's handle, display name, bio and follower count — so GDPR and CCPA still govern how you store and use it once collected: you need a lawful basis (e.g. legitimate interest) for holding personal data at scale, and CCPA gives California residents rights over data collected about them. Consult legal counsel if your use case involves bulk storage of personal data.


❓ Frequently asked questions

What X (Twitter) post fields does the scraper return?

The top fields are description (post text), likes, replies, reposts and views, plus the author's followers and biography. The full 39-field list is in the "What data can I extract" section above.

Does X (Twitter) Post Scraper require an X account or login?

No, not for profile timelines or single post URLs — those run on a keyless guest token. An X session (auth_token + ct0 cookies) is required only for includeReplies; without it, that option makes the run fail immediately with an explanation rather than return a partial timeline.

How many posts can I extract in one run?

Up to maxTweets (1–1000, default 10) per profile in startUrls, and any number of profiles or post URLs per run — each is processed and billed independently. A single post URL always returns exactly one row regardless of maxTweets.

What happens if a profile is suspended, protected, or doesn't exist?

That target produces an uncharged error row instead of post rows, with a machine-readable errorReasonuser_not_found, account_suspended, protected_account, or user_unavailable — and a plain-language message. If every target in a run fails this way, the run itself ends with a failed status rather than a green empty dataset.

Can I scrape multiple X profiles or posts at once?

Yes — startUrls accepts any number of profile URLs, handles, numeric user ids and single post URLs mixed together in one run.

Does X (Twitter) Post Scraper work with Claude, ChatGPT and other AI agent tools?

It has no dedicated MCP server. It's callable as a standard HTTP endpoint through the Apify API or the apify_client SDK, so any agent framework that can make an HTTP call or use a tool-calling wrapper can invoke it and consume the JSON result directly.

What makes this actor's post data different from a generic X scraper?

It unwraps reposts to the original post's real engagement counts and author, rather than the zero-filled retweet wrapper X's own API structurally returns; it reads self-thread replies from timeline module entries that a simpler parser silently drops; and it reports live poll vote counts and an authoritative counts_are_final flag rather than treating polls as static text.

Does X (Twitter) Post Scraper return data in a format LLMs can use directly?

Yes. Every row is typed, normalized JSON with consistent field names across runs — no HTML, no selectors to write. Pass it straight to an LLM, index it into a vector store, or hand it to an agent tool.

What happens when X changes its layout or anti-bot system?

The actor discovers X's live bearer token and GraphQL operation ids from X's own client bundle at runtime instead of hardcoding them, so most frontend churn doesn't break it outright, and the output field contract documented above is designed to stay stable even when X's internal payload shape shifts. No specific update turnaround time is published.

Can I use X (Twitter) Post Scraper without managing proxies or browser infrastructure?

Yes. There is no browser in the request path at all, and Apify Proxy is optional — the actor was measured working from a bare Apify datacenter exit with proxy off.

Which fields work best for AI training data and RAG indexing?

For RAG, index description (post text), biography and link_preview.description as the high-information text fields. For training data, likes, replies, reposts, views, followers and posts_count are the most consistently structured numeric fields across every row.


ScraperWhat it extracts
Twitter (X.com) Tweets & Profiles ScraperX posts and profile-centric data on the same platform
Threads Search Post Scraper: Posts, Quotes & RepliesPosts, quotes and replies on Meta's Threads
Facebook Group Posts And Details ScraperPosts, comments and engagement analytics from Facebook groups
LinkedIn Profile Post ScraperPosts published from a LinkedIn profile
TikTok Data ScraperPost and video data from TikTok

💬 Your feedback

Found a bug, or missing a field you need from an X post? Let us know through the Issues tab on this actor's Apify Store page, or message us directly through the Apify Console — reports like this go straight into the next update.