Twitter Scraper | Extract Tweets & Profiles | No API Key
Pricing
from $2.00 / 1,000 result scrapeds
Twitter Scraper | Extract Tweets & Profiles | No API Key
Scrape Twitter/X profiles and tweets without an API key. Batch many accounts in one run, or fetch specific tweets by ID. Tweet text, author, likes, retweets, replies, views, media URLs and timestamps. No keyword search. PPE — pay only for results delivered.
Pricing
from $2.00 / 1,000 result scrapeds
Rating
0.0
(0)
Developer
Web Data Labs
Maintained by CommunityActor stats
3
Bookmarked
249
Total users
35
Monthly active users
14 days ago
Last modified
Categories
Share
Twitter/X Scraper — Extract Tweets & Profiles Without API Keys
Scrape public Twitter/X profiles and tweets — one account, a list of accounts, or specific tweets by ID — with no API key, no developer account and no login. Structured JSON out, pay only for the rows you receive.
Keyword search is not supported. Anonymous keyword/hashtag search on X is auth-walled and this Actor will not pretend otherwise — see Limitations. If you need keyword search, you need the official X API or a logged-in scraper.
Why Use This Instead of the Official Twitter API?
| Official X API | This Actor | |
|---|---|---|
| Setup | Developer account, application, approval | None — just run it |
| Cost floor | Paid developer tiers only | Pay per result |
| Auth | API keys, OAuth, token rotation | None |
| Output | Raw API JSON | Flat, analysis-ready rows |
X's official API is locked behind a paid developer account: the legacy Basic tier ($200/mo) is closed to new signups, new developers are metered pay-per-use ($0.005/read, 2M-read cap), and anything above that jumps to Enterprise ($42k+/mo). This Actor reads the same public data.
What You Can Scrape
1. One account
{ "username": "nasa", "maxResults": 50 }
Returns the profile row plus up to 50 tweets.
2. Many accounts in ONE run
{ "usernames": ["nasa", "github", "apify"], "maxResults": 25 }
Each account contributes a profile row and its tweets. One run, one dataset — no orchestration on your side. startUrls accepts full profile URLs instead if that is what your pipeline already produces:
{ "startUrls": [{ "url": "https://x.com/nasa" }, { "url": "https://x.com/github" }] }
3. Specific tweets by ID or URL
{ "tweetIds": ["1519480761749016577", "https://x.com/nasa/status/2084712464466190650"] }
Useful for re-hydrating a list of tweet IDs, refreshing engagement metrics on posts you already track, or pulling the exact posts a webhook told you about. Deleted, protected or non-existent tweets are skipped and never charged.
4. Quoted tweets
{ "username": "elonmusk", "maxResults": 50, "includeQuotedTweets": true }
When a scraped tweet quotes another tweet, the quoted tweet is returned as its own row (type: "quoted_tweet", with quoted_by_tweet_id pointing back at the parent) — full text, author, engagement. It comes from the same API response, so it costs no extra time.
Output
Every row carries a type field: profile, tweet or quoted_tweet.
Tweet row
{"type": "tweet","tweet_id": "2084712464466190650","user_id": "11348282","user_name": "NASA","user_handle": "NASA","user_followers": 92241569,"user_verified": true,"tweet_text": "LIVE: After 241 days in orbit, @astro_ChrisW returned home on July 26...","likes": 1247,"retweets": 180,"replies": 112,"quotes": 13,"views": 776732,"bookmarks": 56,"tweet_url": "https://x.com/NASA/status/2084712464466190650","created_at": "Tue Aug 04 18:45:58 +0000 2026","is_retweet": false,"is_reply": false,"is_quote": false,"in_reply_to_tweet_id": "","in_reply_to_handle": "","conversation_id": "2084712464466190650","language": "en","hashtags": ["Artemis"],"mentions": ["astro_ChrisW", "Space_Station"],"links": ["https://www.nasa.gov/live"],"media_urls": ["https://pbs.twimg.com/media/....jpg"],"quoted_tweet_id": "","quoted_tweet_text": "","source": "twitter_api"}
hashtags, mentions, links, user_followers, user_verified, in_reply_to_* and conversation_id are new in 1.0.19 — no input change needed, they simply appear.
Profile row
{"type": "profile","user_id": "11348282","username": "nasa","screen_name": "NASA","name": "NASA","bio": "Making the seemingly impossible, possible. ✨","tweets_count": 74293,"following": 119,"followers": 92241569,"likes_count": 16904,"verified": true,"profile_image": "https://pbs.twimg.com/profile_images/.../..._400x400.jpg","banner_image": "https://pbs.twimg.com/profile_banners/11348282/1775567134","location": "Pale Blue Dot","website": "http://www.nasa.gov/","joined": "Wed Dec 19 20:20:32 +0000 2007","listed_count": 97012,"media_count": 28060,"profile_url": "https://x.com/NASA","source": "twitter_api"}
username echoes what you passed in; screen_name is X's canonical casing.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
username | string | elonmusk | A single account (with or without @) |
usernames | array | [] | Several accounts in one run |
startUrls | array | [] | Profile URLs, e.g. https://x.com/nasa |
tweetIds | array | [] | Tweet IDs or status URLs |
action | string | both | both, tweets (no profile row), or profile (legacy alias for both) |
maxResults | integer | 10 | Max tweets per account. 0 = profiles only |
includeQuotedTweets | boolean | false | Emit quoted tweets as their own rows |
Legacy aliases still accepted: maxTweets, limit, count, handle, user, screen_name.
Use Cases
- Brand monitoring — track what a set of accounts posts, with engagement metrics, on a schedule.
- Competitor research — batch several competitor handles into one daily run and diff the output.
- Influencer vetting —
maxResults: 0over a list of handles gives you follower counts, bios and links for a whole shortlist in one cheap run. - Engagement refresh — feed yesterday's
tweet_ids back in viatweetIdsto see how likes/views moved. - Content curation —
includeQuotedTweetscaptures the conversation around a post, not just the post. - Academic research — flat CSV/JSON export of public timelines without an API application.
Code Examples
Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("cryptosignals/twitter-scraper").call(run_input={"usernames": ["nasa", "github", "apify"],"maxResults": 25,"includeQuotedTweets": True,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item["type"] == "profile":print(f'@{item["screen_name"]}: {item["followers"]:,} followers')else:print(f' {item["likes"]:>6} likes {item["tweet_text"][:70]}')
Node.js
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('cryptosignals/twitter-scraper').call({startUrls: [{ url: 'https://x.com/nasa' }, { url: 'https://x.com/github' }],maxResults: 25,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.table(items.filter(i => i.type === 'tweet').map(({ user_handle, likes, tweet_text }) => ({ user_handle, likes, tweet_text })));
cURL
curl -X POST "https://api.apify.com/v2/acts/cryptosignals~twitter-scraper/runs?token=<TOKEN>" \-H 'Content-Type: application/json' \-d '{"usernames":["nasa","github"],"maxResults":20}'
Pricing
Pay-per-event: $0.005 per result. A result is one row in the dataset — one profile, one tweet, or one quoted tweet.
You are charged before a row is delivered, never after. If you set maxTotalChargeUsd on a run, the Actor stops as soon as your cap is reached and tells you how many rows it delivered. You never receive rows you were not charged for, and you are never charged for rows you did not receive. Failed lookups (deleted tweets, suspended accounts, anti-bot pages) cost nothing.
| Scenario | Results | Cost |
|---|---|---|
| One account, 10 tweets | 11 | $0.055 |
10 accounts, profiles only (maxResults: 0) | 10 | $0.05 |
| 5 competitors × 100 tweets, daily | 505/day | ~$2.53/day |
| Re-hydrating 200 tweet IDs | 200 | $1.00 |
Scheduling & Automation
Use Schedule in the Apify Console for recurring runs, and webhooks to push results downstream (Zapier, Make, your own endpoint, or another Actor). Results export as JSON, CSV, XLSX, XML or RSS.
How It Works
The Actor reads X's public, unauthenticated endpoints using a guest token — the same data any logged-out visitor sees. It carries two generations of X's internal query identifiers and falls through to the second if the first is retired, and it parses both of X's user-object shapes, so a platform-side rotation degrades it rather than breaking it. A public mirror is used as a last-resort fallback for timelines.
A run that retrieves nothing exits with a failed status and a message explaining why. It never finishes green and empty, and it never writes a placeholder row.
Limitations
⚠️ Read this before buying. These are platform restrictions of X's unauthenticated endpoints, not bugs, and no amount of retrying works around them.
- No keyword or hashtag search. X's
SearchTimelineendpoint returns HTTP 404 without a logged-in session, and every public Nitter mirror now answers with an anti-bot challenge. Passingaction: "search"fails the run immediately with an explanation rather than returning nothing. Verified 2026-08-05. - No followers/following lists, no reply timelines, no media tab, no full conversation threads. All of these endpoints are auth-walled for anonymous clients (HTTP 404), verified against X's current query IDs on 2026-08-05.
- Small accounts may return 0 tweets. X's unauthenticated timeline endpoint does not serve timelines for very small accounts (roughly under ~10k followers). The profile row is still returned. Accounts at 10k+ followers were verified working.
- Public data only — no protected accounts, no DMs.
- Max 10,000 tweets per account per run, paginated automatically.
- Rate limits — X throttles public endpoints; very large jobs take longer.
📡 More social monitoring actors by this team
| Platform | Actor |
|---|---|
| Instagram Profile Scraper | |
| Bluesky | Bluesky Scraper |
| Reddit Scraper (Fast) | |
| YouTube | YouTube Scraper |
| Pinterest Scraper |
FAQ
Do I need an X API key or developer account? No. No key, no login, no OAuth.
Can I scrape by keyword or hashtag? No — see Limitations. This used to be advertised and no longer works; the Actor now fails loudly instead of returning an empty dataset.
How do I scrape multiple accounts?
usernames: ["a", "b", "c"] or startUrls. One run, one dataset.
Why am I getting 0 tweets for a username? Almost always account size — X does not serve timelines for very small accounts anonymously. You still get the profile row.
Is this legal? It reads publicly available data with no authentication and no circumvention of access controls. You are responsible for complying with X's terms and with data-protection law in your jurisdiction.
What happens when X changes their endpoints? The Actor carries two query-ID generations and two parser shapes, so a rotation usually degrades one path rather than breaking the run. When something does break, it fails loudly instead of billing you for empty rows.
What is pay-per-event pricing? You pay per row delivered, charged before delivery. No monthly fee, no compute-unit maths.