Zillow Search Scraper — listings by city or ZIP avatar

Zillow Search Scraper — listings by city or ZIP

Pricing

from $0.90 / 1,000 listings

Go to Apify Store
Zillow Search Scraper — listings by city or ZIP

Zillow Search Scraper — listings by city or ZIP

Scrape Zillow listings for sale, for rent or recently sold by typing a city, ZIP or neighborhood — no search URL needed. Get price, beds, baths, sq ft, year built, Zestimate, tax value, photos and a link per listing, up to 1,000 per place. Export JSON, CSV or Excel, run via API or on a schedule.

Pricing

from $0.90 / 1,000 listings

Rating

0.0

(0)

Developer

Mu0i

Mu0i

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Scrape Zillow listings by typing a place the way you would into Zillow's search box — "Austin, TX", "78701", "Pacific Heights, San Francisco, CA", a county — choose for sale, for rent or recently sold, and get one clean row per listing: price, beds, baths, living area, lot, year built, home type, Zestimate, tax assessment, days on Zillow, open houses, photos and a link. No Zillow search URL to hand-craft, no browser, no proxy setup. Export to JSON, CSV, Excel or XML, run it on a schedule, call it from the API, or plug it into Make, Zapier, Google Sheets or an AI agent through Apify's integrations.

  • 🏠 Up to 1,000 listings per place — Zillow's own ceiling per search, walked to the last row. The website's search stops at 820.
  • 🔎 Real inputs — a place, a listing type, a sort. Not a searchQueryState URL you have to build.
  • 🏢 Rentals done right — apartment buildings come back as one row with the rent range and a per-bedroom breakdown, single homes as another; every row says which.
  • 📄 Full property record on request — tick one box and each listing carries its facts, price and tax history, schools, agent and photo gallery.
  • 💵 Pay per listing, never for a run that found nothing.

What Zillow data can you extract?

You typeYou get, per listing
A city, ZIP, neighborhood or county, plus for saleprice, price per sq ft, price changes, beds, baths, living area, lot size, home type, year built, listing flags (FSBO, open house, new construction…), days on Zillow, listed date, Zestimate and rent Zestimate, tax assessment, open houses, 3D tour / floor plan links, photos, link
… plus for rentthe same for single homes and units, and for apartment buildings: building name, rent range, base rent range, required monthly fees, unit count and a per-bedroom price breakdown
… plus recently soldthe sold price and sale date on top of the listing fields
… plus Add the full property recordeverything the Zillow Property Scraper returns, in a details field: 23 curated facts, price history, tax history, schools with ratings, listing agent and broker, MLS id, walk/transit/bike scores, nearby homes, description, engagement

How to scrape Zillow listings

  1. Click Try for free at the top of this page. Apify creates a free account if you do not have one — no card is needed to run this, and the free credit covers about 3,300 listings.
  2. Put the places to search into Places to search — one per line, as you would type them into Zillow.
  3. Pick the Listing type, set Max results per place to bound the run, then press Start. You are billed per row, so this field is also your budget.
  4. Rows show up in the Dataset tab while the run is still going. Export to JSON, CSV, Excel or XML from there, or read them over the API.

Tick Add the full property record to every row to get the facts, price and tax history, schools, agent and photo gallery for every listing in a details field — the same record the Zillow Property Scraper returns, without a second run.

What this Zillow scraper measured

Everything this Actor promises was measured on live Zillow data, and the numbers are the ones below — not round numbers from a brochure.

One search is at most 1,000 listings — Zillow's rule, not ours. Zillow's API rejects any page past 1,000 results and says so in its own error. Measured on Austin, TX (5,673 for sale): pages 1–25 at 40 per page all returned 40 rows with distinct leading zpids; page 26 and beyond is refused.

page 25 × 40 (row 1,000)40 rows, distinct
page 26 × 40refused — at most 1,000 results per search

So Max results per place is capped at 1,000, and the Actor walks to exactly there. For more in a big city, list its ZIP codes or neighborhoods as separate places — each is its own 1,000.

A rental search returns two shapes, and every row says which. Zillow counts an apartment building as one result, with a rent range and a per-bedroom breakdown, alongside single homes and units. Measured on ZIP 78704 for rent: 25 buildings and 15 homes in the first 40. Rows carry resultType: "home" or "rentalBuilding"; a building's fields (title, minPrice, maxPrice, units, unitCount) are null on a home and a home's (price, bedrooms, livingArea…) are null on a building, rather than the two being squeezed into one shape.

The sort orders are Zillow's own. Newest returned listings at 0 days on Zillow; Price: high to low led with $24.9M. Price: low to high puts Zillow's $0 "price on request" and auction listings first — that is the data as Zillow holds it, passed through with its + suffix (price: 0, priceSuffix: "+") rather than hidden.

Input

