TL;DR
Zillow blocks plain HTTP scrapers, so a working Zillow scraper needs stealth fetching and residential proxies. ScrapeGraphAI ships both behind one API call: you describe the fields, it returns validated JSON from any listing page. Every code block below ran against live Zillow pages on July 7, 2026, and you can run the same workflow in the companion Google Colab without installing anything.
To scrape data from Zillow you need three things: listing URLs, a way past Zillow's bot detection, and a schema that turns each page into a database row. This guide covers all three with tested Python code. Find listing URLs with one search call, extract address, price, bedrooms, bathrooms, square footage, and days on market with one extraction schema, then export everything to CSV.
The whole workflow also runs in the browser. Open the notebook, paste your API key, and press run:
Why Your Zillow Scraper Gets Blocked
Zillow protects its pages aggressively. Request a listing with requests.get() and you get the "Press & Hold" bot check, a partial HTML shell, or a page where every price is loaded by JavaScript after the initial response.
We saw this first-hand while testing this article. Fetching the Austin search page without stealth mode returned a technically successful response containing zero listings. The page came back, but the data did not.
The classic fix is to build infrastructure: rotating residential proxies, a headless browser farm, retry logic, fingerprint management. That stack is expensive to rent and worse to maintain, and Zillow updates its defenses often enough that selector-based scrapers break anyway.
ScrapeGraphAI takes a different route. The API includes stealth fetching and residential proxies, so bypassing the bot wall is a parameter, not a project. You pass stealth=True and country="us", and the fetch configuration handles rendering, headers, and proxy routing behind the scenes. No proxy subscription, no browser farm.
Setup
Install the Python SDK. Version 2 requires Python 3.12 or newer:
pip install "scrapegraph-py>=2" pydantic pandasThen create a free ScrapeGraphAI account and copy your API key from the dashboard. The free tier is enough to run every example in this guide. If you prefer to experiment before writing code, the playground runs the same extraction visually.
The examples use Pydantic models as the output schema. The schema is the contract: whatever the page layout looks like, the API returns JSON in exactly this shape. There is also a JavaScript SDK with the same methods if your stack is Node.
Find Zillow Listing URLs with Search
If you already have listing URLs in a database, skip to the next section. If not, one search call finds them:
import os
from scrapegraph_py import ScrapeGraphAI
sgai = ScrapeGraphAI(api_key=os.environ["SGAI_API_KEY"])
search_res = sgai.search(
"site:zillow.com/homedetails house for sale Austin TX",
num_results=10,
)
if search_res.status != "success":
raise RuntimeError(search_res.error)
listing_urls = [
r["url"]
for r in search_res.data.model_dump()["results"]
if "/homedetails/" in r["url"]
]
print(listing_urls)Running this on July 7, 2026 returned ten live property pages, including:
https://www.zillow.com/homedetails/2106-Eva-St-Austin-TX-78704/29473233_zpid/
https://www.zillow.com/homedetails/5700-Avenue-G-Austin-TX-78752/29410916_zpid/
https://www.zillow.com/homedetails/809-E-44th-St-Austin-TX-78751/29400715_zpid/
The extraction example below uses this kind of public listing page: a visible property page with price, address, bedrooms, bathrooms, square footage, status, and photos. ScrapeGraphAI turns that page into the normalized JSON schema instead of depending on Zillow's markup.
The site:zillow.com/homedetails filter matters. Zillow URLs come in many flavors (search pages, agent profiles, rental hubs), and /homedetails/ is the pattern for individual property pages, which are the pages worth extracting. The Python-side filter keeps the code honest even when a search engine sneaks in something else.
Swap the city in the query and the same call covers any market. Nothing else changes.
Scrape Data from Zillow with One Schema
This is the core of the Zillow scraper. Define the fields once, then run every URL through the same extraction:
import os
import time
from pydantic import BaseModel, Field
from scrapegraph_py import FetchConfig, ScrapeGraphAI
class ZillowProperty(BaseModel):
address: str = Field(description="Full street address including city, state, and zip")
price: str = Field(description="Listing price as displayed on the page")
status: str = Field(description="Listing status, for example For sale, Pending, Off market")
bedrooms: float = Field(description="Number of bedrooms")
bathrooms: float = Field(description="Number of bathrooms")
square_feet: int = Field(description="Interior square footage")
days_on_zillow: str = Field(description="Days on Zillow when shown, otherwise 'not shown'")
sgai = ScrapeGraphAI(api_key=os.environ["SGAI_API_KEY"])
prompt = (
"Extract the property listing from this Zillow page. Return the full "
"address, displayed price, listing status, bedrooms, bathrooms, square "
"feet, and days on zillow. Use the visible values on the page."
)
records = []
for listing_url in listing_urls:
for attempt in range(3):
res = sgai.extract(
prompt,
url=listing_url,
schema=ZillowProperty.model_json_schema(),
fetch_config=FetchConfig(stealth=True, country="us"),
)
if res.status == "success":
prop = ZillowProperty.model_validate(res.data.json_data)
records.append({**prop.model_dump(), "listing_url": listing_url})
break
time.sleep(5)Here is the real output from the July 7, 2026 test run, unedited:
[
{
"address": "2409 E Side Dr, Austin, TX 78704",
"price": "$1,197,400",
"status": "OTHER",
"bedrooms": 4.0,
"bathrooms": 3.0,
"square_feet": 3064,
"days_on_zillow": "not shown",
"listing_url": "https://www.zillow.com/homedetails/2409-E-Side-Dr-Austin-TX-78704/29462073_zpid/"
},
{
"address": "2106 Eva St, Austin, TX 78704",
"price": "$1,574,999",
"status": "For Sale",
"bedrooms": 3.0,
"bathrooms": 2.0,
"square_feet": 2120,
"days_on_zillow": "161",
"listing_url": "https://www.zillow.com/homedetails/2106-Eva-St-Austin-TX-78704/29473233_zpid/"
},
{
"address": "5700 Avenue G, Austin, TX 78752",
"price": "$625,000",
"status": "For Sale",
"bedrooms": 4.0,
"bathrooms": 2.0,
"square_feet": 1757,
"days_on_zillow": "14 days",
"listing_url": "https://www.zillow.com/homedetails/5700-Avenue-G-Austin-TX-78752/29410916_zpid/"
}
]Look at the first record. That house is not for sale; the page shows a Zestimate, and the extraction honestly reports status: "OTHER" and a price that is an estimate rather than an ask. This is why the schema includes status: not every Zillow property page is an active listing, and a pipeline that stores only prices will silently mix estimates with real asks. Filter on status == "For Sale" before the data reaches anything that makes decisions.
The retry loop is not decoration either. During testing one request returned a transient HTTP 502 and succeeded on the next attempt. Three attempts with a short sleep is enough for Zillow.
Notice what is missing from this code: no CSS selectors, no XPath, no HTML parsing. When Zillow redesigns a page template, a selector-based scraper returns garbage until someone fixes it. A prompt plus schema describes the data instead of the markup, so the same code kept working across layout changes.
Turn Zillow Data Extraction into a CSV
The records are already flat dictionaries, so the export is two lines:
import pandas as pd
df = pd.DataFrame(records)
df.to_csv("zillow_listings.csv", index=False)That CSV is ready for a spreadsheet, a Postgres COPY, a BigQuery load job, or whatever your analysts use. Store listing_url and a scrape timestamp with every row. The URL makes each record auditable, and the timestamp is what turns isolated snapshots into price history.
The companion Colab runs this exact flow end to end: schema, extract, DataFrame, CSV. The same pattern also appears in the homes-forsale cookbook notebook if you want a second worked example.
Is There a Zillow API?
Zillow does not offer a public API for listing data. The official Zillow data programs run through Bridge Interactive, are approval-gated, and are aimed at MLS participants and enterprise partners, not at developers who need structured listing data for a project. The old public endpoints like GetSearchResults were retired years ago.
So when people search for a "Zillow API", what they usually need is exactly what the code above does: send a URL, get structured JSON back. ScrapeGraphAI works as that missing API layer for Zillow, with the extraction schema playing the role of the API response contract. The difference from an official API is that you define the fields, so the response contains what your application needs instead of what a vendor decided to expose.
The same request works over plain REST if you are not using the SDKs:
curl -X POST https://v2-api.scrapegraphai.com/api/extract \
-H "Content-Type: application/json" \
-H "SGAI-APIKEY: sgai-your-api-key" \
-d '{
"url": "https://www.zillow.com/homedetails/5700-Avenue-G-Austin-TX-78752/29410916_zpid/",
"prompt": "Extract the property listing: address, price, status, bedrooms, bathrooms, square feet, days on zillow.",
"fetchConfig": {"stealth": true, "country": "us"},
"schema": {
"type": "object",
"properties": {
"address": {"type": "string"},
"price": {"type": "string"},
"status": {"type": "string"},
"bedrooms": {"type": "number"},
"bathrooms": {"type": "number"},
"square_feet": {"type": "integer"},
"days_on_zillow": {"type": "string"}
},
"required": ["address", "price", "status", "bedrooms", "bathrooms", "square_feet", "days_on_zillow"]
}
}'Full request options are in the extract API docs and the search API docs.
Skip Search When You Already Have URLs
Search is a discovery tool, not a required step. Plenty of teams already have Zillow URLs: a saved-search export, a CRM full of property links, a previous crawl, an MLS feed mapped to Zillow pages.
In that case, feed your list straight into the extraction loop. One URL or one million URLs, the code path is identical; only the queue in front of it changes. Keep the schema stable, batch the URLs through your own job runner, and write each validated record to storage. The extraction layer does not care where the URLs came from.
Track Zillow Prices Over Time
One scrape answers "what is this house listed at today". The more valuable question is "what changed", and that only needs a schedule and a diff:
- Run the extraction on the same URL list daily or weekly.
- Store every run with a
scraped_attimestamp. - Compare each listing against its previous snapshot by
listing_url. - Alert on the events that matter: price drop, new listing, status flip from For Sale to Pending.
Price drops and days on market are the two signals real estate investors actually act on, and both fall out of snapshot comparison for free. If you build this, alert only on meaningful changes. A monitor that pings someone for every re-extraction gets muted within a week.
The same pattern powers competitor monitoring in other verticals; the ecommerce price scraping guide walks through the identical search-extract-store loop for Amazon, Best Buy, and Walmart product pages.
Works the Same on Redfin and Realtor.com
Nothing in the extraction code is Zillow-specific except the URLs. The ZillowProperty schema describes a property listing, not Zillow's markup, so pointing the loop at Redfin or Realtor.com pages returns the same JSON shape from different sites. One schema, one prompt, every listing source your pipeline needs.
That is the practical difference between an AI-powered real estate scraper and a folder of per-site parsers: adding a source means adding URLs, not writing code.
Scrape Zillow Responsibly
Zillow's terms of service restrict automated access, and listing pages can surface agent names and phone numbers. Be deliberate about what you collect:
- Extract only the property fields your project needs. The schema above deliberately excludes agent contact details.
- Keep request rates low and cache results. Re-scraping an unchanged page burns credits and goodwill.
- Use the data for internal research and analysis, and get legal review before republishing listing data, because MLS content carries its own licensing rules.
For the broader legal picture, read Is Web Scraping Legal?. None of this article is legal advice.
Run It Yourself
Everything above is a single Colab notebook away. Open it, add your key from the ScrapeGraphAI dashboard, and you will have a CSV of live Zillow listings in about two minutes:
When you are ready to move past the notebook, sign up, grab an API key, and drop the extraction loop into your own scheduler. The code you tested in Colab is the code you ship.
Related Articles
- The Complete Guide to Real Estate Scraper Tools in 2026 - Compare the tools before committing to one for your property data pipeline
- Ecommerce Price Scraping API for Developers - The same search-extract-store pattern applied to Amazon, Best Buy, and Walmart
- Is Web Scraping Legal? - What you can collect, store, and republish without a call from legal
- Master Production Web Scraping Best Practices - Queues, retries, and monitoring for when the notebook becomes a product