Best Web Scraping APIs for Instagram: October 2026 Benchmark
Benchmarks run by the Scrapeway team ยท
Last updated: November 01, 2026 ยท
How we benchmark
Scrapingdog is the best web scraping API for Instagram, with a
96% success rate across 6 web scraping APIs
benchmarked against live Instagram pages in October 2026.
3 of them succeed, and 1 cleared Instagram reliably enough to recommend.
Instagram runs Meta's own bot detection rather than a third-party anti-bot, and gates a lot of
data behind a login wall, so most scraping APIs either fail it or pay for it in speed and cost. The benchmark is
refreshed once a month, with no affiliate links and no sponsors.
Ranked by live Instagram success rate, best first:
Ranking history: web scraping APIs for Instagram over time
Instagram target ranking history
The 3 web scraping APIs for Instagram, reviewed
1. Scrapingdog: 96% success on Instagram
On Instagram
Speed
Cost/1k
Overall
From
96%
3.3s
$3.00
#1 of 3
$40/mo
Scrapingdog offers low entry pricing and a simple API, without first party SDKs. Instagram checks browser
signals on each request, and Instagram's login wall gates the data it doesn't serve publicly. On Instagram it
cleared 96% this run.
Pros:
Low entry pricing
Simple API
Cons:
No first party SDKs
Reviewers frequently cite slow, email only support with no live chat option
2. WebScrapingAPI: 79% success on Instagram
On Instagram
Speed
Cost/1k
Overall
From
79%
19.6s
$2.45
#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 Instagram it cleared 79% this run.
Pros:
Broad language SDK range behind a simple REST interface
Async and batch submission for queued jobs
Cons:
Instagram's block and login wall pages make it easy to keep paying unless you check content
Support tickets often go unanswered for days, per user reviews
3. Scrapfly: 75% success on Instagram
On Instagram
Speed
Cost/1k
Overall
From
75%
10.0s
$2.91
#3 of 3
$30/mo
Instagram puts Meta's own bot detection in front of its public profile and post pages, and the profile data is embedded as
JSON in the page rather than in the visible HTML. Scrapfly cleared
75% here by generating a genuine browser fingerprint and passing
Instagram's checks, so it reaches the public profile and post data instead of a challenge or login wall. Because
it only bills for successful requests, the block and login wall pages Instagram returns don't quietly run up
the cost.
At $2.91 per 1,000 successful requests and
10.0s average response time, the figures are strong for an API clearing Instagram
at this rate. The 75% success rate is the headline, and the ranking history above
shows how that rate has held across previous runs.
Pros:
75% success in the Instagram benchmark this run, #3 of 3, clearing Instagram's bot detection on public pages
Only charges for successful scrapes, so challenge and login wall pages cost nothing
One unblocker flag plus residential proxies handles Instagram with little tuning
First class SDKs for Python, TypeScript, Go, and Rust, plus a Scrapy extension
Cons:
Credit cost per request rises once the Unblocker, JavaScript rendering, or residential proxies are enabled
The entry (Discovery) plan caps concurrency at 5, so large profile 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
COMPARISON
About scraping Instagram
Instagram is one of the largest social platforms, and the data people scrape from it is mostly public profile
detail. That means usernames and display names, bios, follower and following counts, post counts, captions, like
and comment counts, hashtags, and media URLs from public profiles and posts. Most of this is reachable on public
profile pages and individual post pages without an account.
Instagram is a JavaScript heavy app. A public profile page embeds the profile's data as JSON in its own HTML (the
xig_user_by_username object) and renders the latest posts as links, so the reliable approach reads that
JSON instead of parsing the visible text. Instagram's backend profile API (web_profile_info) no longer
answers logged-out requests reliably, and loading more posts goes through its GraphQL API, which asks for a login.
A large amount of
data, including private accounts, follower lists, and full feeds, sits behind a login wall and is out of scope for
public scraping. The benchmark targets public profile and post data only.
HTTP ANALYSIS
Instagram doesn't sit behind a third-party anti-bot: Meta runs its own bot detection, so there is no anti-bot page
to compare it with.
The practical detail for scraping Instagram is that blocks often come back as a login wall redirect or an empty
response rather than a hard error, so success has to be measured on response content, not status codes.
instagram_scraper.py
fromparselimportSelector# scrapingdog has no integration but we can use httpx# install using `pip install httpx`importhttpx# create an API client instanceclient=httpx.Client(timeout=180)# Scrapingdog has a dedicated Instagram endpoint, the one the benchmark uses: it takes the username# and answers with the profile as JSON, so there is no page to parseusername="google"api_result=client.get("https://api.scrapingdog.com/instagram/profile",params={"api_key":"YOUR API KEY","username":username},)assertapi_result.status_code==200,api_result.textdata=api_result.json()profile={key:data[key]forkeyin["username","full_name","bio","followers_count","following_count","is_verified"]}# some examples of what can be found in the dataset:frompprintimportpprintpprint(profile)
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)
# Scrapingdog has a dedicated Instagram endpoint, the one the benchmark uses: it takes the username
# and answers with the profile as JSON, so there is no page to parse
username = "google"
api_result = client.get(
"https://api.scrapingdog.com/instagram/profile",
params={"api_key": "YOUR API KEY", "username": username},
)
assert api_result.status_code == 200, api_result.text
data = api_result.json()
profile = {key: data[key] for key in ["username", "full_name", "bio", "followers_count", "following_count", "is_verified"]}
# some examples of what can be found in the dataset:
from pprint import pprint
pprint(profile)
importjsonfromnested_lookupimportnested_lookup# pip install nested-lookupfromparselimportSelector# 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='1',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_js,"method":'GET',},)assertapi_result.status_code==200,api_result.reason_phrasereturnSelector(api_result.text)# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,# so load the public profile page, as the benchmark does, and read the data the page embeds as JSONusername="google"selector=scrape(f"https://www.instagram.com/{username}")datasets=selector.css('script[type="application/json"][data-sjs]::text').getall()dataset=next(dfordindatasetsif"xig_user_by_username"indand"follower_count"ind)user=nested_lookup("xig_user_by_username",json.loads(dataset))[0]# the latest posts are the links in the profile's grid, the ones the benchmark checks forposts=selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()profile={key:user[key]forkeyin["username","full_name","biography","follower_count","following_count","is_verified"]}profile["latest_posts"]=posts[:3]# some examples of what can be found in the dataset:frompprintimportpprintpprint(profile)
import json
from nested_lookup import nested_lookup # pip install nested-lookup
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='1', 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,
"method": 'GET',
},
)
assert api_result.status_code == 200, api_result.reason_phrase
return Selector(api_result.text)
# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,
# so load the public profile page, as the benchmark does, and read the data the page embeds as JSON
username = "google"
selector = scrape(f"https://www.instagram.com/{username}")
datasets = selector.css('script[type="application/json"][data-sjs]::text').getall()
dataset = next(d for d in datasets if "xig_user_by_username" in d and "follower_count" in d)
user = nested_lookup("xig_user_by_username", json.loads(dataset))[0]
# the latest posts are the links in the profile's grid, the ones the benchmark checks for
posts = selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()
profile = {key: user[key] for key in ["username", "full_name", "biography", "follower_count", "following_count", "is_verified"]}
profile["latest_posts"] = posts[:3]
# some examples of what can be found in the dataset:
from pprint import pprint
pprint(profile)
importjsonfromnested_lookupimportnested_lookup# pip install nested-lookupfromparselimportSelector# 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=True,headers:dict=None)->Selector:api_result=client.scrape(ScrapeConfig(url=url,headers=headers,unblocker=True,render_js=render_js,cache=False,cache_ttl=900,debug=True,rendering_wait=5000,method='GET',))returnapi_result.selector# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,# so load the public profile page, as the benchmark does, and read the data the page embeds as JSONusername="google"selector=scrape(f"https://www.instagram.com/{username}")datasets=selector.css('script[type="application/json"][data-sjs]::text').getall()dataset=next(dfordindatasetsif"xig_user_by_username"indand"follower_count"ind)user=nested_lookup("xig_user_by_username",json.loads(dataset))[0]# the latest posts are the links in the profile's grid, the ones the benchmark checks forposts=selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()profile={key:user[key]forkeyin["username","full_name","biography","follower_count","following_count","is_verified"]}profile["latest_posts"]=posts[:3]# some examples of what can be found in the dataset:frompprintimportpprintpprint(profile)
import json
from nested_lookup import nested_lookup # pip install nested-lookup
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=True, headers: dict=None) -> Selector:
api_result = client.scrape(ScrapeConfig(
url=url,
headers=headers,
unblocker=True,
render_js=render_js,
cache=False,
cache_ttl=900,
debug=True,
rendering_wait=5000,
method='GET',
))
return api_result.selector
# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,
# so load the public profile page, as the benchmark does, and read the data the page embeds as JSON
username = "google"
selector = scrape(f"https://www.instagram.com/{username}")
datasets = selector.css('script[type="application/json"][data-sjs]::text').getall()
dataset = next(d for d in datasets if "xig_user_by_username" in d and "follower_count" in d)
user = nested_lookup("xig_user_by_username", json.loads(dataset))[0]
# the latest posts are the links in the profile's grid, the ones the benchmark checks for
posts = selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()
profile = {key: user[key] for key in ["username", "full_name", "biography", "follower_count", "following_count", "is_verified"]}
profile["latest_posts"] = posts[:3]
# some examples of what can be found in the dataset:
from pprint import pprint
pprint(profile)
importjsonfromnested_lookupimportnested_lookup# pip install nested-lookupfromparselimportSelector# 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)# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,# so load the public profile page, as the benchmark does, and read the data the page embeds as JSONusername="google"selector=scrape(f"https://www.instagram.com/{username}")datasets=selector.css('script[type="application/json"][data-sjs]::text').getall()dataset=next(dfordindatasetsif"xig_user_by_username"indand"follower_count"ind)user=nested_lookup("xig_user_by_username",json.loads(dataset))[0]# the latest posts are the links in the profile's grid, the ones the benchmark checks forposts=selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()profile={key:user[key]forkeyin["username","full_name","biography","follower_count","following_count","is_verified"]}profile["latest_posts"]=posts[:3]# some examples of what can be found in the dataset:frompprintimportpprintpprint(profile)
import json
from nested_lookup import nested_lookup # pip install nested-lookup
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)
# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,
# so load the public profile page, as the benchmark does, and read the data the page embeds as JSON
username = "google"
selector = scrape(f"https://www.instagram.com/{username}")
datasets = selector.css('script[type="application/json"][data-sjs]::text').getall()
dataset = next(d for d in datasets if "xig_user_by_username" in d and "follower_count" in d)
user = nested_lookup("xig_user_by_username", json.loads(dataset))[0]
# the latest posts are the links in the profile's grid, the ones the benchmark checks for
posts = selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()
profile = {key: user[key] for key in ["username", "full_name", "biography", "follower_count", "following_count", "is_verified"]}
profile["latest_posts"] = posts[:3]
# some examples of what can be found in the dataset:
from pprint import pprint
pprint(profile)
importjsonfromnested_lookupimportnested_lookup# pip install nested-lookupfromparselimportSelector# 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)# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,# so load the public profile page, as the benchmark does, and read the data the page embeds as JSONusername="google"selector=scrape(f"https://www.instagram.com/{username}")datasets=selector.css('script[type="application/json"][data-sjs]::text').getall()dataset=next(dfordindatasetsif"xig_user_by_username"indand"follower_count"ind)user=nested_lookup("xig_user_by_username",json.loads(dataset))[0]# the latest posts are the links in the profile's grid, the ones the benchmark checks forposts=selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()profile={key:user[key]forkeyin["username","full_name","biography","follower_count","following_count","is_verified"]}profile["latest_posts"]=posts[:3]# some examples of what can be found in the dataset:frompprintimportpprintpprint(profile)
import json
from nested_lookup import nested_lookup # pip install nested-lookup
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)
# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,
# so load the public profile page, as the benchmark does, and read the data the page embeds as JSON
username = "google"
selector = scrape(f"https://www.instagram.com/{username}")
datasets = selector.css('script[type="application/json"][data-sjs]::text').getall()
dataset = next(d for d in datasets if "xig_user_by_username" in d and "follower_count" in d)
user = nested_lookup("xig_user_by_username", json.loads(dataset))[0]
# the latest posts are the links in the profile's grid, the ones the benchmark checks for
posts = selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()
profile = {key: user[key] for key in ["username", "full_name", "biography", "follower_count", "following_count", "is_verified"]}
profile["latest_posts"] = posts[:3]
# some examples of what can be found in the dataset:
from pprint import pprint
pprint(profile)
importjsonfromnested_lookupimportnested_lookup# pip install nested-lookupfromparselimportSelector# 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)# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,# so load the public profile page, as the benchmark does, and read the data the page embeds as JSONusername="google"selector=scrape(f"https://www.instagram.com/{username}")datasets=selector.css('script[type="application/json"][data-sjs]::text').getall()dataset=next(dfordindatasetsif"xig_user_by_username"indand"follower_count"ind)user=nested_lookup("xig_user_by_username",json.loads(dataset))[0]# the latest posts are the links in the profile's grid, the ones the benchmark checks forposts=selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()profile={key:user[key]forkeyin["username","full_name","biography","follower_count","following_count","is_verified"]}profile["latest_posts"]=posts[:3]# some examples of what can be found in the dataset:frompprintimportpprintpprint(profile)
import json
from nested_lookup import nested_lookup # pip install nested-lookup
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)
# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,
# so load the public profile page, as the benchmark does, and read the data the page embeds as JSON
username = "google"
selector = scrape(f"https://www.instagram.com/{username}")
datasets = selector.css('script[type="application/json"][data-sjs]::text').getall()
dataset = next(d for d in datasets if "xig_user_by_username" in d and "follower_count" in d)
user = nested_lookup("xig_user_by_username", json.loads(dataset))[0]
# the latest posts are the links in the profile's grid, the ones the benchmark checks for
posts = selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()
profile = {key: user[key] for key in ["username", "full_name", "biography", "follower_count", "following_count", "is_verified"]}
profile["latest_posts"] = posts[:3]
# some examples of what can be found in the dataset:
from pprint import pprint
pprint(profile)
importjsonfromnested_lookupimportnested_lookup# pip install nested-lookupfromparselimportSelector# 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,"render_js":render_js,"premium_proxy":True,"method":'GET',})assertapi_result.ok,api_result.textdata=api_result.json()returnSelector(data['body'])# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,# so load the public profile page, as the benchmark does, and read the data the page embeds as JSONusername="google"selector=scrape(f"https://www.instagram.com/{username}")datasets=selector.css('script[type="application/json"][data-sjs]::text').getall()dataset=next(dfordindatasetsif"xig_user_by_username"indand"follower_count"ind)user=nested_lookup("xig_user_by_username",json.loads(dataset))[0]# the latest posts are the links in the profile's grid, the ones the benchmark checks forposts=selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()profile={key:user[key]forkeyin["username","full_name","biography","follower_count","following_count","is_verified"]}profile["latest_posts"]=posts[:3]# some examples of what can be found in the dataset:frompprintimportpprintpprint(profile)
import json
from nested_lookup import nested_lookup # pip install nested-lookup
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,
"render_js": render_js,
"premium_proxy": True,
"method": 'GET',
}
)
assert api_result.ok, api_result.text
data = api_result.json()
return Selector(data['body'])
# Instagram's backend profile API (web_profile_info) no longer answers logged-out requests reliably,
# so load the public profile page, as the benchmark does, and read the data the page embeds as JSON
username = "google"
selector = scrape(f"https://www.instagram.com/{username}")
datasets = selector.css('script[type="application/json"][data-sjs]::text').getall()
dataset = next(d for d in datasets if "xig_user_by_username" in d and "follower_count" in d)
user = nested_lookup("xig_user_by_username", json.loads(dataset))[0]
# the latest posts are the links in the profile's grid, the ones the benchmark checks for
posts = selector.xpath("//main//a[contains(@href,'/p/') or contains(@href,'/reel/')]/@href").getall()
profile = {key: user[key] for key in ["username", "full_name", "biography", "follower_count", "following_count", "is_verified"]}
profile["latest_posts"] = posts[:3]
# some examples of what can be found in the dataset:
from pprint import pprint
pprint(profile)
How to choose a web scraping API for Instagram
Because Instagram runs its own bot detection and a login wall, 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 profile
lookups give you more room on cost. Large profile or post crawls put more weight on reliability and concurrency.
Reliability first.Scrapingdog leads the current Instagram ranking, which makes it the
default starting point for production public profile crawls. The ranking history shows its track record.
Value. Among the APIs still clearing Instagram, sort by cost per successful request. The
cheapest sticker price is rarely the cheapest per usable Instagram page.
Speed. For latency sensitive profile lookups rather than bulk crawls, pick the fastest option
that still clears Instagram reliably.
The key principle is to judge on cost per successful request, not sticker price. Instagram returns block pages and
login wall redirects 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 Instagram
We independently benchmark 6 web scraping APIs against live Instagram pages, up to 1,550 requests per
service, once a month. Every API is tested against the same public Instagram 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.
6 APIs ยท up to 1,550 requests each ยท once a month ยท no affiliate links, no sponsors
Benchmarking Instagram has one wrinkle worth knowing about. Success is measured on response content, not HTTP
status codes. Instagram's block responses and login wall redirects 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 profile or post 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: Oct 01 to Nov 01, 2026.
QUALITY TESTING
Frequently asked questions about scraping Instagram
Is it legal to scrape Instagram?
Scraping publicly available Instagram data is more sensitive than scraping ecommerce listings because profiles and
posts often contain personal data, which can fall under privacy laws such as GDPR and CCPA. Instagram's Terms of
Service also 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.
What's the cheapest API that works on Instagram?
Sort the ranked table by cost per successful request and read down to the first provider still clearing Instagram
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 Instagram's blocks and login wall redirects hide those
failures unless you check response content.
Do I need a headless browser to scrape Instagram?
In practice yes. Instagram scores browser signals on every request, and producing credible ones reliably needs a
genuine browser context. The profile data itself is embedded as JSON in the profile page, so once you're past the
bot checks you read that JSON rather than the DOM. The APIs at the top of the ranking
handle the browser and antibot layer for you.
Why do some APIs score low on Instagram?
Because Instagram blocks clients whose browser signals it doesn't trust, and Instagram redirects unauthenticated or
suspicious requests to a login wall. Those responses come back as block or login wall pages, and since we score on
response content, they count as failures rather than successes.
How often is this benchmark updated?
Once a month against the same live public Instagram targets, up to 1,550 requests per API each run. We publish after
validating the run and checking failures for configuration or detection errors.
Conclusion
Instagram runs its own bot detection and a login wall, and its backend API no longer answers logged-out requests reliably, so the right
web scraping API is one that clears the antibot layer reliably and reaches the public profile and post data. For
production public profile crawls, start with Scrapingdog, the highest success rate in the current run. For
cost or speed on lighter lookups, choose among the providers still clearing Instagram this run.
Whatever you pick, verify results on response content rather than status codes, because Instagram's block pages
and login wall redirects don't always carry a hard error status. The benchmark refreshes once a month, so check
the live Instagram results before committing.