Zillow Search Scraper avatar

Zillow Search Scraper

Pricing

$24.99/month + usage

Go to Apify Store
Zillow Search Scraper

Zillow Search Scraper

Extract Zillow search data at scale with customizable filters for location, pricing, and home features. Gather listings, estimates, images, and agent details in clean structured format. Ideal for real estate analytics, automation pipelines, and investment research workflows.

Pricing

$24.99/month + usage

Rating

5.0

(1)

Developer

Scrapier

Scrapier

Maintained by Community

Actor stats

1

Bookmarked

10

Total users

0

Monthly active users

4 days ago

Last modified

Share

Zillow Search Scraper — Listings, Zestimate and Price-Drop Data

Zillow Search Scraper pulls property listings from Zillow.com search results by URL, city, or ZIP-code keyword and returns structured JSON for every listing: address, price, beds/baths, home type, and status — plus the Zestimate, Rent Zestimate, tax-assessed value, and price-drop data Zillow's own search response already attaches to each row, with no second detail-page fetch required. Every response is structured JSON, ready to pass directly to an LLM, index into a vector store, or feed a price-monitoring pipeline. No Zillow login is required to run it.

What is Zillow Search Scraper?

Zillow Search Scraper queries Zillow.com's search results by full search URL, city, or ZIP-code keyword and returns one structured JSON row per property listing — for-sale, for-rent, recently sold, or FSBO homes, depending on the filters already encoded in the URL you supply. What sets it apart from a plain listings scraper is that it surfaces the valuation and price-history signals Zillow's own search response already carries per listing — Zestimate, Rent Zestimate, tax-assessed value, and price-drop data — as first-class fields, instead of leaving them buried in a raw nested blob or requiring a second scrape of each property's detail page. No Zillow account or login is required — the Actor reads the same public search endpoints and pages a browser does.

  • Three extraction methods — dynamic-zoom pagination (default, splits large map areas to get past Zillow's search-result cap), plain HTML pagination (fallback for when the API is blocked), and map markers (lightweight, capped ~500 per search URL)
  • Bulk input — any number of full Zillow search URLs or location keywords per run, processed in sequence with results streamed live to the dataset
  • Valuation data bundled per row — zestimate, rentZestimate, taxAssessedValue, priceReduction, datePriceChanged pulled straight from the same search response
  • Listing lifecycle flags — isFSBO, isNewHome, isForAuction, isBankOwned, isOpenHouse, isComingSoon, and daysOnZillow
  • Configurable sortOrder (8 options: relevance, newest, price low/high, square feet, lot size, beds, baths) and request delay
  • Automatic proxy fallback (none → datacenter → residential) plus automatic fallback from the search API to HTML pagination when Zillow's anti-bot layer blocks a request

What data can you get with Zillow Search Scraper?

Every run returns one row per property listing, carrying four field groups: core listing data, valuation/price signals, lifecycle status flags, and raw Zillow metadata.

Result TypeExtracted FieldsPrimary Use Case
Listing core datazpid, id, price, unformattedPrice, address, addressStreet, addressCity, addressState, addressZipcode, beds, baths, area, homeType, lotAreaValue, lotAreaUnit, detailUrl, statusType, statusText, imgSrc, latLongListing discovery, mapping, market browsing
Valuation & price signalszestimate, rentZestimate, taxAssessedValue, priceReduction, datePriceChanged, daysOnZillowInvestment analysis, price monitoring
Listing lifecycle flagsisFSBO, isNewHome, isForAuction, isBankOwned, isOpenHouse, isComingSoonOpportunity filtering, distressed/off-market discovery
Raw Zillow metadatahdpData, carouselPhotosComposable, palsId, rawHomeStatusCd, marketingStatusSimplifiedCd, providerListingId, foundFromSearchUrl, zoomQuadrantSequence, scrapedAtAdvanced/custom parsing, audit trail

Zestimate, Rent Zestimate & price-drop data

This is the field group that separates this Actor from a plain Zillow search scraper. Zillow's own search-results response already attaches a valuation snapshot to every listing card — the automated Zestimate home-value estimate, an estimated monthly Rent Zestimate, the most recent tax-assessed value, and (when applicable) the dollar amount and date of a price drop — but that data sits nested inside each listing's hdpData.homeInfo block, which a plain search scraper typically leaves untouched. This Actor lifts those fields out to the top level of every row (zestimate, rentZestimate, taxAssessedValue, priceReduction, datePriceChanged) so you get a valuation and price-history signal for an entire search area in one run, without a second request to each property's detail page. Fields are null when Zillow's response genuinely has no value to disclose for that listing, never a fabricated number.

{
"zestimate": 994000,
"rentZestimate": 4200,
"taxAssessedValue": 812000,
"priceReduction": "$15,000",
"datePriceChanged": "2026-06-30T00:00:00+00:00"
}

Listing status & lifecycle flags

Every row also carries boolean lifecycle flags Zillow's search response marks per listing: isFSBO (for-sale-by-owner), isNewHome (new construction), isForAuction, isBankOwned (REO), isOpenHouse, and isComingSoon (not yet active), alongside daysOnZillow and statusType/statusText. These come from the same listing_sub_type block Zillow's API returns and let you filter a search-area pull down to a specific opportunity type — auction and bank-owned listings for distressed-property investors, or FSBO listings for buyers avoiding agent commissions — without a separate scrape.

Why not build this yourself?

Zillow retired its public search/listings API to third-party developers years ago, and there's no self-serve signup or API key that returns search-result listings today. Anyone who wants this data programmatically has to read the same JSON a browser gets — either the server-rendered __NEXT_DATA__ payload on a search page, or the async-create-search-page-state endpoint the site's own front end calls. This Actor's own source notes a live example of the maintenance burden: that search-state endpoint TLS/JA3-fingerprints every request, so a plain HTTP client gets a bare 403 from every proxy tier, and the working fix — impersonating a current Chrome/Safari TLS fingerprint via curl_cffi on a warmed, cookie-bearing session — has already needed updating once as Zillow's anti-bot layer caught up to an older fingerprint set. On top of that, large search areas are capped at a few hundred listings per single request, so pulling a whole metro means recursively splitting the map into smaller zones and deduplicating by zpid. Maintaining that pipeline — fingerprint rotation, proxy escalation, map-splitting logic, and a fallback to HTML pagination when the API is blocked outright — is the real alternative to using a maintained Actor.

What is the difference between a Zillow search scraper and a Zillow property detail scraper?

A Zillow search scraper collects many listings from one query — a city, ZIP code, or map area — and returns one row per property from the search-results page itself. A Zillow property detail scraper does the opposite: given a single ZPID, address, or listing URL, it opens that one property's own page and pulls everything only that page carries — full price and tax history over time, listing-agent and broker contact details, nearby school ratings, and walk/transit scores. The distinction matters because search results and detail pages are genuinely different Zillow endpoints with different data: this Actor's search response carries a valuation snapshot (current Zestimate, current tax-assessed value, the most recent price change) but not a multi-year price or tax history, and it carries no agent contact or school data at all, because none of that lives on a search-results card.

Zillow Search Scraper is a search scraper: built to discover and monitor many listings across an area in one run, with the valuation snapshot bundled in. If you need a full historical price/tax timeline, agent contacts, or school data for one specific property, that's a property-detail scraper's job, run against the zpid or detailUrl this Actor returns.

How to scrape Zillow with Zillow Search Scraper?

  1. Open Zillow Search Scraper on Apify (Scrapier/Zillow-Search-Scraper) and click Start.
  2. Add one or more entries to searchUrls — a full Zillow search URL (with searchQueryState, most accurate) or a plain location keyword like dallas-tx. No field is schema-required, but the Actor logs an error and exits immediately if searchUrls is empty.
  3. Pick an extractionMethod, set maxItems, and optionally set sortOrder (only takes effect on full search URLs — see the Input pitfall below) and delay.
  4. Configure proxyConfiguration if you expect a large or repeated run.
  5. Start the run and download results as JSON, CSV, or Excel from the dataset, or pull them via the Apify API.
{
"searchUrls": [
"https://www.zillow.com/dallas-tx/?category=SEMANTIC&searchQueryState=%7B%22isMapVisible%22%3Atrue%2C%22mapBounds%22%3A%7B%22north%22%3A33.10483509834637%2C%22south%22%3A32.53008985410089%2C%22east%22%3A-96.3544578671875%2C%22west%22%3A-97.2004051328125%7D%2C%22filterState%22%3A%7B%22sort%22%3A%7B%22value%22%3A%22globalrelevanceex%22%7D%7D%2C%22isListVisible%22%3Atrue%2C%22usersSearchTerm%22%3A%22Dallas%2C%20TX%22%2C%22category%22%3A%22cat1%22%2C%22regionSelection%22%3A%5B%7B%22regionId%22%3A38128%2C%22regionType%22%3A6%7D%5D%7D"
],
"extractionMethod": "PAGINATION_WITH_DYNAMIC_ZOOM_INCREASE",
"maxItems": 200,
"sortOrder": "priced_low",
"delay": 2
}

How to run multiple queries in one job

Add multiple entries to searchUrls in the same run — each can be a full search URL or a plain keyword, mixed freely. The Actor processes them in sequence, one search URL at a time, and every listing from every URL streams live into the same dataset as it's collected, tagged with foundFromSearchUrl so you can tell which query produced which row. There's no documented parallel-fetch setting across URLs; each URL runs to its own maxItems limit before the next one starts.

⬇️ Input

All 6 fields are optional in the schema — but the Actor exits immediately with a logged error if searchUrls is empty, since there's nothing to scrape.

ParameterRequiredTypeDescriptionExample Value
searchUrlsNoarrayZillow search URLs (with searchQueryState, most accurate) or location keywords (e.g. dallas-tx, new-york-ny). Supports bulk input.["dallas-tx"]
extractionMethodNostring (enum)PAGINATION_WITH_DYNAMIC_ZOOM_INCREASE (default) — splits large map areas into zones for full coverage. PAGINATION_WITHOUT_ZOOMING_IN — HTML pages, max ~820 results per search URL. MAP_MARKERS — map-pin endpoint, max ~500 results per search URL."PAGINATION_WITH_DYNAMIC_ZOOM_INCREASE"
maxItemsNointegerMaximum listings to collect per search URL (1–10,000). Default 10.200
sortOrderNostring (enum, nullable)Override sort order: globalrelevanceex (Homes for You), days (Newest), priced_low, priced_high, sqft, lot, beds, baths. Leave blank to keep each URL's own sort."priced_low"
delayNointegerSeconds to pause between requests (0–60). Default 2.2
proxyConfigurationNoobjectApify Proxy settings. Default is no proxy (prefill {"useApifyProxy": false}), with automatic datacenter → residential fallback if Zillow blocks a request.{"useApifyProxy": false}

Example JSON input

{
"searchUrls": [
"https://www.zillow.com/dallas-tx/?category=SEMANTIC&searchQueryState=%7B%22isMapVisible%22%3Atrue%2C%22mapBounds%22%3A%7B%22north%22%3A33.10483509834637%2C%22south%22%3A32.53008985410089%2C%22east%22%3A-96.3544578671875%2C%22west%22%3A-97.2004051328125%7D%2C%22filterState%22%3A%7B%22sort%22%3A%7B%22value%22%3A%22globalrelevanceex%22%7D%7D%2C%22isListVisible%22%3Atrue%2C%22usersSearchTerm%22%3A%22Dallas%2C%20TX%22%2C%22category%22%3A%22cat1%22%2C%22regionSelection%22%3A%5B%7B%22regionId%22%3A38128%2C%22regionType%22%3A6%7D%5D%7D",
"new-york-ny"
],
"extractionMethod": "PAGINATION_WITH_DYNAMIC_ZOOM_INCREASE",
"maxItems": 300,
"sortOrder": "priced_low",
"delay": 2,
"proxyConfiguration": { "useApifyProxy": false }
}

Pitfall: sortOrder only takes effect on full Zillow search URLs that already carry a searchQueryState parameter — the Actor edits filterState.sortSelection/sort inside that JSON blob before dispatching the request. A plain location keyword like dallas-tx has no searchQueryState to edit, so sortOrder is silently ignored on keyword input; use a full search URL copied from Zillow.com if you need guaranteed sort control.

⬆️ Output

Each run produces one dataset row per property listing, with a consistent schema across every run. Results can be exported as JSON, CSV, Excel, or XML, or fetched directly via the Apify API. Pricing is pay-per-event: each pushed row is billed once under the row_result charged event; a listing that fails to build is never pushed, so you're never charged for a row you didn't receive.

Scraped results

[
{
"zpid": "55826232",
"id": "55826232",
"rawHomeStatusCd": "ForSale",
"marketingStatusSimplifiedCd": "ForSale",
"providerListingId": "*APID55826232*",
"imgSrc": "https://photos.zillowstatic.com/fp/abc123-p_e.jpg",
"detailUrl": "https://www.zillow.com/homedetails/3810-Hawthorne-Ave-Dallas-TX-75219/55826232_zpid/",
"statusType": "FOR_SALE",
"statusText": "House for sale",
"price": "$1,000,000",
"unformattedPrice": 1000000,
"address": "3810 Hawthorne Ave, Dallas, TX 75219",
"addressStreet": "3810 Hawthorne Ave",
"addressCity": "Dallas",
"addressState": "TX",
"addressZipcode": "75219",
"beds": 3,
"baths": 3.0,
"area": 2622,
"latLong": { "latitude": 32.822216, "longitude": -96.811325 },
"homeType": "SINGLE_FAMILY",
"lotAreaValue": 0.19,
"lotAreaUnit": "acres",
"daysOnZillow": 12,
"zestimate": 994000,
"rentZestimate": 4200,
"taxAssessedValue": 812000,
"priceReduction": null,
"datePriceChanged": null,
"isFSBO": false,
"isNewHome": false,
"isForAuction": false,
"isBankOwned": false,
"isOpenHouse": false,
"isComingSoon": false,
"hdpData": { "...": "raw nested Zillow homeInfo payload" },
"carouselPhotosComposable": { "...": "photo carousel data" },
"foundFromSearchUrl": "https://www.zillow.com/dallas-tx/?...",
"zoomQuadrantSequence": [],
"scrapedAt": "2026-07-25T14:02:11.503284+00:00"
},
{
"zpid": "55831904",
"id": "55831904",
"rawHomeStatusCd": "ForSale",
"providerListingId": "*APID55831904*",
"imgSrc": "https://photos.zillowstatic.com/fp/def456-p_e.jpg",
"detailUrl": "https://www.zillow.com/homedetails/1220-Elm-St-Dallas-TX-75201/55831904_zpid/",
"statusType": "FOR_SALE",
"statusText": "House for sale",
"price": "$415,000",
"unformattedPrice": 415000,
"address": "1220 Elm St, Dallas, TX 75201",
"addressStreet": "1220 Elm St",
"addressCity": "Dallas",
"addressState": "TX",
"addressZipcode": "75201",
"beds": 2,
"baths": 2.0,
"area": 1340,
"latLong": { "latitude": 32.784, "longitude": -96.799 },
"homeType": "CONDO",
"lotAreaValue": null,
"lotAreaUnit": null,
"daysOnZillow": 41,
"zestimate": 432000,
"rentZestimate": 2350,
"taxAssessedValue": 398000,
"priceReduction": "$15,000",
"datePriceChanged": "2026-06-30T00:00:00+00:00",
"isFSBO": false,
"isNewHome": false,
"isForAuction": false,
"isBankOwned": false,
"isOpenHouse": true,
"isComingSoon": false,
"hdpData": { "...": "raw nested Zillow homeInfo payload" },
"carouselPhotosComposable": { "...": "photo carousel data" },
"foundFromSearchUrl": "https://www.zillow.com/dallas-tx/?...",
"zoomQuadrantSequence": [0, 2],
"scrapedAt": "2026-07-25T14:02:13.118732+00:00"
},
{
"zpid": "55840217",
"id": "55840217",
"rawHomeStatusCd": "ForSale",
"providerListingId": "*APID55840217*",
"imgSrc": "https://photos.zillowstatic.com/fp/ghi789-p_e.jpg",
"detailUrl": "https://www.zillow.com/homedetails/900-Ross-Ave-Dallas-TX-75202/55840217_zpid/",
"statusType": "FOR_SALE",
"statusText": "Bank owned",
"price": "$189,900",
"unformattedPrice": 189900,
"address": "900 Ross Ave, Dallas, TX 75202",
"addressStreet": "900 Ross Ave",
"addressCity": "Dallas",
"addressState": "TX",
"addressZipcode": "75202",
"beds": 3,
"baths": 1.0,
"area": 1180,
"latLong": { "latitude": 32.78, "longitude": -96.805 },
"homeType": "SINGLE_FAMILY",
"lotAreaValue": 0.14,
"lotAreaUnit": "acres",
"daysOnZillow": 96,
"zestimate": 205000,
"rentZestimate": 1650,
"taxAssessedValue": 176000,
"priceReduction": "$10,100",
"datePriceChanged": "2026-07-10T00:00:00+00:00",
"isFSBO": false,
"isNewHome": false,
"isForAuction": false,
"isBankOwned": true,
"isOpenHouse": false,
"isComingSoon": false,
"hdpData": { "...": "raw nested Zillow homeInfo payload" },
"carouselPhotosComposable": { "...": "photo carousel data" },
"foundFromSearchUrl": "https://www.zillow.com/dallas-tx/?...",
"zoomQuadrantSequence": [1],
"scrapedAt": "2026-07-25T14:02:15.774509+00:00"
}
]

How can I use the data extracted with Zillow Search Scraper?

  • Real estate investors: rank listings across an entire metro by zestimate, rentZestimate, and taxAssessedValue, and filter straight to isForAuction/isBankOwned rows to find distressed or off-market opportunities in one run.
  • Market trend trackers: run the same searchUrls entry on a schedule and track unformattedPrice, priceReduction, and daysOnZillow across the whole result set to see how a ZIP code or metro area is moving.
  • AI engineers and LLM developers: have an agent issue a searchUrls + sortOrder query, receive structured JSON back, and ground a property-search assistant's answer in real price, Zestimate, and status data instead of the model's own guess.
  • Home buyers and agents: filter a search-area pull straight to isOpenHouse or isComingSoon listings, or compare price against rentZestimate for quick rental-yield math before reaching out about a property.

How do you monitor price drops over time?

Price-drop monitoring means re-running the same search on a schedule and diffing specific fields between runs, rather than treating one run as a static snapshot. Between two runs of the same searchUrls entry, match listings by zpid and compare unformattedPrice, priceReduction, and datePriceChanged — a newly non-null priceReduction, or a datePriceChanged timestamp more recent than your last run, means that listing's price moved since you last checked. daysOnZillow climbing steadily on a listing that hasn't dropped in price yet can also flag a property likely to get a price cut soon. Comparing the set of zpid values between runs also surfaces brand-new listings (present in this run, absent from the last) and delistings (the reverse), without needing a separate "new listings" feature.

A practical workflow: schedule a run every few days for a fixed search URL or keyword set with a consistent sortOrder, export each run's dataset, and diff by zpid against the previous export. Alert when priceReduction goes from null to a value, or when unformattedPrice on a matched zpid decreases. Apify's built-in Schedules feature can trigger the run on that cadence; you then compare the new run's dataset against the previous one using the Apify API or your own pipeline, since the Actor doesn't retain history across runs itself — each run reflects Zillow's current search response only.

Integrate Zillow Search Scraper and automate your workflow

Zillow Search Scraper works with any language or tool that can send an HTTP request.

REST API with Python

import requests
TOKEN = "YOUR_APIFY_TOKEN"
ACTOR = "scrapier~zillow-search-scraper"
resp = requests.post(
f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items",
params={"token": TOKEN},
json={
"searchUrls": ["dallas-tx"],
"maxItems": 100,
"sortOrder": "priced_low",
},
timeout=300,
)
listings = resp.json()
for listing in listings[:5]:
print(listing["address"], listing["price"], listing["zestimate"])

Scheduled monitoring and delivery

Use Apify's built-in Schedules to trigger this Actor on a recurring cadence (e.g. every few days) for a fixed search URL or keyword set. There's no actor-specific webhook payload beyond Apify's standard run-finished webhooks; poll the Apify API for the new dataset, or configure an Apify webhook to notify your endpoint when the run completes, then pull run-sync-get-dataset-items from there.

Scraping publicly accessible Zillow search-result pages is generally legal — this Actor retrieves the same listing data any visitor sees on Zillow.com without logging in or bypassing any access control. The data returned here — prices, addresses, home specs, and Zillow's own valuation estimates — is business and product data about properties, not personal data about individuals, so it falls under Zillow's Terms of Service and general web-scraping norms rather than GDPR/CCPA-style personal-data regimes. Scraping for AI model training and scraping for operational monitoring (price tracking, market research) carry different risk profiles and different Terms-of-Service implications — review Zillow's current Terms of Service for your use case. Consult your legal team for commercial use cases involving bulk data storage.

Frequently asked questions

Does Zillow Search Scraper cover locations outside the United States?

No — Zillow.com only covers U.S. real estate, and the Actor has no separate country/locale parameter; coverage is whatever the searchUrls you supply resolve to on Zillow.com itself.

How do I sort results by price or newest listings?

Set sortOrder to one of the 8 supported values (priced_low, priced_high, days, sqft, lot, beds, baths, or globalrelevanceex). It only takes effect on full search URLs that carry a searchQueryState; leave it blank to keep a URL's own sort, or use a location keyword and accept Zillow's default relevance order.

How does Zillow Search Scraper handle Zillow's anti-bot measures?

It escalates automatically: requests start with no proxy, fall back to a datacenter proxy, then a residential proxy on repeated blocks, retrying with exponential backoff at each tier. For the API-based extraction methods, it also rotates Chrome/Safari TLS impersonation fingerprints on a warmed, cookie-persistent session, and automatically falls back to HTML-page pagination for a URL if the search API returns zero results.

Does Zillow Search Scraper extract Zestimate and price-drop data?

Yes — zestimate, rentZestimate, and taxAssessedValue carry Zillow's current valuation figures, and priceReduction/datePriceChanged carry the amount and date of the listing's most recent price change. These are null when Zillow's own response doesn't disclose a value for that listing.

How many results does it return per query?

Up to maxItems, which accepts 1–10,000 per search URL (default 10). The MAP_MARKERS method hard-caps at 500 results per search URL regardless of maxItems; PAGINATION_WITHOUT_ZOOMING_IN is documented at roughly 820 results per search URL, the ceiling Zillow's HTML pagination exposes. The default PAGINATION_WITH_DYNAMIC_ZOOM_INCREASE method can reach higher counts by splitting a large search area into smaller map zones.

How do I monitor price drops over time?

Schedule a recurring run for the same searchUrls entry, extract unformattedPrice, priceReduction, and datePriceChanged from each run's dataset, and diff by zpid against the previous run. Use Apify's Schedules feature to trigger the run, then compare datasets via the Apify API or your own pipeline.

Does it work with Claude, ChatGPT, and AI agent frameworks?

Yes — it's callable as a standard HTTP endpoint through the Apify API, so any agent framework that can make an HTTP request can call it, receive structured JSON back, and ground a property-search assistant's answer in real listing and valuation data before responding.

How does Zillow Search Scraper compare to other Zillow scrapers?

As observed on Apify Store listings on 2026-07-26, most other Zillow scrapers are built around a single property's detail page — accepting a URL, ZPID, or address and returning a full dossier (multi-year price/tax history, listing-agent contacts, school ratings). Zillow Search Scraper is built around the search-results query instead: it discovers many listings across a city, ZIP code, or map area in one run, with a current valuation snapshot bundled into each row rather than a per-property history.

Can I use it without managing proxies or a Zillow account?

Yes. Proxy tier escalation (none → datacenter → residential) is automatic; you only need to supply Apify Proxy access if you want to override the defaults via proxyConfiguration. No Zillow login or account is required at any point — the Actor reads the same public search pages a browser does.

Does this need a Zillow API key?

No. Zillow's public search API was retired to third-party developers years ago; this Actor reads the same search endpoint and pages Zillow.com's own website uses, so no API key or developer account is needed.

Your feedback

Found a bug or a field that doesn't match what's documented here? Let us know through the Actor's Issues tab on Apify or Scrapier's support contact — reports like this go straight into fixing the extractor. It helps us keep this README and the schema in sync.