field
locationsrequiredOne place per line: city, ZIP, neighborhood or county, as typed into Zillow. Each is searched separately and tagged in the output
statusforSaleforSale (agent listings, FSBO, coming soon, new construction, auctions), forRent, or sold (recently sold)
maxResultsPerLocation100Up to 1,000 — Zillow's own ceiling per search
sortByrelevancerelevance, newest, price_desc, price_asc, most_bedrooms, largest, largest_lot, newest_built
enrichWithDetailsoffAttach the full property record to each row as details. Billed as property instead of result — see pricing

Every field, with its default — paste the whole block and delete what you do not need:

{
"locations": [
"Austin, TX"
],
"status": "forSale",
"maxResultsPerLocation": 100,
"sortBy": "relevance",
"enrichWithDetails": false
}

Output example

Every row carries location (the place it was found for), status and resultType, so several places can share one dataset.

Every row — zpid, url, street, city, state, zip, buildingId, lat, lng, currency, listingStatus, listedAt, photo, photoCount, photos[], has3DTour, hasVideo, isShowcase, isFeatured.

Homes (resultType: "home") — price, priceSuffix, pricePerSqft, priceChange, priceChangedAt, bedrooms, bathrooms, fullBathrooms, halfBathrooms, livingArea, lotSize, lotSizeUnit, propertyType, yearBuilt, marketingStatus, listingFlags[] (Zillow's own flags, verbatim — isFSBA, isOpenHouse, isNewConstruction…), daysOnZillow, lastSoldAt (sold only), zestimate, rentZestimate, taxAssessedValue, taxAssessmentYear, openHouses[], hasFloorPlan, virtualTourUrl, floorPlanUrl, isPreforeclosureAuction.

Rental buildings (resultType: "rentalBuilding") — title (the building's name), minPrice, maxPrice, minBaseRent, maxBaseRent, requiredMonthlyFees, unitCount, units[] (bedrooms, minPrice per bedroom count), groupType, isRoomForRent, isBuildToRent, acceptsApplications.

With full records — details, the same object the Zillow Property Scraper returns: facts (beds, baths, area, lot, year, type, parking, heating, cooling, appliances, HOA, tax…), priceHistory[], taxHistory[], schools[], agent, broker, mls, scores (walk/transit/bike), nearbyHomes[], photos[], description, engagement.

One home row, exactly as it lands in your dataset (photo list shortened):

{
"location": "78701",
"status": "forSale",
"resultType": "home",
"zpid": "83816995",
"url": "https://www.zillow.com/homedetails/83816995_zpid/",
"street": "805 Congress Ave",
"city": "Austin",
"state": "TX",
"zip": "78701",
"lat": 30.270214,
"lng": -97.74169,
"price": 18999000,
"pricePerSqft": 1218,
"priceChange": -901000,
"priceChangedAt": "2026-08-25T05:00:00.000Z",
"currency": "USD",
"bedrooms": 3,
"bathrooms": 8,
"fullBathrooms": 4,
"halfBathrooms": 4,
"livingArea": 15600,
"lotSize": 3920.4,
"lotSizeUnit": "squareFeet",
"propertyType": "singleFamily",
"yearBuilt": 1871,
"listingStatus": "forSale",
"marketingStatus": "active",
"listingFlags": ["isFSBA"],
"daysOnZillow": 131,
"listedAt": "2026-05-15T21:59:20.730Z",
"zestimate": null,
"rentZestimate": null,
"taxAssessedValue": 4768097,
"taxAssessmentYear": "2025",
"openHouses": [],
"photo": "https://photos.zillowstatic.com/fp/d298abe245ea58946a69a3b205ac2c06-p_f.jpg",
"photoCount": 39,
"photos": ["https://photos.zillowstatic.com/fp/d298abe245ea58946a69a3b205ac2c06-p_f.jpg", "…"],
"has3DTour": false,
"hasVideo": false,
"hasFloorPlan": false,
"isShowcase": false,
"isFeatured": false,
"isPreforeclosureAuction": false,
"note": null
}

And one rental building, from "Austin, TX" for rent:

{
"location": "Austin, TX",
"status": "forRent",
"resultType": "rentalBuilding",
"zpid": "446904283",
"url": "https://www.zillow.com/homedetails/446904283_zpid/",
"title": "Lookout",
"street": "13059 Four Star Blvd",
"city": "Austin",
"state": "TX",
"zip": "78737",
"minPrice": 1305,
"maxPrice": 2660,
"minBaseRent": 1249,
"maxBaseRent": 2604,
"requiredMonthlyFees": 56,
"unitCount": 46,
"units": [
{ "bedrooms": 1, "minPrice": 1305 },
{ "bedrooms": 2, "minPrice": 1875 },
{ "bedrooms": 3, "minPrice": 2640 }
],
"groupType": "apartmentComplex",
"listingStatus": "forRent",
"photoCount": 3,
"…": "home-only fields are null"
}

How much does it cost to scrape Zillow?

Pay per result — you are billed for rows you receive, not for time or compute.

EventPer 1,000What one unit is
result$1.50one listing row in the dataset
property$2.00one listing row carrying the full property record (enrichWithDetails on) — instead of result, never on top of it. A listing whose full record could not be fetched, or came back only partially, keeps its search fields and is charged as a result

Paid Apify plans pay less than this. The table above is the free-plan rate. Starter, Scale and Business each get a lower per-unit price — up to 40% off on Business — and the Actor's Pricing tab shows every tier before you run anything.

Is scraping Zillow free to try? Yes: Apify's free plan comes with monthly credit, enough for roughly 3,300 listings at the rate above, and no card is needed.

Starting a run costs $0.00001 — a cent per thousand runs — and there is no run minimum. You are never billed for a run that found nothing. If a place has no listings of that type, or Zillow does not know the place, the run says so in a row of the dataset — never a silently empty result, and never a charge. A run that fails because the data could not be reached fails loudly, because that one is worth retrying. Your run budget (maxTotalChargeUsd) is met exactly: the run delivers what fits and stops, never one row over.

Use cases

  • Market analysis — inventory, price per square foot and days on market by ZIP, refreshed on a schedule.
  • Investor and agent lead lists — FSBO listings, price cuts, long days-on-market, new construction — the listingFlags and priceChange fields are made for filters.
  • Rental comps — building rent ranges and per-bedroom prices across a city in one run.
  • Portfolio and watchlist monitoring — schedule the same places daily and diff the dataset.
  • Datasets for analysis and AI — clean, typed rows with stable field names; add the full record for training-grade detail.

Integrations and API

Every Actor is an API endpoint. This starts a run, waits for it, and returns the rows in one call — swap in your token from Settings → Integrations:

curl -X POST 'https://api.apify.com/v2/acts/mu0i~zillow-search-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{ "locations": ["Austin, TX"], "status": "forSale", "maxResultsPerLocation": 100 }'

From JavaScript with the Apify client:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('mu0i/zillow-search-scraper').call({
locations: ['Austin, TX'], status: 'forSale', maxResultsPerLocation: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

From Python:

from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('mu0i/zillow-search-scraper').call(run_input={
'locations': ['Austin, TX'], 'status': 'forSale', 'maxResultsPerLocation': 100,
})
items = client.dataset(run['defaultDatasetId']).list_items().items

Schedule it, get a webhook when a run finishes, or connect it to Make, Zapier, Google Sheets, Slack and the rest of Apify's integrations from the Actor's Integrations tab. Every Apify Actor is also a tool for AI agents through the Apify MCP server, so an assistant can search Zillow for you in a sentence.

Good to know

1,000 listings per place. Zillow's API refuses anything deeper (see above). The Actor stops there and says so in the log; it does not page in circles or pad the count. Split a big city into ZIPs or neighborhoods to go past it.

No price, beds or baths filter yet. Zillow's search body carries filter blocks that have not been measured end to end, and a filter that quietly does nothing is worse than no filter. Sort by price (either direction) and cut the list; filters will be added once each is confirmed to genuinely narrow the results.

Zestimates are Zillow's to withhold. zestimate and rentZestimate are null where Zillow's own search does not carry one — on many luxury and new-construction listings. The value is the API's, never estimated here.

url is Zillow's short canonical link (/homedetails/<zpid>_zpid/); it redirects to the full address URL. A sold home's price is the price on Zillow's sold card, and lastSoldAt is the sale date. Rows are deduplicated by zpid within a run, per place. Runs do not time out on you: if your run timeout is about to hit, the Actor stops ahead of it with everything collected so far and says so in the status message. A temporary problem on our side is retried for about half a minute, then per listing, before a row is given up on; rows already delivered always stand.

ActorWhat it does
Zillow Property Scraper — full record by zpidThe full record for any zpid or listing URL — facts, price and tax history, schools, agent, scores, photos. Works for off-market homes.

FAQ

Is it legal to scrape Zillow? This Actor reads listing data that is publicly available to any visitor without logging in, and scraping public data is generally lawful. What you do with it is your responsibility: comply with Zillow's terms where they apply to you, treat agent names and phone numbers as personal data under GDPR and CCPA, and do not republish listings wholesale. This is not legal advice.

Do I need a Zillow API key? No. Zillow's public API for listings has been closed for years; this Actor needs no key, no account and no proxy setup.

How many Zillow listings can I get? Up to 1,000 per place per run, which is Zillow's own ceiling per search. A city with more listings is split into its ZIP codes or neighborhoods, each its own 1,000.

How fresh is the data? Every run reads Zillow live at the moment it runs; nothing is cached between runs. Schedule the Actor to keep a dataset current.

Can I search by address, or get one home? Use the Zillow Property Scraper with the home's zpid or Zillow URL; it returns the full record, including for off-market homes.

Why is zestimate null on some rows? Zillow withholds it on some listings, notably luxury and new construction. The Actor never estimates a value itself.

What export formats are there? JSON, CSV, Excel and XML from the Dataset tab, or any of them over the API.

Something is wrong. Open a ticket in the Issues tab of this Actor with the run id from the run's URL; every run carries one, and it is enough to find exactly what happened.