Best Web Scraping APIs for Twitter/X: July 2026 Benchmark
Benchmarks run by the Scrapeway team ยท
Last updated: August 14, 2026 ยท
How we benchmark
Scrapfly is the best web scraping API for Twitter/X, with a
98% success rate across 8 web scraping APIs
benchmarked against live Twitter/X pages in July 2026.
3 of the 8 cleared Twitter/X reliably enough to recommend.
Twitter/X gates almost all of its data behind login and a GraphQL API with aggressive rate limiting rather than a
single named antibot, so most scraping APIs either fail it or pay for it in speed and cost. The benchmark is
refreshed twice a month, with no affiliate links and no sponsors.
Ranked by live Twitter/X success rate, best first:
Ranking history: web scraping APIs for Twitter/X over time
Twitter/X target ranking history
The 3 web scraping APIs for Twitter/X, reviewed
1. Scrapfly: 98% success on Twitter/X
On Twitter/X
Speed
Cost/1k
Overall
From
98%
7.9s
$0.89
#1 of 3
$30/mo
Twitter/X serves its data through a GraphQL API that requires guest or authenticated tokens, gates most pages
behind a login, and rate limits aggressively. Scrapfly cleared 98%
here by generating a genuine browser fingerprint and handling the token flow, so it reaches public post and
profile data instead of a login gate. Because it only bills for successful requests, the login gate and rate
limit responses X returns don't quietly run up the cost.
At $0.89 per 1,000 successful requests and
7.9s average response time, the figures are strong for a site that gates and
throttles this hard. The 98% success rate is the headline, and the ranking history
above shows how that rate has held across previous runs.
Pros:
Highest success rate in the Twitter/X benchmark this run, handling X's token flow and rate limits on public
pages
Only charges for successful scrapes, so login gate and rate limit pages cost nothing
One asp flag plus residential proxies handles Twitter/X with little tuning
First class SDKs for Python, TypeScript, Go, and Rust, plus a Scrapy extension
Cons:
Credit cost per request rises once ASP, JavaScript rendering, or residential proxies are enabled
The entry (Discovery) plan caps concurrency at 5, so large timeline crawls need a higher tier
The free tier is a single batch of 1,000 credits, enough to prototype but not to benchmark at volume
2. WebScrapingAPI: 93% success on Twitter/X
On Twitter/X
Speed
Cost/1k
Overall
From
93%
14.8s
$2.71
#2 of 3
$19/mo
WebScrapingAPI covers a broad range of language SDKs behind a simple REST interface, so it drops into most
stacks without a client library, and it supports async and batch submission for queued jobs. The scorecard
carries this run's speed and cost. On Twitter/X it cleared 93% this run.
Pros:
Broad language SDK range behind a simple REST interface
Async and batch submission for queued jobs
Cons:
X's login gate and rate limit responses make it easy to keep paying unless you check content
Support tickets often go unanswered for days, per user reviews
3. Scrapingant: 90% success on Twitter/X
On Twitter/X
Speed
Cost/1k
Overall
From
90%
13.8s
$1.90
#3 of 3
$19/mo
Scrapingant bundles JavaScript rendering and session support at a low entry price, with a smaller feature
surface than the larger providers. On Twitter/X it cleared 90% this run.
Pros:
Low sticker price
JavaScript rendering included
Cons:
Smaller feature surface and fewer integration options than the larger providers
Billing covers requests that return block or challenge pages, so failures still cost credits
Small provider with a thin public track record
COMPARISON
About scraping Twitter/X
Twitter/X is a large public conversation platform, and the data people scrape from it is mostly post detail. That
means tweet text, timestamps, like, repost and reply counts, author handles and display names, follower and
following counts, and public profile bios. The subset reachable without an account has narrowed over time, so
public scraping focuses on individual public posts and profiles.
Twitter/X is a JavaScript heavy app that serves data through an internal GraphQL API, and that API expects guest
or authenticated tokens obtained through a specific request flow rather than a single page load. Most timeline and
search data sits behind a login and aggressive rate limiting, so the main obstacles are managing the token flow
and staying under rate limits rather than solving one challenge script.
AUTOMATION
Twitter/X does not map to a single named antibot in this benchmark. Access is controlled by login gating, the
GraphQL token flow, and per account and per IP rate limits. The practical detail for scraping Twitter/X is that
login gate and rate limit responses can return without a hard error status, so success has to be measured on
response content, not status codes.
twitter_scraper.py
importjsonfromparselimportSelector# install using `pip install scrapfly-sdk`fromscrapflyimportScrapflyClient,ScrapeConfig,ScrapeApiResponse# create an API client instanceclient=ScrapflyClient(key="YOUR API KEY")# create scrape function that returns HTML parser for a given URLdefscrape(url:str,country:str="",render_js=False,headers:dict=None)->Selector:api_result=client.scrape(ScrapeConfig(url=url,headers=headers,asp=True,render_js=render_jsorFalse,cache=False,cache_ttl=900,country=countryor'us',rendering_stage='domcontentloaded',method='GET',))returnapi_result.selectorurl="https://twitter.com/XCreators/status/1770093017506189440"selector=scrape(url,render_js=True,country="US")# Twitter can be parsed using css selectors and data-testid attributesviews,reposts,quotes,likes,bookmarks,*_=selector.css('[data-testid=app-text-transition-container] span::text').getall()data={"tweet":selector.css("[data-testid=tweetText] ::text").get(),"views":views,"reposts":reposts,"quotes":quotes,"likes":likes,"bookmarks":bookmarks,}frompprintimportpprintpprint(data)
Output$ python twitter_scraper.py
{'bookmarks': '44',
'likes': '725',
'quotes': '24',
'reposts': '127',
'tweet': 'X is the platform for content creators to freely express their '
'artistic and diverse perspectives without the constraints of '
'censorship. Since the introduction of our ad revenue share program, '
'X has paid out an impressive sum of more than $45 million to more '
'than 150,000 creators.',
'views': '530.8K'}
import json
from parsel import Selector
# install using `pip install scrapfly-sdk`
from scrapfly import ScrapflyClient, ScrapeConfig, ScrapeApiResponse
# create an API client instance
client = ScrapflyClient(key="YOUR API KEY")
# create scrape function that returns HTML parser for a given URL
def scrape(url: str, country: str="", render_js=False, headers: dict=None) -> Selector:
api_result = client.scrape(ScrapeConfig(
url=url,
headers=headers,
asp=True,
render_js=render_js or False,
cache=False,
cache_ttl=900,
country=country or 'us',
rendering_stage='domcontentloaded',
method='GET',
))
return api_result.selector
url = "https://twitter.com/XCreators/status/1770093017506189440"
selector = scrape(url, render_js=True, country="US")
# Twitter can be parsed using css selectors and data-testid attributes
views, reposts, quotes, likes, bookmarks, *_ = selector.css('[data-testid=app-text-transition-container] span::text').getall()
data = {
"tweet": selector.css("[data-testid=tweetText] ::text").get(),
"views": views,
"reposts": reposts,
"quotes": quotes,
"likes": likes,
"bookmarks": bookmarks,
}
from pprint import pprint
pprint(data)
importjsonfromparselimportSelector# webscrapingapi has a Python SDK but it's not great, use httpx instead:# `pip install httpx`importhttpx# create an API client instanceclient=httpx.Client(timeout=180)# create scrape function that returns HTML parser for a given URLdefscrape(url:str,country:str="",render_js=False,headers:dict=None)->Selector:api_result=client.get("https://api.webscrapingapi.com/v2",# the target url is passed as a parameter belowheaders=headers,params={"url":url,"api_key":"YOUR API KEY",# NOTE: add your API KEY here!"timeout":60_000,"render_js":render_jsorFalse,"method":'GET',},)assertapi_result.status_code==200,api_result.reason_phrasereturnSelector(api_result.text)url="https://twitter.com/XCreators/status/1770093017506189440"selector=scrape(url,render_js=True,country="US")# Twitter can be parsed using css selectors and data-testid attributesviews,reposts,quotes,likes,bookmarks,*_=selector.css('[data-testid=app-text-transition-container] span::text').getall()data={"tweet":selector.css("[data-testid=tweetText] ::text").get(),"views":views,"reposts":reposts,"quotes":quotes,"likes":likes,"bookmarks":bookmarks,}frompprintimportpprintpprint(data)
Output$ python twitter_scraper.py
{'bookmarks': '44',
'likes': '725',
'quotes': '24',
'reposts': '127',
'tweet': 'X is the platform for content creators to freely express their '
'artistic and diverse perspectives without the constraints of '
'censorship. Since the introduction of our ad revenue share program, '
'X has paid out an impressive sum of more than $45 million to more '
'than 150,000 creators.',
'views': '530.8K'}
import json
from parsel import Selector
# webscrapingapi has a Python SDK but it's not great, use httpx instead:
# `pip install httpx`
import httpx
# create an API client instance
client = httpx.Client(timeout=180)
# create scrape function that returns HTML parser for a given URL
def scrape(url: str, country: str="", render_js=False, headers: dict=None) -> Selector:
api_result = client.get(
"https://api.webscrapingapi.com/v2", # the target url is passed as a parameter below
headers=headers,
params={
"url": url,
"api_key": "YOUR API KEY", # NOTE: add your API KEY here!
"timeout": 60_000,
"render_js": render_js or False,
"method": 'GET',
},
)
assert api_result.status_code == 200, api_result.reason_phrase
return Selector(api_result.text)
url = "https://twitter.com/XCreators/status/1770093017506189440"
selector = scrape(url, render_js=True, country="US")
# Twitter can be parsed using css selectors and data-testid attributes
views, reposts, quotes, likes, bookmarks, *_ = selector.css('[data-testid=app-text-transition-container] span::text').getall()
data = {
"tweet": selector.css("[data-testid=tweetText] ::text").get(),
"views": views,
"reposts": reposts,
"quotes": quotes,
"likes": likes,
"bookmarks": bookmarks,
}
from pprint import pprint
pprint(data)
importjsonfromparselimportSelector# install using `pip install scrapingant-client`fromscrapingant_clientimportScrapingAntClient# create an API client instanceclient=ScrapingAntClient(token="YOUR API KEY")# create scrape function that returns HTML parser for a given URLdefscrape(url:str,country:str="",render_js=False,headers:dict=None)->Selector:api_result=client.general_request(url,json=True,method='GET',)# the scrapingant client returns its own Response object: content holds the pageassertapi_result.status_code==200,api_result.textreturnSelector(api_result.content)url="https://twitter.com/XCreators/status/1770093017506189440"selector=scrape(url,render_js=True,country="US")# Twitter can be parsed using css selectors and data-testid attributesviews,reposts,quotes,likes,bookmarks,*_=selector.css('[data-testid=app-text-transition-container] span::text').getall()data={"tweet":selector.css("[data-testid=tweetText] ::text").get(),"views":views,"reposts":reposts,"quotes":quotes,"likes":likes,"bookmarks":bookmarks,}frompprintimportpprintpprint(data)
Output$ python twitter_scraper.py
{'bookmarks': '44',
'likes': '725',
'quotes': '24',
'reposts': '127',
'tweet': 'X is the platform for content creators to freely express their '
'artistic and diverse perspectives without the constraints of '
'censorship. Since the introduction of our ad revenue share program, '
'X has paid out an impressive sum of more than $45 million to more '
'than 150,000 creators.',
'views': '530.8K'}
import json
from parsel import Selector
# install using `pip install scrapingant-client`
from scrapingant_client import ScrapingAntClient
# create an API client instance
client = ScrapingAntClient(token="YOUR API KEY")
# create scrape function that returns HTML parser for a given URL
def scrape(url: str, country: str="", render_js=False, headers: dict=None) -> Selector:
api_result = client.general_request(
url,
json=True,
method='GET',
)
# the scrapingant client returns its own Response object: content holds the page
assert api_result.status_code == 200, api_result.text
return Selector(api_result.content)
url = "https://twitter.com/XCreators/status/1770093017506189440"
selector = scrape(url, render_js=True, country="US")
# Twitter can be parsed using css selectors and data-testid attributes
views, reposts, quotes, likes, bookmarks, *_ = selector.css('[data-testid=app-text-transition-container] span::text').getall()
data = {
"tweet": selector.css("[data-testid=tweetText] ::text").get(),
"views": views,
"reposts": reposts,
"quotes": quotes,
"likes": likes,
"bookmarks": bookmarks,
}
from pprint import pprint
pprint(data)
importjsonfromparselimportSelector# install using `pip install scrapingbee`fromscrapingbeeimportScrapingBeeClient# create an API client instanceclient=ScrapingBeeClient(api_key="YOUR API KEY")# create scrape function that returns HTML parser for a given URLdefscrape(url:str,country:str="",render_js=False,headers:dict=None)->Selector:api_result=client.get(url,headers=headers,params={"json_response":True,"transparent_status_code":True,})assertapi_result.ok,api_result.textdata=api_result.json()returnSelector(data['body'])url="https://twitter.com/XCreators/status/1770093017506189440"selector=scrape(url,render_js=True,country="US")# Twitter can be parsed using css selectors and data-testid attributesviews,reposts,quotes,likes,bookmarks,*_=selector.css('[data-testid=app-text-transition-container] span::text').getall()data={"tweet":selector.css("[data-testid=tweetText] ::text").get(),"views":views,"reposts":reposts,"quotes":quotes,"likes":likes,"bookmarks":bookmarks,}frompprintimportpprintpprint(data)
Output$ python twitter_scraper.py
{'bookmarks': '44',
'likes': '725',
'quotes': '24',
'reposts': '127',
'tweet': 'X is the platform for content creators to freely express their '
'artistic and diverse perspectives without the constraints of '
'censorship. Since the introduction of our ad revenue share program, '
'X has paid out an impressive sum of more than $45 million to more '
'than 150,000 creators.',
'views': '530.8K'}
import json
from parsel import Selector
# install using `pip install scrapingbee`
from scrapingbee import ScrapingBeeClient
# create an API client instance
client = ScrapingBeeClient(api_key="YOUR API KEY")
# create scrape function that returns HTML parser for a given URL
def scrape(url: str, country: str="", render_js=False, headers: dict=None) -> Selector:
api_result = client.get(
url,
headers=headers,
params={
"json_response": True,
"transparent_status_code": True,
}
)
assert api_result.ok, api_result.text
data = api_result.json()
return Selector(data['body'])
url = "https://twitter.com/XCreators/status/1770093017506189440"
selector = scrape(url, render_js=True, country="US")
# Twitter can be parsed using css selectors and data-testid attributes
views, reposts, quotes, likes, bookmarks, *_ = selector.css('[data-testid=app-text-transition-container] span::text').getall()
data = {
"tweet": selector.css("[data-testid=tweetText] ::text").get(),
"views": views,
"reposts": reposts,
"quotes": quotes,
"likes": likes,
"bookmarks": bookmarks,
}
from pprint import pprint
pprint(data)
importjsonfromparselimportSelector# install using `pip install zenrows`fromzenrowsimportZenRowsClient# create an API client instanceclient=ZenRowsClient(apikey="YOUR API KEY")# create scrape function that returns HTML parser for a given URLdefscrape(url:str,country:str="",render_js=False,headers:dict=None)->Selector:api_result=client.get(url,headers=headers,params={})# zenrows answers with the page HTML; "json_response" is only accepted with js_render onassertapi_result.ok,api_result.textreturnSelector(api_result.text)url="https://twitter.com/XCreators/status/1770093017506189440"selector=scrape(url,render_js=True,country="US")# Twitter can be parsed using css selectors and data-testid attributesviews,reposts,quotes,likes,bookmarks,*_=selector.css('[data-testid=app-text-transition-container] span::text').getall()data={"tweet":selector.css("[data-testid=tweetText] ::text").get(),"views":views,"reposts":reposts,"quotes":quotes,"likes":likes,"bookmarks":bookmarks,}frompprintimportpprintpprint(data)
Output$ python twitter_scraper.py
{'bookmarks': '44',
'likes': '725',
'quotes': '24',
'reposts': '127',
'tweet': 'X is the platform for content creators to freely express their '
'artistic and diverse perspectives without the constraints of '
'censorship. Since the introduction of our ad revenue share program, '
'X has paid out an impressive sum of more than $45 million to more '
'than 150,000 creators.',
'views': '530.8K'}
import json
from parsel import Selector
# install using `pip install zenrows`
from zenrows import ZenRowsClient
# create an API client instance
client = ZenRowsClient(apikey="YOUR API KEY")
# create scrape function that returns HTML parser for a given URL
def scrape(url: str, country: str="", render_js=False, headers: dict=None) -> Selector:
api_result = client.get(
url,
headers=headers,
params={
}
)
# zenrows answers with the page HTML; "json_response" is only accepted with js_render on
assert api_result.ok, api_result.text
return Selector(api_result.text)
url = "https://twitter.com/XCreators/status/1770093017506189440"
selector = scrape(url, render_js=True, country="US")
# Twitter can be parsed using css selectors and data-testid attributes
views, reposts, quotes, likes, bookmarks, *_ = selector.css('[data-testid=app-text-transition-container] span::text').getall()
data = {
"tweet": selector.css("[data-testid=tweetText] ::text").get(),
"views": views,
"reposts": reposts,
"quotes": quotes,
"likes": likes,
"bookmarks": bookmarks,
}
from pprint import pprint
pprint(data)
importjsonfromparselimportSelector# install using `pip install scraperapi-sdk`fromscraperapi_sdkimportScraperAPIClient# create an API client instanceclient=ScraperAPIClient(api_key="YOUR API KEY")# create scrape function that returns HTML parser for a given URLdefscrape(url:str,country:str="",render_js=False,headers:dict=None)->Selector:api_result=client.make_request(url=url,headers=headers,params={},)assertapi_result.ok,api_result.textreturnSelector(api_result.text)url="https://twitter.com/XCreators/status/1770093017506189440"selector=scrape(url,render_js=True,country="US")# Twitter can be parsed using css selectors and data-testid attributesviews,reposts,quotes,likes,bookmarks,*_=selector.css('[data-testid=app-text-transition-container] span::text').getall()data={"tweet":selector.css("[data-testid=tweetText] ::text").get(),"views":views,"reposts":reposts,"quotes":quotes,"likes":likes,"bookmarks":bookmarks,}frompprintimportpprintpprint(data)
Output$ python twitter_scraper.py
{'bookmarks': '44',
'likes': '725',
'quotes': '24',
'reposts': '127',
'tweet': 'X is the platform for content creators to freely express their '
'artistic and diverse perspectives without the constraints of '
'censorship. Since the introduction of our ad revenue share program, '
'X has paid out an impressive sum of more than $45 million to more '
'than 150,000 creators.',
'views': '530.8K'}
import json
from parsel import Selector
# install using `pip install scraperapi-sdk`
from scraperapi_sdk import ScraperAPIClient
# create an API client instance
client = ScraperAPIClient(api_key="YOUR API KEY")
# create scrape function that returns HTML parser for a given URL
def scrape(url: str, country: str="", render_js=False, headers: dict=None) -> Selector:
api_result = client.make_request(
url=url,
headers=headers,
params={
},
)
assert api_result.ok, api_result.text
return Selector(api_result.text)
url = "https://twitter.com/XCreators/status/1770093017506189440"
selector = scrape(url, render_js=True, country="US")
# Twitter can be parsed using css selectors and data-testid attributes
views, reposts, quotes, likes, bookmarks, *_ = selector.css('[data-testid=app-text-transition-container] span::text').getall()
data = {
"tweet": selector.css("[data-testid=tweetText] ::text").get(),
"views": views,
"reposts": reposts,
"quotes": quotes,
"likes": likes,
"bookmarks": bookmarks,
}
from pprint import pprint
pprint(data)
importjsonfromparselimportSelector# install using `pip install firecrawl-py`fromfirecrawlimportFirecrawl# create an API client instanceclient=Firecrawl(api_key="YOUR API KEY")# create scrape function that returns HTML parser for a given URLdefscrape(url:str,country:str="",render_js=False,headers:dict=None)->Selector:api_result=client.scrape(url=url,headers=headersor{},)assertapi_result.raw_html,"firecrawl returned no html for this page"returnSelector(api_result.raw_html)url="https://twitter.com/XCreators/status/1770093017506189440"selector=scrape(url,render_js=True,country="US")# Twitter can be parsed using css selectors and data-testid attributesviews,reposts,quotes,likes,bookmarks,*_=selector.css('[data-testid=app-text-transition-container] span::text').getall()data={"tweet":selector.css("[data-testid=tweetText] ::text").get(),"views":views,"reposts":reposts,"quotes":quotes,"likes":likes,"bookmarks":bookmarks,}frompprintimportpprintpprint(data)
Output$ python twitter_scraper.py
{'bookmarks': '44',
'likes': '725',
'quotes': '24',
'reposts': '127',
'tweet': 'X is the platform for content creators to freely express their '
'artistic and diverse perspectives without the constraints of '
'censorship. Since the introduction of our ad revenue share program, '
'X has paid out an impressive sum of more than $45 million to more '
'than 150,000 creators.',
'views': '530.8K'}
import json
from parsel import Selector
# install using `pip install firecrawl-py`
from firecrawl import Firecrawl
# create an API client instance
client = Firecrawl(api_key="YOUR API KEY")
# create scrape function that returns HTML parser for a given URL
def scrape(url: str, country: str="", render_js=False, headers: dict=None) -> Selector:
api_result = client.scrape(
url=url,
headers=headers or {},
)
assert api_result.raw_html, "firecrawl returned no html for this page"
return Selector(api_result.raw_html)
url = "https://twitter.com/XCreators/status/1770093017506189440"
selector = scrape(url, render_js=True, country="US")
# Twitter can be parsed using css selectors and data-testid attributes
views, reposts, quotes, likes, bookmarks, *_ = selector.css('[data-testid=app-text-transition-container] span::text').getall()
data = {
"tweet": selector.css("[data-testid=tweetText] ::text").get(),
"views": views,
"reposts": reposts,
"quotes": quotes,
"likes": likes,
"bookmarks": bookmarks,
}
from pprint import pprint
pprint(data)
importjsonfromparselimportSelector# scrapingdog has no integration but we can use httpx# install using `pip install httpx`importhttpx# create an API client instanceclient=httpx.Client(timeout=180)# create scrape function that returns HTML parser for a given URLdefscrape(url:str,country:str="",render_js=False,headers:dict=None)->Selector:params={"api_key":"YOUR API KEY","url":url,}api_result=client.get('https://api.scrapingdog.com/scrape',params=params,)assertapi_result.status_code==200,api_result.textreturnSelector(api_result.text,type="html")url="https://twitter.com/XCreators/status/1770093017506189440"selector=scrape(url,render_js=True,country="US")# Twitter can be parsed using css selectors and data-testid attributesviews,reposts,quotes,likes,bookmarks,*_=selector.css('[data-testid=app-text-transition-container] span::text').getall()data={"tweet":selector.css("[data-testid=tweetText] ::text").get(),"views":views,"reposts":reposts,"quotes":quotes,"likes":likes,"bookmarks":bookmarks,}frompprintimportpprintpprint(data)
Output$ python twitter_scraper.py
{'bookmarks': '44',
'likes': '725',
'quotes': '24',
'reposts': '127',
'tweet': 'X is the platform for content creators to freely express their '
'artistic and diverse perspectives without the constraints of '
'censorship. Since the introduction of our ad revenue share program, '
'X has paid out an impressive sum of more than $45 million to more '
'than 150,000 creators.',
'views': '530.8K'}
import json
from parsel import Selector
# scrapingdog has no integration but we can use httpx
# install using `pip install httpx`
import httpx
# create an API client instance
client = httpx.Client(timeout=180)
# create scrape function that returns HTML parser for a given URL
def scrape(url: str, country: str="", render_js=False, headers: dict=None) -> Selector:
params = {
"api_key": "YOUR API KEY",
"url": url,
}
api_result = client.get(
'https://api.scrapingdog.com/scrape',
params=params,
)
assert api_result.status_code == 200, api_result.text
return Selector(api_result.text, type="html")
url = "https://twitter.com/XCreators/status/1770093017506189440"
selector = scrape(url, render_js=True, country="US")
# Twitter can be parsed using css selectors and data-testid attributes
views, reposts, quotes, likes, bookmarks, *_ = selector.css('[data-testid=app-text-transition-container] span::text').getall()
data = {
"tweet": selector.css("[data-testid=tweetText] ::text").get(),
"views": views,
"reposts": reposts,
"quotes": quotes,
"likes": likes,
"bookmarks": bookmarks,
}
from pprint import pprint
pprint(data)
How to choose a web scraping API for Twitter/X
Because Twitter/X gates data behind login, a token flow, and rate limits rather than one challenge, the deciding
factor is which providers manage the token flow and hold their success rate under throttling. Start by narrowing
to the providers still clearing it this run (the top of the ranked table), then choose within that set based on
your job. Low volume public post lookups give you more room on cost. Large profile or timeline crawls put more
weight on reliability and concurrency.
Reliability first.Scrapfly leads the current Twitter/X ranking, which makes it the
default starting point for production public post and profile crawls. The ranking history shows its track
record.
Value. Among the APIs still clearing Twitter/X, sort by cost per successful request. The
cheapest sticker price is rarely the cheapest per usable Twitter/X page.
Speed. For latency sensitive post lookups rather than bulk crawls, pick the fastest option that
still clears Twitter/X reliably.
The key principle is to judge on cost per successful request, not sticker price. X returns login gate and rate
limit responses that can look like a normal response, so a cheap API can burn through requests without returning
usable data, which makes its real cost per usable result far higher than the rate card suggests.
PRICING
How we benchmark web scraping APIs for Twitter/X
We independently benchmark 8 web scraping APIs against live Twitter/X pages, 1,000+ requests per
service, twice a month. Every API is tested against the same public Twitter/X URLs at the same time, and cost is
measured per 1,000 successful requests on entry plan pricing. We pay for the plans ourselves. No affiliate links.
No sponsors. Just data.
8 APIs ยท 1,000+ requests each ยท twice a month ยท no affiliate links, no sponsors
Benchmarking Twitter/X has one wrinkle worth knowing about. Success is measured on response content, not HTTP
status codes. X's login gate and rate limit responses can return without a hard error status, so a test that only
checked the status code would overstate results. We verify that responses contain the expected public post or
profile data before counting them as successful. Every provider is tested against the same URLs in the same run,
so the numbers stay comparable.
Latest data: Jul 31 to Aug 14, 2026.
QUALITY TESTING
Frequently asked questions about scraping Twitter/X
Is it legal to scrape Twitter/X?
Scraping publicly available Twitter/X data is sensitive because posts and profiles can contain personal data that
falls under privacy laws such as GDPR and CCPA, and X's Terms of Service prohibit automated access. Legality
depends on what you collect, where you operate, and how you use the data, so treat this as general information
rather than legal advice and check your own situation before scraping Twitter/X.
What's the cheapest API that works on Twitter/X?
Sort the ranked table by cost per successful request and read down to the first provider still clearing Twitter/X
this run. That's the cheapest option that actually delivers. Lower priced APIs further down often fail too many
requests for their sticker price to be meaningful, and X's login gating hides those failures unless you check
response content.
Do I need a headless browser to scrape Twitter/X?
Not always. The post and profile data itself comes from the GraphQL API rather than page text, so it can be read
directly once you have a valid token. A genuine browser context helps manage the token flow and pass X's rate
limiting reliably. The APIs at the top of the ranking handle both for you.
Why do some APIs score low on Twitter/X?
Because X gates its GraphQL API behind guest or authenticated tokens and rate limits aggressively. Without valid
tokens, requests come back as login gate or rate limit responses, and since we score on response content, those
count as failures rather than successes.
How often is this benchmark updated?
Twice a month against the same live public Twitter/X targets, 1,000+ requests per API each run. We publish after
validating the run and checking failures for configuration or detection errors.
Conclusion
Twitter/X gates most data behind login, a GraphQL token flow, and rate limits rather than one named challenge, so
the right web scraping API is one that manages the token flow and sustains its success rate under throttling. For
production public post and profile crawls, start with Scrapfly, the highest success rate in the current
run. For cost or speed on lighter lookups, choose among the providers still clearing Twitter/X this run.
Whatever you pick, verify results on response content rather than status codes, because X's login gate and rate
limit responses don't always carry a hard error status. The benchmark refreshes twice a month, so check the live
Twitter/X results before committing.