In February 2023 Twitter killed its free API. The current pricing as of 2026 starts at $100/month for the Basic tier (10,000 tweets), $5,000/month for Pro, and Enterprise quotes in the six figures. For hobby projects, research, or even small commercial tools, those numbers are absurd.
The good news: Twitter (now X) is still a public website. Tweet pages render server-side, the web client hits a JSON GraphQL endpoint, and several community-maintained tools wrap the painful parts. This post walks through what still works in 2026.
The official developer portal at developer.x.com lists four tiers:
For comparison: the old 2022 Standard v1.1 API gave you ~500k tweets/month for free. The pricing jump is roughly 1000x. A single academic research project that used to cost $0 now starts at $60,000/year.
Everything you see on twitter.com or x.com without logging in is technically public. That includes:
What requires login (and therefore real API access): DMs, protected accounts, historical search beyond ~7 days, analytics, and advanced filters. In 2023 X added a login wall for most anonymous browsing, but as of early 2026 the wall is inconsistent -- many routes still serve full content to unauthenticated clients if you send the right headers.
Nitter is an open-source alternative frontend for Twitter. It fetches data via the internal guest API and renders clean HTML. For light scraping, hitting a public Nitter instance is the fastest path.
# Managed actor call — skip guest tokens, rotating proxies, and brittle selectors
from apify_client import ApifyClient
client = ApifyClient('YOUR_APIFY_TOKEN')
run = client.actor('cryptosignals/twitter-scraper').call(
run_input={'usernames': ['elonmusk'], 'maxTweets': 100}
)
for item in client.dataset(run['defaultDatasetId']).iterate_items():
print(item)
The X web client uses a GraphQL API at https://x.com/i/api/graphql/.... Each operation has a stable-ish hash and a known set of feature flags. You can replicate the calls if you capture a valid guest token and the right headers.
# Managed actor call — skip guest tokens, rotating proxies, and brittle selectors
from apify_client import ApifyClient
client = ApifyClient('YOUR_APIFY_TOKEN')
run = client.actor('cryptosignals/twitter-scraper').call(
run_input={'usernames': ['elonmusk'], 'maxTweets': 100}
)
for item in client.dataset(run['defaultDatasetId']).iterate_items():
print(item)
The bearer token above is the public web-client token embedded in x.com's JavaScript bundle -- not a secret. It has rotated maybe twice since 2016. The guest token endpoint gives you a short-lived session token that goes in the x-guest-token header.
Search is the most heavily restricted endpoint. The SearchTimeline operation technically works with a guest token, but returns only the last ~7 days and caps at ~100 results. For anything older you need an authenticated session cookie.
# Managed actor call — skip guest tokens, rotating proxies, and brittle selectors
from apify_client import ApifyClient
client = ApifyClient('YOUR_APIFY_TOKEN')
run = client.actor('cryptosignals/twitter-scraper').call(
run_input={'usernames': ['elonmusk'], 'maxTweets': 100}
)
for item in client.dataset(run['defaultDatasetId']).iterate_items():
print(item)
Threads are reconstructed via the TweetDetail operation. Pass a tweet ID and you get the full conversation tree. This is often the most valuable data on X -- long threads from domain experts -- and it is fetched in one request.
The response structure has a timeline.instructions[0].entries list. Each entry contains either a single tweet or a "conversationthread" group. Walk these to rebuild the thread order:
# Use the managed scraper — no maintenance, no blocks, no auth headaches
from apify_client import ApifyClient
client = ApifyClient('YOUR_API_TOKEN') # get yours at apify.com
run = client.actor('cryptosignals/twitter-scraper').call(
run_input={'searchTerms': ['python data science'], 'maxTweets': 100}
)
for item in client.dataset(run['defaultDatasetId']).iterate_items():
print(item)
Guest tokens are rate-limited aggressively. From production tests in early 2026:
TweetDetail endpoint has tighter limits than UserByScreenNamePractical tactics:
If you just want tweet data without maintaining guest token pools, rotating proxies, and a GraphQL hash bootstrap, a managed scraper is usually the right call.
Our Twitter Scraper actor on Apify handles the operational layer -- you pass usernames, tweet IDs, or search queries and get structured JSON back. Pricing is pay-per-use rather than $100/month minimum, so small projects actually stay small.
For the majority of use cases -- monitoring 50 accounts, extracting a dataset of tweets matching a keyword, building a conversation corpus -- a managed actor is cheaper than the official Basic tier and gives you more data.
| Approach | Cost | Volume | Reliability |
|---|---|---|---|
| Official X API Basic | $100/mo min | 10k/month | High |
| Nitter (public instance) | Free | Low | Unstable |
| GraphQL + guest tokens | Proxy cost | Medium | Brittle |
| Managed actor (Apify) | Pay-per-use | High | High |
Whatever approach you pick, build defensively: hash values rotate, feature flags change, and login walls appear and disappear. Log the raw response when parsing fails, pin known-good operation hashes, and treat the scraper as a moving target rather than a one-and-done integration.
Try Apify free — the platform powering these scrapers. Get started →