Best Web Scraping APIs for Realtor: 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 Realtor.com, with a
99% success rate across 8 web scraping APIs
benchmarked against live Realtor.com pages in July 2026.
2 of the 8 cleared Realtor reliably enough to recommend.
Realtor.com is protected by Kasada, 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.
Ranking history: web scraping APIs for Realtor over time
Realtor target ranking history
The 6 web scraping APIs for Realtor, reviewed
1. Scrapfly: 99% success on Realtor
On Realtor
Speed
Cost/1k
Overall
From
99%
9.0s
$4.35
#1 of 6
$30/mo
Realtor.com puts Kasada in front of its property and search pages, and the listing data sits in a
__NEXT_DATA__ JSON blob rather than the visible HTML. Kasada issues an obfuscated JavaScript
challenge with a proof of work step, so a client has to run the script in a real browser to earn a valid
token. Scrapfly cleared 99% here by doing exactly that, reaching the
rendered page and the embedded JSON instead of a block. Because it only bills for successful requests, the
block pages Realtor returns don't quietly run up the cost.
At $4.35 per 1,000 successful requests and
9.0s average response time, the figures are strong for an API capable of
clearing Kasada. The 99% 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 Realtor benchmark this run, solving Kasada's JavaScript proof of work challenge
in a real browser
Only charges for successful scrapes, so blocked requests cost nothing
One asp flag plus residential proxies handles Realtor 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 search 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. Firecrawl: 93% success on Realtor
On Realtor
Speed
Cost/1k
Overall
From
93%
5.9s
$7.38
#2 of 6
$16/mo
Firecrawl's draw on Realtor is its output format. It returns structured markdown rather than raw HTML, so a
pipeline feeding an LLM or RAG system skips the parsing step on Realtor's page markup. Rendering a full
browser on every page and generating that markdown carries a cost and latency premium over lighter, HTTP based
options, so that saving has to be worth the premium for your use case. On Realtor it cleared 93%
this run.
Pros:
Returns markdown built for LLM pipelines, saving a parsing step for AI and RAG pipelines
Runs a real browser, which covers pages that need JavaScript execution
Cons:
Cost is the top user complaint in reviews
Renders a full browser on every page, which adds latency
3. WebScrapingAPI: 67% success on Realtor
On Realtor
Speed
Cost/1k
Overall
From
67%
21.3s
$2.71
#3 of 6
$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 Realtor it cleared 67% this run.
Pros:
Broad language SDK range behind a simple REST interface
Async and batch submission for queued jobs
Cons:
Kasada's blocked responses are easy to keep paying for unless you check content
Support tickets often go unanswered for days, per user reviews
4. Scraperapi: 15% success on Realtor
On Realtor
Speed
Cost/1k
Overall
From
15%
5.7s
$0.49
#4 of 6
$49/mo
Scraperapi is built around a fast request path. When it clears a request it tends to return quickly, which
suits latency sensitive lookups that can absorb retries. Realtor's Kasada layer has to be cleared before the
__NEXT_DATA__ payload is reachable, and the scorecard carries how it did on that this run. On
Realtor it cleared 15% this run.
Pros:
Fast when it clears, with a simple integration
Broad language SDK support
Cons:
Login required flows and form filling are off limits
Geotargeting is gated by plan (US and EU only until the Business tier)
5. Scrapingdog: 14% success on Realtor
On Realtor
Speed
Cost/1k
Overall
From
14%
13.7s
$5.00
#5 of 6
$40/mo
Scrapingdog offers low entry pricing and a simple API, without first party SDKs. Kasada requires a valid
proof of work token from its challenge script before it returns the page. On Realtor it cleared
14% 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
6. Scrapingant: 1% success on Realtor
On Realtor
Speed
Cost/1k
Overall
From
1%
6.7s
$1.90
#6 of 6
$19/mo
Scrapingant bundles JavaScript rendering and session support at a low entry price, with a smaller feature
surface than the larger providers. On Realtor it cleared 1% 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 Realtor.com
Realtor.com is one of the largest US real estate portals, and the data people scrape from it is mostly property
detail. That means listing prices, addresses, beds and baths and square footage, lot size, photos, property and
tax history, days on market, school and neighborhood info, and listing agent or broker details. Most of this lives
on property detail pages (the /realestateandhomes-detail/ URLs) and on search results by city or ZIP.
Realtor.com is a Next.js application, so the visible HTML is only part of the story. The reliable place to read
data is the __NEXT_DATA__ JSON blob embedded in the page, which holds the structured property and
search records in one object rather than scattered DOM nodes. A genuine browser context matters for clearing the
antibot layer, and search results paginate through predictable URL parameters.
JS FINGERPRINT
Realtor.com is protected by Kasada. See the
Kasada benchmark page for how Kasada detects bots. The
practical detail for scraping Realtor is that Kasada requires a client to run its JavaScript challenge and return
a valid token, so a plain HTTP request without a real browser context gets blocked before it reaches the listing
data.
realtor_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,method='GET',))returnapi_result.selectorurl="https://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"selector=scrape(url)# The entire dataset can be found in a javascript variable:data=selector.css("script#__NEXT_DATA__::text").get()data=json.loads(data)["props"]["pageProps"]["initialReduxState"]# The resulting dataset is pretty big but here are some example fields:frompprintimportpprintpprint(data)
Output$ python realtor_scraper.py
{
'property_id': '2181349460',
'status': 'for_sale',
'price_per_sqft': 2190,
'photo_count': 45,
'primary_photo': {'href': 'https://ap.rdcpix.com/48bce403ce912cb3e41bf38df9526a4al-b3276956225s.jpg'}
# and much more
}
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,
method='GET',
))
return api_result.selector
url = "https://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"
selector = scrape(url)
# The entire dataset can be found in a javascript variable:
data = selector.css("script#__NEXT_DATA__::text").get()
data = json.loads(data)["props"]["pageProps"]["initialReduxState"]
# The resulting dataset is pretty big but here are some example fields:
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{},formats=['rawHtml'],only_main_content=False,skip_tls_verification=False,timeout=150000,store_in_cache=False,max_age=900,)assertapi_result.raw_html,"firecrawl returned no html for this page"returnSelector(api_result.raw_html)url="https://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"selector=scrape(url)# The entire dataset can be found in a javascript variable:data=selector.css("script#__NEXT_DATA__::text").get()data=json.loads(data)["props"]["pageProps"]["initialReduxState"]# The resulting dataset is pretty big but here are some example fields:frompprintimportpprintpprint(data)
Output$ python realtor_scraper.py
{
'property_id': '2181349460',
'status': 'for_sale',
'price_per_sqft': 2190,
'photo_count': 45,
'primary_photo': {'href': 'https://ap.rdcpix.com/48bce403ce912cb3e41bf38df9526a4al-b3276956225s.jpg'}
# and much more
}
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 {},
formats=['rawHtml'],
only_main_content=False,
skip_tls_verification=False,
timeout=150000,
store_in_cache=False,
max_age=900,
)
assert api_result.raw_html, "firecrawl returned no html for this page"
return Selector(api_result.raw_html)
url = "https://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"
selector = scrape(url)
# The entire dataset can be found in a javascript variable:
data = selector.css("script#__NEXT_DATA__::text").get()
data = json.loads(data)["props"]["pageProps"]["initialReduxState"]
# The resulting dataset is pretty big but here are some example fields:
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://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"selector=scrape(url)# The entire dataset can be found in a javascript variable:data=selector.css("script#__NEXT_DATA__::text").get()data=json.loads(data)["props"]["pageProps"]["initialReduxState"]# The resulting dataset is pretty big but here are some example fields:frompprintimportpprintpprint(data)
Output$ python realtor_scraper.py
{
'property_id': '2181349460',
'status': 'for_sale',
'price_per_sqft': 2190,
'photo_count': 45,
'primary_photo': {'href': 'https://ap.rdcpix.com/48bce403ce912cb3e41bf38df9526a4al-b3276956225s.jpg'}
# and much more
}
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://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"
selector = scrape(url)
# The entire dataset can be found in a javascript variable:
data = selector.css("script#__NEXT_DATA__::text").get()
data = json.loads(data)["props"]["pageProps"]["initialReduxState"]
# The resulting dataset is pretty big but here are some example fields:
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={"render":False,},method='GET',)assertapi_result.ok,api_result.textreturnSelector(api_result.text)url="https://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"selector=scrape(url)# The entire dataset can be found in a javascript variable:data=selector.css("script#__NEXT_DATA__::text").get()data=json.loads(data)["props"]["pageProps"]["initialReduxState"]# The resulting dataset is pretty big but here are some example fields:frompprintimportpprintpprint(data)
Output$ python realtor_scraper.py
{
'property_id': '2181349460',
'status': 'for_sale',
'price_per_sqft': 2190,
'photo_count': 45,
'primary_photo': {'href': 'https://ap.rdcpix.com/48bce403ce912cb3e41bf38df9526a4al-b3276956225s.jpg'}
# and much more
}
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={
"render": False,
},
method='GET',
)
assert api_result.ok, api_result.text
return Selector(api_result.text)
url = "https://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"
selector = scrape(url)
# The entire dataset can be found in a javascript variable:
data = selector.css("script#__NEXT_DATA__::text").get()
data = json.loads(data)["props"]["pageProps"]["initialReduxState"]
# The resulting dataset is pretty big but here are some example fields:
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,"dynamic":True,"premium":True,"method":'GET',}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://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"selector=scrape(url)# The entire dataset can be found in a javascript variable:data=selector.css("script#__NEXT_DATA__::text").get()data=json.loads(data)["props"]["pageProps"]["initialReduxState"]# The resulting dataset is pretty big but here are some example fields:frompprintimportpprintpprint(data)
Output$ python realtor_scraper.py
{
'property_id': '2181349460',
'status': 'for_sale',
'price_per_sqft': 2190,
'photo_count': 45,
'primary_photo': {'href': 'https://ap.rdcpix.com/48bce403ce912cb3e41bf38df9526a4al-b3276956225s.jpg'}
# and much more
}
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,
"dynamic": True,
"premium": True,
"method": 'GET',
}
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://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"
selector = scrape(url)
# The entire dataset can be found in a javascript variable:
data = selector.css("script#__NEXT_DATA__::text").get()
data = json.loads(data)["props"]["pageProps"]["initialReduxState"]
# The resulting dataset is pretty big but here are some example fields:
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,)# the scrapingant client returns its own Response object: content holds the pageassertapi_result.status_code==200,api_result.textreturnSelector(api_result.content)url="https://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"selector=scrape(url)# The entire dataset can be found in a javascript variable:data=selector.css("script#__NEXT_DATA__::text").get()data=json.loads(data)["props"]["pageProps"]["initialReduxState"]# The resulting dataset is pretty big but here are some example fields:frompprintimportpprintpprint(data)
Output$ python realtor_scraper.py
{
'property_id': '2181349460',
'status': 'for_sale',
'price_per_sqft': 2190,
'photo_count': 45,
'primary_photo': {'href': 'https://ap.rdcpix.com/48bce403ce912cb3e41bf38df9526a4al-b3276956225s.jpg'}
# and much more
}
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,
)
# 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://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"
selector = scrape(url)
# The entire dataset can be found in a javascript variable:
data = selector.css("script#__NEXT_DATA__::text").get()
data = json.loads(data)["props"]["pageProps"]["initialReduxState"]
# The resulting dataset is pretty big but here are some example fields:
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://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"selector=scrape(url)# The entire dataset can be found in a javascript variable:data=selector.css("script#__NEXT_DATA__::text").get()data=json.loads(data)["props"]["pageProps"]["initialReduxState"]# The resulting dataset is pretty big but here are some example fields:frompprintimportpprintpprint(data)
Output$ python realtor_scraper.py
{
'property_id': '2181349460',
'status': 'for_sale',
'price_per_sqft': 2190,
'photo_count': 45,
'primary_photo': {'href': 'https://ap.rdcpix.com/48bce403ce912cb3e41bf38df9526a4al-b3276956225s.jpg'}
# and much more
}
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://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"
selector = scrape(url)
# The entire dataset can be found in a javascript variable:
data = selector.css("script#__NEXT_DATA__::text").get()
data = json.loads(data)["props"]["pageProps"]["initialReduxState"]
# The resulting dataset is pretty big but here are some example fields:
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://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"selector=scrape(url)# The entire dataset can be found in a javascript variable:data=selector.css("script#__NEXT_DATA__::text").get()data=json.loads(data)["props"]["pageProps"]["initialReduxState"]# The resulting dataset is pretty big but here are some example fields:frompprintimportpprintpprint(data)
Output$ python realtor_scraper.py
{
'property_id': '2181349460',
'status': 'for_sale',
'price_per_sqft': 2190,
'photo_count': 45,
'primary_photo': {'href': 'https://ap.rdcpix.com/48bce403ce912cb3e41bf38df9526a4al-b3276956225s.jpg'}
# and much more
}
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://www.realtor.com/realestateandhomes-detail/16-Sea-Cliff-Ave_San-Francisco_CA_94121_M21813-49460"
selector = scrape(url)
# The entire dataset can be found in a javascript variable:
data = selector.css("script#__NEXT_DATA__::text").get()
data = json.loads(data)["props"]["pageProps"]["initialReduxState"]
# The resulting dataset is pretty big but here are some example fields:
from pprint import pprint
pprint(data)
How to choose a web scraping API for Realtor
Because Realtor.com sits behind Kasada, 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 property lookups give you more room
on cost. Large search area crawls put more weight on reliability and concurrency.
Reliability first.Scrapfly leads the current Realtor ranking, which makes it the
default starting point for production crawls. The ranking history shows its track record.
Value. Among the APIs still clearing Realtor, sort by cost per successful request. The cheapest
sticker price is rarely the cheapest per usable Realtor page.
Speed. For latency sensitive property lookups rather than bulk crawls, pick the fastest option
that still clears Realtor reliably.
The key principle is to judge on cost per successful request, not sticker price. Kasada blocks requests that can't
complete its challenge, 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 Realtor
We independently benchmark 8 web scraping APIs against live Realtor.com pages, 1,000+ requests per
service, twice a month. Every API is tested against the same Realtor 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 Realtor has one wrinkle worth knowing about. Success is measured on response content, not HTTP status
codes. Kasada can return challenge or block pages that still carry a 200, so a test that only checked for a 200
would overstate results. We verify that responses contain the expected property 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 Realtor
Is it legal to scrape Realtor.com?
Scraping publicly available Realtor.com data such as listing prices and property details is generally treated as
lower risk than scraping data behind a login, but Realtor.com's Terms of Service prohibit automated access, and
agent contact details can fall under privacy laws. 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 Realtor?
Sort the ranked table by cost per successful request and read down to the first provider still clearing Realtor
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 Kasada's blocks hide those failures unless you check
response content.
Do I need a headless browser to scrape Realtor?
In practice yes. Kasada requires a client to run its JavaScript challenge and return a valid proof of work token,
which a plain HTTP request can't do, and Realtor.com is a Next.js site that leans heavily on JavaScript. Once you
clear the challenge, the data is in the __NEXT_DATA__ JSON. The APIs at the top of the ranking handle
the browser and antibot layer for you.
Why do some APIs score low on Realtor?
Because Kasada will not return the page without a valid proof of work token from its challenge script. Clients it
doesn't trust get blocked outright or receive challenge pages, 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 Realtor.com targets, 1,000+ requests per API each run. We publish after
validating the run and checking failures for configuration or detection errors.
Conclusion
Realtor.com sits behind Kasada's JavaScript proof of work challenge and serves its data through an embedded JSON
blob, so the right web scraping API is one that runs the challenge in a real browser and reaches the rendered
page. For production crawls, start with Scrapfly, the highest success rate in the current run. For cost
or speed on lighter property page jobs, choose among the providers still clearing Realtor this run.
Whatever you pick, verify results on response content rather than status codes, because Kasada can return
challenge pages that still carry a 200. The benchmark refreshes twice a month, so check the live Realtor results
before committing.