Zillow scraper
Pricing
from $1.70 / 1,000 results
Zillow scraper
Pricing
from $1.70 / 1,000 results
Rating
0.0
(0)
Developer
Lukáš Širhal
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
🏡 Zillow Scraper — For Sale, For Rent & Sold
Scrape Zillow listings by location, search URL or ZPID — no Zillow API key, no browser, no captured credentials.
Type a place name (Los Angeles, Melrose, Bronx, 90210), paste a Zillow search URL, or hand it a list of ZPIDs, and get every for-sale, for-rent or recently-sold listing: price, address, coordinates, beds and baths, living area, lot size, year built, Zestimate, tax assessment, open houses, broker, 3D tour and every listing photo.
Filter exactly as you would in the Zillow app — price, beds, baths, home type, square feet, lot size, year built, days on market, HOA, keywords, views, basement, pool, waterfront, pets, laundry — and sort the results the same way.
✨ Why this Zillow scraper
| This scraper | Typical Zillow scraper | |
|---|---|---|
| Input | Place name, Zillow URL, or ZPIDs | Usually a hand-crafted ?searchQueryState=… URL only |
| Engine | Direct API calls | Headless browser + HTML parsing |
| Detail request per listing | Not needed — the search response is already complete | Often a second, billed request per property |
| Result ceiling per area | Unlimited via automatic map splitting | Often ~500 or ~820 per URL |
| Credentials | None | Sometimes an API key or a login |
Zillow caps any single search at 1,000 results. This Actor works around that cap by splitting the map into quadrants and searching each one, so a dense metro is scraped completely rather than truncated at the first page.
🚪 What it can do
- 🔎 Search by free text — city, borough, county, neighborhood, ZIP, subdivision or state.
- 🔗 Or paste Zillow URLs — a search URL's map area and its filters are read straight out of
searchQueryState; a property URL is fetched by its ZPID. - 🆔 Or give it ZPIDs — fetch specific properties directly, no search involved.
- 🏷️ For sale, for rent, or recently sold — one switch.
- 🎛️ The Zillow app's own filters — price, beds, baths, home type, square feet, lot size, year built, days on market (or sold within for sold listings), keywords, HOA, basement, view, pool, air conditioning, waterfront, parking, open houses, 3D tours, price cuts, foreclosures — plus pets, laundry, parking and availability date for rentals.
- ↕️ Sorting — price, newest, beds, baths, square feet, lot size or year built, ascending or descending.
- 📦 Full listing data in a single request — no second request per property.
- 🗺️ Automatic map splitting — beats Zillow's 1,000-per-search cap.
- ♻️ Resumable — results are deduplicated by
zpidand counters survive a migration, so a restarted run does not re-push what it already scraped. - ⬇️ Export anywhere — JSON, CSV, Excel, XML or HTML, via the Apify API, integrations or webhooks.
🎯 What can you do with Zillow data?
- Market research — track median prices, days on market and inventory across neighborhoods over time.
- Comparable sales — pull recently sold listings with
lastSoldDateand price per square foot to value a property. - Deal finding — scrape for-sale listings with
zestimateandpriceChange, and filter for homes priced under their Zestimate or recently reduced. - Rental analysis — collect rents by bedroom count and compare against
rentZestimatefor yield estimates. - Lead generation — collect listings with broker and agent names in a target area.
- New-listing alerts — schedule a run with
daysOnZillowMax: 1and a webhook to be notified of everything listed today.
📚 How do I scrape Zillow?
- Create a free Apify account.
- Open this Actor and tell it what to scrape — put a location in the Search field (e.g.
Los Angeles), paste Zillow URLs into Zillow URLs, or list ZPIDs. One of the three is enough. - Pick a Listing type — for sale, for rent, or recently sold.
- Optionally set filters and a sort order. They work the same way as the filter sheet in the Zillow app.
- Set Limit the number of items (or leave it empty to scrape everything).
- Click Start, then download the results as JSON, CSV, Excel, XML or HTML.
⬇️ Input
Give it one of search, startUrls or zpids. Everything else is optional.
| Field | Type | Default | Description |
|---|---|---|---|
search | String | — | Location to search, e.g. Los Angeles, Melrose, Bronx, 90210 |
startUrls | Array | — | Zillow search URLs (must include ?searchQueryState=…) or property URLs |
zpids | Array | — | Zillow property IDs, e.g. 89070667 |
type | String | sale | sale, rent, or sold |
maxItems | Integer | 200 | Maximum properties to return; empty means no limit |
maxSplitLevel | Integer | 3 | How deep a map area may be split — see Map splitting |
proxy | Object | Apify Proxy | Proxy configuration — US residential recommended |
debugLog | Boolean | false | Verbose logging |
Filters
All optional. Leave one empty to not apply it.
| Field | Type | Description |
|---|---|---|
priceMin, priceMax | Integer | Price range. For rent, the monthly rent |
bedsMin, bedsMax | Integer | Bedrooms |
bathsMin | Integer | Bathrooms (half baths count, so 2 also matches 2.5) |
homeTypes | Array | singleFamily, condo, townhome, multiFamily, apartment, manufactured, land |
sqftMin, sqftMax | Integer | Living area in square feet |
lotSizeMin, lotSizeMax | Integer | Lot size in square feet (1 acre = 43,560) |
yearBuiltMin, yearBuiltMax | Integer | Year built |
daysOnZillowMax | Integer | Listed within N days — for sold, sold within N days |
keywords | String | Free text over the listing description, e.g. pool |
showOnly | Array | priceReduction, openHouses, approvedVirtualTour, singleStory, garage |
amenities | Array | havePool, haveAirConditioning, onWaterfront |
parkingSpotsMin | Integer | Minimum parking spots |
basementStatuses | Array | finished, partial, unfinished |
viewTypes | Array | city, mountain, water, park |
hoaMaxPerMonth | Integer | Maximum monthly HOA fee |
includeHomesWithNoHoaData | Boolean | Keep listings with unknown HOA (default true) |
includePending | Boolean | Keep pending / under-contract listings (hidden by default) |
includeForeclosed, includePreforeclosure | Boolean | Add (pre-)foreclosures |
pets | Array | Rentals — largeDogs, smallDogs |
rentalAmenities | Array | Rentals — laundryAvailable, parkingAvailable |
availableFrom | String | Rentals — available on or after YYYY-MM-DD |
excludeSeniorCommunity | Boolean | Rentals — drop 55+ communities |
sortOrder | String | relevance, price, beds, baths, daysOn, recentlyChanged, livingArea, lotArea, yearBuilt |
sortAscending | Boolean | Low to high instead of high to low |
🧪 Example inputs
Family homes in a city, under $1.5M:
{"search": "Austin, TX","type": "sale","priceMax": 1500000,"bedsMin": 3,"bathsMin": 2,"homeTypes": ["singleFamily"],"sortOrder": "price","sortAscending": true,"maxItems": 500}
Everything listed today, for a daily alert:
{"search": "90210","type": "sale","daysOnZillowMax": 1}
Pet-friendly rentals with in-unit laundry, available next month:
{"search": "Brooklyn, NY","type": "rent","priceMin": 2000,"priceMax": 4000,"pets": ["largeDogs", "smallDogs"],"rentalAmenities": ["laundryAvailable"],"availableFrom": "2026-10-01"}
Comparable sales from the last 6 months:
{"search": "Melrose, Bronx","type": "sold","daysOnZillowMax": 182,"homeTypes": ["singleFamily", "condo"]}
From a Zillow search URL, filters and all:
{"startUrls": [{ "url": "https://www.zillow.com/bronx-new-york-ny/?searchQueryState=%7B%22mapBounds%22%3A%7B...%7D%7D" }]}
Specific properties by ZPID:
{ "zpids": ["89070667", "448148857"] }
🌳 What data can I extract?
| 🏠 Full address | 💰 Price and price per sqft |
| 🛏️ Bedrooms and bathrooms | 📍 Latitude and longitude |
| 📅 Listing date | ⏳ Days on Zillow |
| 📷 All listing photos | 📏 Living area and lot size |
| 💼 Broker and agent name | 🚪 Open house times |
| 🏠 Home type and status | 🏗️ Year built |
| 🔗 Listing URL | 🏢 ZPID |
| 💸 Rent Zestimate | 💵 Zestimate |
| 🧾 Tax assessed value | 📉 Price change |
| 🎥 Video and 3D tour | 📐 Floor plan availability |
⬆️ Output
One dataset item per property. All three examples below are real, unedited items.
For sale
{"zpid": 89070667,"url": "https://www.zillow.com/homedetails/89070667_zpid/","homeStatus": "forSale","marketingStatus": "active","homeType": "condo","price": 299999,"currency": "usd","pricePerSquareFoot": 452,"zestimate": null,"rentZestimate": null,"taxAssessedValue": 73950,"taxAssessmentYear": 2025,"address": {"streetAddress": "837 Washington Avenue #3B","city": "Bronx","state": "NY","zipcode": "10451"},"latitude": 40.822433,"longitude": -73.91177,"bedrooms": 1,"bathrooms": 1,"fullBathrooms": 1,"halfBathrooms": 0,"livingArea": 664,"lotSize": 0.5089072543617998,"lotSizeUnit": "acres","yearBuilt": 2008,"daysOnZillow": 1,"listingDate": "2026-08-24T17:43:05.566Z","isZillowOwned": false,"isShowcaseListing": false,"brokerName": "Listing by: Century 21 Milestone Team Rlty","openHouse": [{ "startTime": "2026-08-29T16:00:00.000Z", "endTime": "2026-08-29T17:00:00.000Z" }],"hasFloorPlan": true,"virtualTour": "https://www.zillow.com/view-3d-home/8644b1a0-852b-40d0-8f9f-db458d6bce55","photos": ["https://photos.zillowstatic.com/fp/e53c22aded73ef47a29c8f666405d1d4-p_f.jpg"]}
For rent
Rentals are grouped buildings rather than single homes, so instead of one price they carry a title, a minPrice/maxPrice band, the available unitsGroup, and matchingHomeCount — how many units in that building match your filters.
{"zpid": 463355670,"url": "https://www.zillow.com/homedetails/463355670_zpid/","homeStatus": "forRent","title": "Inkwell","price": 3664,"minPrice": 3664,"maxPrice": 3980,"currency": "usd","address": {"streetAddress": "586 Gerard Ave","city": "Bronx","state": "NY","zipcode": "10451"},"latitude": 40.82056,"longitude": -73.9285,"listingDate": "2026-08-21T20:50:20.785Z","unitsGroup": [{ "bedrooms": 2, "minPrice": 3664, "isRoomForRent": false }],"matchingHomeCount": 8,"photos": ["https://photos.zillowstatic.com/fp/b60cc73bb73341791703c4b83eff7127-p_f.jpg"]}
Recently sold
Sold listings add lastSoldDate, and price is the sale price — compare it against zestimate for a valuation gap.
{"zpid": 29816126,"url": "https://www.zillow.com/homedetails/29816126_zpid/","homeStatus": "recentlySold","marketingStatus": "closed","homeType": "singleFamily","price": 650000,"pricePerSquareFoot": 464,"zestimate": 749900,"rentZestimate": 4131,"taxAssessedValue": 732000,"address": {"streetAddress": "1819 Willow Lane","city": "Bronx","state": "NY","zipcode": "10461"},"bedrooms": 3,"bathrooms": 3,"livingArea": 1400,"lotSize": 2500,"lotSizeUnit": "squareFeet","yearBuilt": 1925,"lastSoldDate": "2023-09-04T04:00:00.000Z","brokerName": "Keller Williams Realty NYC Grp","photos": ["https://photos.zillowstatic.com/fp/af0b6605ea49bb6446d4b724bc1801a9-p_f.jpg"]}
Zillow decides which fields it returns per listing, so individual fields can be null — zestimate in particular is absent for many for-sale listings.
🗺️ Map splitting
Zillow's search rejects pageNumber * pageSize > 1000, so at most 1,000 listings are reachable per map area (2 pages of 500). When an area holds more, the scraper splits it into four quadrants and searches each one separately, recursively, until every listing is reachable.
The root area is the region's own bounding box, so the very first split already divides the results — scraping all 2,666 Bronx listings takes 12 requests this way, against 53 from a US-wide root.
maxSplitLevel limits the depth. Only splits that actually divide the results count towards it, so a raised limit costs nothing on areas that don't need it. Raise it for a large metro or a whole state; lower it to cap the run.
💰 What does it cost?
$3.00 per 1,000 results on the free plan, dropping with your Apify plan:
| Plan | Per 1,000 results |
|---|---|
| Free | $3.00 |
| Starter | $2.60 |
| Scale | $2.20 |
| Business | $1.70 |
You pay per result, plus $0.00005 when a run starts. Platform usage (compute, proxy, storage) is included — there is no separate infrastructure bill.
Two things keep the total down:
maxItemsis a hard budget cap. Set it and the run cannot exceed it. 200 results costs $0.60 on the free plan, 5,000 costs $15.00.- There is no charge per property detail. The mobile search response already carries the full listing, so scrapers that open a detail page per home bill you roughly twice — once for the result, once for the detail — where this one bills once.
Filtering server-side is also cheaper than filtering afterwards: priceMin, homeTypes and daysOnZillowMax shrink what gets scraped in the first place, so you are not paying for rows you throw away.
🧰 Getting zero or too few results?
| Symptom | Cause and fix |
|---|---|
| Fewer items than the total Zillow shows | The area needs deeper splitting — raise maxSplitLevel. |
| Zero results with several filters set | Filters are combined with AND. A small neighborhood plus a narrow filter genuinely can hold nothing — widen the area or drop a filter to confirm. |
| Zero results for rent | Rentals are grouped buildings, so a neighborhood may have only a handful. Try the city instead of the neighborhood. |
carries no searchQueryState in the log | The URL has no search state. Open it on zillow.com, move the map once, then copy the URL again. |
| A filter from a URL "did nothing" | Some website-only filters have no mobile-API equivalent. The run log names each one it skipped. |
| Wrong location matched | search takes the first autocomplete match. Be more specific (Melrose, Bronx rather than Melrose), or use a URL. |
px-captcha in the log | PerimeterX blocked the IP. Enable Apify Proxy, ideally US residential. |
🔌 Using the data elsewhere
Results land in a dataset you can export as JSON, CSV, Excel, XML or HTML, or pull through the API:
curl "https://api.apify.com/v2/acts/<username>~zillow-scraper/runs?token=<TOKEN>" \-d '{ "search": "Austin, TX", "type": "sale", "maxItems": 100 }' \-H 'Content-Type: application/json'
In Python, with the Apify client:
from apify_client import ApifyClientclient = ApifyClient("<TOKEN>")run = client.actor("<username>/zillow-scraper").call(run_input={"search": "Austin, TX","type": "sale","priceMax": 1500000,"maxItems": 100,})for home in client.dataset(run["defaultDatasetId"]).iterate_items():print(home["address"]["streetAddress"], home["price"])
It also connects to Make, Zapier, Slack, Airbyte, GitHub, Google Sheets, Google Drive and LangChain through Apify integrations, and webhooks can trigger an action when a run finishes — which is how you build a new-listing alert. For AI agents, the Actor is callable through the Apify MCP server at https://mcp.apify.com.
🔐 Authentication and proxies
No credentials of any kind. Zillow's public web search is behind PerimeterX, but the mobile endpoints this Actor uses validate only client identity, not any device or session token:
x-client: com.zillow.ZillowMapis required; without it the API answersmissingRequiredTrackingHeader.- The user agent must not look like automation or a desktop browser —
curl/*and Chrome user agents get anpx-captcha403, while the app's own user agent passes. zgsession,x-px-helloandx-px-uuidrotate constantly in the real app and are never validated, so the Actor sends none of them.
Verified against the live endpoints. Proxies are still recommended: sustained unproxied traffic from one datacenter IP does eventually earn a px-captcha 403, so runs default to Apify Proxy, with US residential the safest choice for large runs.
❓ FAQ
Is there an official Zillow API?
Zillow publishes a handful of partner APIs (Property Details, Neighborhood Data, and others), but they need an approved API key, are rate limited, and cover only part of the data shown on the site. This Actor needs no key and has no daily call limit.
Do I need a Zillow search URL?
No — a plain place name is resolved to Zillow's own region ID for you, which is simpler and more precise than a hand-drawn map box. But if you already have a search URL, paste it into startUrls: its map area and its filters are both read out of the ?searchQueryState=… part. A URL without that part carries no search state, so open it on zillow.com and move the map once before copying it.
How many results can I get?
As many as the region has. Any single Zillow query is capped at 1,000 listings, which the automatic map splitting works around — the practical limit is maxItems and maxSplitLevel, not Zillow.
Does it work for rentals and sold homes?
Yes — set type to rent or sold. Both have their own filters (pets, laundry and availability date for rentals; a sold-within window for sold listings) and their own output shape, shown above.
Can I scrape a specific property?
Yes. Put its URL in startUrls or its ID in zpids — both are fetched directly, with no search and no map area involved.
How do I track price drops in a neighborhood?
Run it with showOnly: ["priceReduction"] on a schedule, and send the dataset to a webhook or Google Sheet. Compare priceChange and price between runs.
Can I use it with Python or AI agents?
Yes — see Using the data elsewhere for an API and Python example. For AI agents, call it through the Apify MCP server at https://mcp.apify.com.
Is scraping Zillow legal?
Scraping publicly available data such as prices, availability and street addresses is generally legal. Listings can also carry personal data about agents, which is covered by the GDPR and similar laws — get legal advice before collecting or storing that.
📝 Changelog
1.1.0
- Filters: price, beds, baths, home type, square feet, lot size, year built, days on market (sold-within for sold listings), keywords, HOA, basement, view, amenities, parking, open houses, 3D tours, price cuts, foreclosures, and rental pets/laundry/parking/availability.
- Sorting by price, newest, beds, baths, square feet, lot size or year built.
- Zillow URLs as input — a search URL's map area and filters are read from
searchQueryState; property URLs are fetched by ZPID. - ZPIDs as input, fetched directly with no search.
1.0.0
- First release: search by place name for sale/rent/sold listings, with automatic map splitting.