Twitter (X) Search Scraper
Scrape X (Twitter) search by keyword or advanced query. Get the full text, engagement, media and author. No login needed from you. $0.40 per 1,000 tweets.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
searchTerms,sort,searchCommunities(sensible defaults are pre-filled). - 3Click Run
The tool runs on Apify’s cloud and collects the data for you.
- 4Export the results
Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.
Pricing
$0.0004 per tweet = $0.4 per 1,000
| You are charged for | When | Price |
|---|---|---|
| Tweet scraped | Charged once per tweet returned. | $0.0004 |
Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-08-19, and they are what you are actually charged.
Inputs
| Field | What it does | Type |
|---|---|---|
searchTerms | X search queries (advanced operators supported). | array |
sort | Latest gives you the newest tweets in the window. Top gives you what X ranks as most relevant, which skews to popular accounts. | string |
searchCommunities | X Communities have their own search index, separate from the main one. Turn this on and each of your search terms is run there too, after the normal search. Community posts arrive in the same dataset with the community id and link attached, and cost the same per post. X does not send the community's name with a search result, so that is not on the row. It searches across communities, not inside one you pick - X gives no way to scope a search to a single community. | boolean |
since | Only tweets posted on or after this date, as YYYY-MM-DD. Sorted by Latest, each search term stops once its tweets are older than this. Leave empty for no lower bound. | string |
until | Only tweets posted on or before this date, as YYYY-MM-DD. Leave empty to run up to now. | string |
language | Only tweets X tagged with this language. Leave it empty for every language. It does the same as typing lang: into a search term. | string |
minReplies | Only tweets with at least this many replies. Leave it empty for no minimum. Reply counts are X's at the moment of reading. | integer |
mediaType | Only tweets that carry this kind of media, checked against the tweet's own media list. Leave it empty to keep tweets with or without media. | string |
maxItems | Hard cap on tweets returned. You are charged per tweet, so this is also your budget for the run. | integer |
onlyVerifiedUsers | Keep only verified authors. | boolean |
notionConnector | Optional Notion delivery. | string |
notionParentId | Optional Notion data-source id. | string |
sessionCookies | Optional. Supply your own account cookie here and the run uses your account. In Chrome, logged in to x.com: F12 -> Application -> Cookies -> https://x.com, and copy auth_token and ct0. Paste one account per line as 'auth_token=XXXX; ct0=YYYY'. Several lines means several accounts, rotated. Prefer a throwaway account over your main one. Use an account you own and are authorised to automate. | array |
What you get
A structured dataset — each result includes fields like:
textlikeCountretweetCountcreatedAturlExport every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.
5 ready-to-run use cases
Twitter Stock Sentiment Feed: Posts by Cashtag
Raw feed for twitter stock sentiment work: latest $NVDA, $AMD and $TSLA posts with text, author, likes, retweets and views. No sentiment score, just posts.
Find Jobs on Twitter: Remote Engineering Hiring Posts
Find jobs on twitter. 'We're hiring' posts for remote engineer and developer roles, replies stripped out so every row is an original post with a link.
Search Tweets by Date Range with since: and until:
Search tweets by date. Put since: and until: in the query to rebuild what X said during a past launch, earnings day or incident. Top posts for that window.
Service Outage Twitter Monitor: Live Downtime Posts
Catch a service outage on twitter while users are still reporting it: latest posts saying a site, app or server is down, with author, time and engagement.
Verified Twitter Accounts Posting on Any Keyword
Top posts from verified twitter accounts on a keyword you choose, with text, author, likes, retweets and views. Unverified authors are filtered out.
Ready-made automation using this tool
A finished workflow you can import into n8n and run — this tool does the data-gathering inside it. Free, and yours to change.
Send each mention of your brand where it belongs
Watches X for mentions of your product and sorts each one: a question becomes a reply queue in Notion, a complaint goes straight to Slack with an urgency, and everything else is logged and forgotten. Most mentions are noise, and this stops them all arriving identically.
Related tools in Social Media Scrapers
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
Twitter (X) List Scraper
Scrape every tweet from an X (Twitter) list. Get full text, likes, retweets, views, hashtags, media and author. No login. $0.40 per 1,000 tweets.
Threads Scraper
Scrape public Threads profiles and recent posts with no login. Rows carry bios, followers, post text, likes, timestamps and media URLs. $0.40 per 1,000 rows.
Facebook Pages Scraper
Scrape public Facebook Page names, handles, likes, talking-about counts and verified badges. Up to 10 Pages per run. No login needed from you.
LinkedIn Profile Posts Scraper
Scrape LinkedIn profile posts with no login: post text, author, image, publish time and URL. $0.90 per 1,000 posts, flat on every plan.
LinkedIn Company Posts Scraper
Scrape public posts from any LinkedIn company page: post text, reactions, comments and URLs. No login or cookies. $0.90 per 1,000 posts.
LinkedIn Post Comments Scraper
Scrape public LinkedIn post comments: text, author name, profile URL, reactions. No login or cookie. Up to 10 per post. $0.60 per 1,000 comments.
Where this tool sits
- Categories
- Social Media Scrapers
- Platforms
- X (Twitter)
Twitter (X) Search Scraper: tweets by keyword, with the full advanced query syntax
Type the same search you would type into X and get one flat row per tweet: text, likes, retweets, replies, views, media links and the author's numbers. Advanced operators work, because whatever you type is passed to X as the query.
The part worth knowing first: X will not answer a search for a signed-out visitor, so this runs signed in. You do not need an account of your own for that, but you can supply one if you would rather have a rate limit nobody else is drawing on.
| Input | Search terms, with X's advanced operators |
| Output | One row per tweet |
| Ceiling | 5,000 tweets per run, and up to 4,000 for any one term |
| Account needed | None from you, unless you want to use your own |
| Price | $0.40 per 1,000 tweets, flat on every plan |
🔎 What Twitter (X) Search Scraper does
You give it search terms. Each one is run against X's search and paged through until it runs out of results, hits your cap, or reaches 100 pages.
Because the term is sent as written, everything X's advanced search understands works here: from:nasa, "web scraping" min_faves:50, filter:media, -filter:replies, lang:fr, since:2024-01-01. Several terms in one run are fine, and a tweet matching two of them is delivered once, not twice.
sort picks between the newest tweets in the window and what X ranks as most relevant. Top skews to big accounts, which is useful for finding the loudest voices and useless for measuring volume.
Turn on searchCommunities and each term is run against X Communities as well, after the normal search. Those posts land in the same dataset with the community id and link on them, at the same price per post. X searches across communities rather than inside one you pick, and it does not send the community's name back, so that is not on the row.
📥 What you give it
{
"searchTerms": ["\"web scraping\" min_faves:50", "from:nasa"],
"sort": "Latest",
"maxItems": 500
}
| Field | Default | What it is |
|---|---|---|
searchTerms | none | Required. Your queries, one per line, sent to X exactly as typed. The form opens with apify filled in as an example, which is a starting point rather than a default. |
sort | Latest | Latest or Top. |
searchCommunities | false | Also search X Communities with each term. |
since, until | none | YYYY-MM-DD bounds. Read the note below before relying on these. |
language | none | Only tweets X tagged with this language, picked from a list of 32. The same as adding lang: to every term. |
minReplies | none | Only tweets with at least this many replies, going by X's count when the run reads it. |
mediaType | none | any, photos or videos. Only tweets that carry that kind of media. |
maxItems | 100 | Hard cap on tweets for the whole run, up to 5,000. Since you pay per tweet, this is also the run's budget. |
onlyVerifiedUsers | false | Drop rows whose author is not verified. |
sessionCookies | none | Optional. Your own X account, as auth_token=XXXX; ct0=YYYY, one account per line. Use a throwaway account: the value sits in the run input, which anyone who can open the run can read. |
proxyConfiguration | Apify's default | Leave it alone. It is there for people who need the run to leave through servers they already pay for. |
notionConnector, notionParentId | none | Optional. Write each tweet into a Notion database as well as the dataset. |
since stops a term, until only filters. On Latest a term stops once its tweets are older than since. On Top, since also goes to X with the term. until is checked on each row, so an old window pages past newer tweets, and a tweet from the until day can fall outside. For an old window, put since: and until: in the term.
📤 What you get back
A real row from a real run, with the emoji and the author's bio cut short:
{
"ok": true,
"charged": true,
"id": "2102017699974987896",
"url": "https://x.com/apify/status/2102017699974987896",
"text": "Apify is on the front page of @bot's plugins. Thank you, @grok team and the alphabet ... https://t.co/yGXsw3Vicc",
"createdAt": "Mon Sep 21 12:50:47 +0000 2026",
"lang": "en",
"replyCount": 1,
"retweetCount": 0,
"likeCount": 0,
"quoteCount": 0,
"bookmarkCount": 0,
"viewCount": 29,
"isReply": false,
"isRetweet": false,
"isQuote": false,
"conversationId": "2102017699974987896",
"inReplyToId": null,
"inReplyToUser": null,
"hashtags": [],
"mentions": ["bot", "grok"],
"urls": [],
"media": [{ "type": "photo", "url": "https://pbs.twimg.com/media/HSvdyfaW8AAwwNs.png" }],
"author": {
"id": "3510729917",
"userName": "apify",
"name": "Apify",
"description": "Thousands of Actors to automate your business, get real-time web data, ...",
"followersCount": 16181,
"friendsCount": 300,
"statusesCount": 4897,
"verified": false,
"isBlueVerified": false,
"location": "The Interwebz",
"profileImageUrl": "https://pbs.twimg.com/profile_images/2036008913192316928/yuW3cobE_normal.png",
"createdAt": "Tue Sep 01 08:38:01 +0000 2015"
}
}
| Field | What it is |
|---|---|
text, createdAt, lang | The tweet as posted, X's own timestamp format, and the language X tagged it with. |
viewCount | Impressions. Often the most useful number on the row, and it is null on older tweets X no longer reports it for. |
isReply, isRetweet, isQuote | What kind of tweet it is, so you can filter the noise out yourself. |
conversationId, inReplyToId, inReplyToUser | Enough to rebuild a thread from a set of rows. |
hashtags, mentions, urls | Pulled out of the text as arrays. |
media | One entry per photo or video, with a direct link. |
author | The posting account: handle, name, bio and follower counts, as X reported them at read time. |
🧾 Reading the output
Three kinds of row can land in your dataset.
| Row | How to spot it | Billed |
|---|---|---|
| A tweet | ok: true and an id | yes |
| The sample row | _sample: true | no |
| A diagnostic | ok: false and an errorCode | no |
Filter on ok == true and you have your data. The charged field is there to separate real rows from sample and diagnostic rows, not as a receipt.
The sample row appears when the run could not sign in at all. Its text says so in as many words, but the numbers beside it are made up, so filter on _sample rather than trusting a table scan.
NO_RESULTS means a term genuinely matched nothing. BLOCKED means a term could not be read this run, which is worth retrying. BAD_INPUT means no usable search terms were supplied. If every term fails, an X_SEARCH_FAILED row says so and the run still finishes.
The Overview table shows text, likeCount, retweetCount, createdAt and url. Open a row or the JSON for the author block and viewCount.
▶️ How to run it
1. Open Twitter (X) Search Scraper and click Try for free. 2. Type your queries into Search terms, one per line. 3. Set Max tweets. It is both the cap and the budget. 4. Click Start. 5. Download the dataset as JSON, CSV or Excel, or pull it from the Apify API.
💰 How much does it cost?
$0.40 per 1,000 tweets. Flat on every Apify plan, no volume tiers.
You pay per tweet delivered. The sample row, diagnostic rows, and the second copy of a tweet that matched two of your search terms are not charged. maxItems is the ceiling on what a run can spend, so set it deliberately.
💡 What people use it for
- Watching a brand, a product name or a hashtag on a schedule and diffing the results week to week.
- Pulling everything one account posted in a date range with
from:handleand date operators. - Building a sentiment or topic set, where
min_faves:keeps the noise out. - Finding people talking about a problem you solve, then working the author fields into a list.
🚧 What it does not do
- X search is not an archive. It returns what its index will serve, which thins out the further
back you go. A missing tweet usually means X did not return it, not that it was missed.
- No replies under a tweet, and no follower lists. Siblings below cover those.
- No private or protected accounts, ever.
viewCountisnullon plenty of older tweets, because X stopped reporting it for them.urlsand the author'sstatusesCountcan be empty on some rows, when X's rate limit runs
out mid-run. Links stay in text as t.co addresses.
- Up to 100 pages per term, which works out at about 4,000 tweets for any single query.
- Community search is across communities, not inside a specific one. X gives no way to scope it.
- No location filter. X no longer filters search by place,
geocode:included. - Counts are a snapshot at the moment of reading, not a history.
🧭 Which X scraper do you need?
| If you want | Use |
|---|---|
| Tweets matching a search | This one |
| Specific tweets or a profile's timeline by URL | Tweet Scraper |
| Profile records for handles you already have | Twitter Profile Scraper |
| Who follows an account | X Followers Scraper |
| Everything posted into a curated X list | Twitter List Scraper |
❓ Questions people ask
Do I need an X account or an API key? Neither. The cookie field is optional and only worth filling in if you want a rate limit of your own.
Do advanced operators really work? Yes. The term goes to X as you typed it, so anything X's advanced search understands is available.
Why did I get fewer tweets than maxItems? Either the search ran out of results, or a term hit the 100-page limit. Diagnostic rows will say if something went wrong.
Can I search inside one community? No. X only offers a search across communities.
Is scraping X legal? This reads public tweets only, never private accounts or messages. Results can still contain personal data, which GDPR and similar laws cover, so have a reason for collecting it. Apify's write-up on the legality of web scraping is a good starting point, and we are not lawyers.
Can I run it on a schedule? Yes. Use Apify's scheduler, or start it from the API and read the dataset when it finishes.
🆘 If something breaks
Open the Issues tab on the actor page. Include the search terms you used and the run ID, and paste any errorCode you got back, since that usually says what happened straight away.