Zillow Property Listings Scraper – For Sale, Rent & Sold avatar

Zillow Property Listings Scraper – For Sale, Rent & Sold

Pricing

from $1.00 / 1,000 listing row — basic data

Go to Apify Store
Zillow Property Listings Scraper – For Sale, Rent & Sold

Zillow Property Listings Scraper – For Sale, Rent & Sold

Scrape Zillow listings by city, ZIP, neighborhood, address or map area - for sale, for rent, sold, new construction and foreclosures. One row per home: price, beds, baths, sqft, lot, year built, Zestimate, MLS attribution, full-res photos, geo. Add price/tax history, schools, comps and climate risk.

Pricing

from $1.00 / 1,000 listing row — basic data

Rating

0.0

(0)

Developer

R.L.

R.L.

Maintained by Community

Actor stats

1

Bookmarked

16

Total users

3

Monthly active users

8 hours ago

Last modified

Categories

Share

Zillow Property Listings Scraper — For Sale, For Rent, Sold, Prices & Zestimates

Zillow Property Listings Scraper — Search Zillow real-estate listings by city, ZIP, neighborhood, address or map area, or look up full property detail by ZPID, via Zillow's private mobile API. Collect a whole metro, not just the 1,000 listings one Zillow search will return. Returns the maximum field set: all photos (full-res), price, tax assessment, the bed/bath breakdown, agent contact, Zestimate, geo and IDs. It reads Zillow's own API directly, so it's fast, reliable and complete — no flaky HTML parsing, no headless browser. Export to JSON, CSV, Excel, or an API.

Search any US city, ZIP, neighborhood or address — for-sale, for-rent and sold homes — or look up exact ZPIDs.


What does this Zillow scraper do?

You give it a location — Austin, TX, 90210, Downtown, Austin, TX — a map area, or a list of ZPIDs. (Zillow search-page URLs are not an input: one only ever reproduced 2 of the 11 filters it carried, so the filters are set explicitly instead.)

It returns one row per property: address, price, beds, baths, living area, lot size, year built, property type, listing status, MLS status, tax assessment, full-resolution photos, latitude/longitude and the ZPID.

Turn on Include details for the agent and broker contact, the Zestimate, the bathroom breakdown and the listing-type flags — those come from Zillow's property-detail endpoint, which is a second request and a second charge per property. A ZPID lookup always returns the full detail set, so it is always charged for it.

  • No code required — set your input, click Start, download your data.
  • Fast & accurate — reads Zillow's own private API and returns structured JSON, not scraped HTML.
  • Rich, structured data — every field below, clean and ready to use.
  • Gets past Zillow's 1,000-listing search cap — Deep search subdivides the area until every piece fits under it, so you can collect a whole metro instead of 13% of it. It turns itself on when you ask for more than 1,000 results.
  • Ask for just the slice you need — Modes return listings, full detail, one row per photo, agent contacts, or media/tour links, so you get a narrow dataset instead of sixty columns.
  • Transparent pay-per-result pricing — charged per property delivered, never per row, never for duplicates and never for the count requests that plan a split. Each mode is priced for what it returns; from 29 October 2026 there is no separate platform-usage charge and no run minimum. See How much does it cost?.
  • Export anywhere — JSON, CSV, Excel, XML, or pull it live via the Apify API.

What can you do with Zillow data?

  • Market research — price, price-per-sqft and days-on-market distributions across a city or ZIP.
  • Price & availability monitoring — re-run on a schedule and diff price, marketingStatus and priceChangeAmount.
  • Comparable sales (comps) — pull recentlySold listings around an address.
  • Rental analysis — pull forRent listings and compare asking rent against rentZestimate.
  • Agent and broker lead lists — agentName, agentBusinessName, agentPhone, brokerName.

Listing types (listingType) — pick one bucket per run:

listingType valueWhat you get
fsba,fsbo,newConstruction,comingSoon,auctionEverything currently for sale (the default for-sale mix)
forRentRentals
recentlySoldSold homes
foreclosure,preforeclosure,foreclosedForeclosures, pre-foreclosures and bank-owned

Property types (homeTypes) — leave empty for all, or pick any combination. These are the exact values the API accepts and the exact values that come back in the propertyType field:

homeTypes valueZillow's label
singleFamilyHouses
condoCondos
townhomeTownhomes
multiFamilyMulti-family / duplex
apartmentApartments
manufacturedManufactured homes
landLots & land

How do I collect a whole city? (the 1,000-listing cap)

One Zillow search returns at most 1,000 listings, however many match. Zillow stops paging at 25 pages of 40 and says nothing about it. Measured on 2026-10-15: an Austin-wide bounding box reports 7,720 matching listings and hands back 1,000 of them — 13% of the city. That is Zillow's limit, not this actor's, and no scraper can page past it in one search.

The way past it is to run several searches that each fit under the cap, and Deep search does that for you. It subdivides the area into tiles, checks each tile's match count, and splits again until every tile fits — then pages each one and merges the results.

You rarely have to set it. Left unset it turns itself on whenever you ask for more than one search can deliver — maxItems above 1,000, or 0 for every match. Set it explicitly to force it on or off.

Deep search only splits a tile it knows is over the cap. The count comes back on the search response before any rows do, so planning the split costs one count request per candidate tile and delivers no rows — count requests are never charged. maxTileDepth bounds how far it may subdivide (default 4), and a tile still over the cap at full depth is partitioned into price bands rather than quietly truncated. Measured on the beta build 2026-10-15: Austin-wide (6,496 matches) planned 151 searches at depth 4, six of which were dense downtown tiles still over the cap that went to price bands. Listings on a seam between two tiles are de-duplicated across the whole run, so you are never charged twice for the same property.

With deep search off, the actor still tells you when you have hit the cap: the run's status message and log say how many listings matched and how to collect the rest, so a short dataset is never mistaken for "that is all there is".


Can I get just the photos, or just the agent contacts?

Yes — that is what Mode is for. Every mode returns a different slice of the same data, so you get a narrow, ready-to-use dataset instead of sixty columns you have to cut down:

ModeWhat one row isColumns
Listingsone listingthe full search row — price, beds/baths, sqft, status, geo, tax, photos
Property detailone listingthe above plus facts, Zestimate, price-change, agent contact and tour links
Photosone photozpid, address, photoIndex, photoUrl, photoCount
Agent contactsone listingzpid, address, agentName, agentBusinessName, agentPhone, brokerName, listingStatus, price
Media & toursone listingzpid, address, photoCount, hasVideos, has3DTour, virtualTourUrl, floorPlanImageUrl

Each mode has its own view on the dataset's Output tab, so you see that mode's columns and not a wall of blanks from the others.

Floor plans come from Zillow's own 3D Home scan, so they appear on listings that have a 3D tour and not otherwise — the listing's hasFloorPlan flag is unreliable and reads false even when a plan exists. The field is floorPlanImageUrl, the floor-plan PNG — the artifact Zillow's own page renders. The API also reports a view-floor-plan viewer page, which is not returned, because every one of those URLs answers 404. A multi-floor home can have more than one plan — the row carries the first and the run log says so when there are more.

Photos returns the listing's own photo set. Spot-checked against zillow.com 2026-10-15: two resale listings matched the site's gallery exactly (40 and 38 photos). On a new-construction plan listing the site's gallery is larger than the listing's own set — 24 against 36 — because it also shows community and floor-plan imagery that belongs to the builder's community page rather than to the address.

Every mode works with a search or with zpids — pair Photos with a ZPID list to pull the photo sets of listings you already have. And every mode works with Deep search, so "every agent contact in Austin" is one run.

Billing is per property, not per row. A listing with 40 photos yields 40 photo rows and is charged once. Photos needs no per-property detail request at all — the photo set arrives on the search response — so it carries no detail-row surcharge. Property detail, Agent contacts and Media & tours do make that request, and are charged for it.

Tiling trades requests for coverage — the whole of Austin is ~7,700 rows instead of 1,000, spread over more searches and so more proxy traffic and compute. Set maxItems if you want a hard stop.


Market stats: one row per place, not per home

Set Mode to market and put cities, ZIPs or neighborhoods in Regions. Each place returns one row describing its market rather than any listing in it:

columnwhat it is
marketTempIndexZillow's own market-temperature index for the place
marketTempLabelwhat that index means in words — e.g. strong buyer
marketTempDescriptionZillow's paragraph explaining the label
medianDaysToPendinghow long a listing there typically takes to go under contract
region, regionId, useCodethe place as you asked for it, Zillow's id for it, and the property type the figures cover

Measured across US cities: median days to pending ran from 13 to 73 (Denver 31, Dallas 40, Houston 44, Austin 73), and Austin read marketTempIndex: 28.0, marketTempLabel: "strong buyer". Zillow's own batch endpoint is unreliable above about 20 regions — asked for 20 it returned 18, and asked for 25 it failed outright — which is why this mode asks one region per request instead of batching them.

Only one property use code returns the full picture. uc_sfrcondo (single-family plus condo, the default) returns both the temperature and the days-to-pending; uc_sfr returns the days only. Anything else Zillow answers with silence rather than an error, so the actor refuses an unsupported code by name instead of handing you an empty run to diagnose.

medianDaysToPending is published per city, so a ZIP-level request returns the market temperature and leaves that column null — measured: 78745 came back with marketTempIndex: 33 and no days-to-pending, while Austin, Denver and Miami had both. A place that returns neither is skipped rather than emitted as a row of nulls.

This mode ignores every listing filter — there is no listing to filter. It costs one request per place, it is billed per region row rather than per property, and maxItems counts places. A street address is skipped: a single home has no market of its own.

What are the price history, tax history, school and comp rows?

Turn on Include sections and each property brings its own history and context with it, as extra rows in the same dataset:

sectionone row percarries
priceHistoryprice change, listing or sale eventdate, event, price, price-change rate, price/sqft, and the MLS or agent that reported it
taxHistorytax yeartax paid, assessed value, and the year-on-year change in each
schoolsassigned schoolname, grades, level, GreatSchools rating, distance, enrollment, students per teacher, students per counselor, certified-teacher share, themed ratings, address
nearbyHomesnearby homethe 8 homes Zillow shows beside this one — address, price, beds, baths, area, status, MLS, agent
comparablescomparableZillow's own valuation comp set, same columns as nearby homes
offerStrengthprice pointZillow's modelled probability of winning at each price from 80% to 120% of list — 41 rows per property. For-sale only; new construction returns nothing
demandByPriceprice point × dayFor four candidate asking prices, a 30-day series of average page views, saves and shares for similar listings nearby — not this property's own traffic. Sold / off-market only, 120 rows per property
nearbyBuildingsbuildingThe neighbouring buildings — name, address, price range, bedroom groups ("2 bed from 2200+; 3 bed from 3300+") and minimum beds/baths. A rental's neighbours are buildings, so nearbyHomes answers the wrong question for an apartment. For-rent only

Four more sections add columns to the listing row rather than rows of their own, because each is one value per property:

sectioncolumns
climatefire, flood, air, wind and heat: risk score, label and source, plus the FEMA flood zone, bad-air days and the 98th-percentile summer temperature. An insurance recommendation comes with fire, flood and wind only — air and heat have no insurance product behind them, and Zillow returns nothing for them
neighborhoodWalk Score, Bike Score and Transit Score with their descriptions, the parent region, and the local LGBT / source-of-income / housing-voucher protection levels with Zillow's source page
provenanceMLS id and name, the disclaimer text, the must-display flags a redistributor has to honour, notMlsSourced, the buyer brokerage, the listing AOR, and when Zillow last checked and last updated the listing
engagementpage views, favourites and time on Zillow

All of them arrive in one request — with one exception, which is worth knowing before you pick. Zillow keeps rental detail in a different document from for-sale detail, and this Actor makes one request per property. So for a rental, nearbyBuildings and climate are mutually exclusive: ask for nearby buildings and you get the rental document (no climate); ask for climate and you get the for-sale document, which serves rentals fine but has no nearby-buildings list. Everything else is unaffected, and a for-sale listing never uses the rental document at all. That is why this is a checklist rather than eleven separate modes: selecting all eleven costs exactly what selecting one costs — one extra request per property, charged once. Measured on 2 properties with every section selected: 44 rows on new construction, 336 on sold homes — and 2 property rows plus 2 detail rows either way. The spread is because the offer curve (41 rows) is for-sale only and the demand series (120 rows) is sold-only, so no single listing can produce both.

Every child row carries a type column naming its section and the parent's zpid, so one export joins straight back to the listing. A nearby home or comparable keeps its own ID under relatedZpid, and a school keeps its own address — the property's address is deliberately not copied onto child rows, or the school's city would be overwritten with the property's.

Two honest caveats:

  • Which sections a listing can carry depends on what it is. Measured across three listings: a resale on the market filled climate 23/25, provenance 9/12 and engagement 3/3; new construction filled climate 0 (there is no history for a house that does not exist yet); and a sold home filled climate 0, provenance 0 and engagement 0, because Zillow's off-market detail does not carry them at all. Walk, bike and transit scores were the only group populated on all three. A Walk Score of 0 is a real score — it arrives with "Car-Dependent" and a live Walk Score link — not a missing value.
  • No agent email, and here is exactly how far that goes. Several competing Zillow scrapers advertise agent email addresses. For an ordinary listing Zillow's app API publishes none: no email-shaped value and no field named *mail* anywhere in the search or lookup payloads, and the property type has no agentEmail, email, listingAgentEmail, contactEmail or brokerEmail field at all. One exception exists — Zillow Showcase listings, a paid seller product, carry the agent's email — but that was 1 of 40 listings in a sampled page, so this Actor does not scrape it rather than ship a column that is empty on ~97% of rows. You get agentName, agentBusinessName, agentPhone, agentId, agentProfileUrl and brokerName, and on a sold listing the buyer brokerage where the MLS publishes it.
  • Three MLS-provenance columns are rare. attributionTitle, buyerBrokerageName and listingAor were empty across all 30 listings sampled in four listing kinds and three metros. They are kept rather than dropped because they are real fields that some MLS feeds do publish, and a buyer redistributing listings wants the AOR when it exists — but do not plan a pipeline around them.
  • On a sold listing, price is what it sold for — where the state publishes it. Measured: Denver CO sold listings carry price of $400,000 / $1,075,000 / $875,000 and Miami FL $680,000 / $775,000 / $875,500, while every Austin TX sold listing carries no price at all. Texas is a non-disclosure state, so sale prices are not public record and Zillow has none to give. lastSoldDate still populates there, and zestimate is the best available estimate of value. This is geography, not a gap in the scraper.
  • Rentals have a section of their own, nearbyBuildings, from Zillow's own rental detail document — and choosing it costs that rental its climate columns, because the two live in different documents and a property gets one request. See the note above the sections table.
  • A for-rent row fills fewer columns, and which ones depends on the record. Measured across 5 Austin rentals: 24 to 37 of the 76 listing columns populated, mean 31, against 37 on for-sale and 42 on sold. mlsStatus, yearBuilt and lotSize were null on all five — Zillow's rental search record carries no MLS/display-rules block. But taxAssessedValue (2/5), daysOnZillow (3/5), bathrooms (3/5) and livingArea (3/5) do populate, and the discriminator is the record kind: a building or complex listing has no unit-level facts, while an individual unit does. No listing agent either way (Zillow publishes none for rentals, so Agent contacts mode returns no rows and charges nothing for one). rentZestimate and rentalApplicationsAccepted, by contrast, only populate here.
  • A section can be legitimately empty. New construction has no tax history, because the house does not exist yet; a sold home has no asking price. An empty section is Zillow having nothing, not a failure — and if the request itself fails for a property, the run says so and that property is not charged for sections.
  • taxYear is a year, not a date. Zillow sends the tax record as a timestamp, but all of a property's rows carry the same time-of-day as your request, stepped back one year each — so only the year is real, and that is what is published rather than an invented day.

What do Zillow's own terms mean?

  • ZPID — Zillow's permanent numeric ID for a property, the number in a .../12345678_zpid/ URL. Stable across relistings, which makes it the right key to join runs on.
  • Zestimate — Zillow's estimated market value, computed by Zillow, not an asking price and not an appraisal. rentZestimate is the same idea for monthly rent. Both are only populated when Include details is on, and Zillow does not publish one for every property.
  • FSBA vs FSBO — for sale by agent (isFsba) vs for sale by owner (isFsbo).
  • The three status fields — listingStatus is the API bucket (forSale, forRent, recentlySold, other); marketingStatus is the lifecycle (active, comingSoon, offMarket, closed); trueStatus is the MLS-side status string, e.g. Active, Active Under Contract. They can disagree, and the MLS one is the most precise when present.
  • Contingent / pending — contingentListingType and isPending mark a listing that is under contract but not closed.
  • Days on Zillow — daysOnZillow counts days since the listing appeared on Zillow, not days since it hit the MLS.
  • Bathroom breakdown — bathroomsFull, bathroomsThreeQuarter, bathroomsHalf, bathroomsOneQuarter, bathroomsPartial and bathroomsFloat (the decimal total). Plain columns, only populated when Include details is on. Zillow's wider facts & features (appliances, heating, parking, HOA) are not available: the app's property-detail query is pinned to a fixed field set by Zillow's persisted-query safelist, so asking for one more field is refused outright rather than answered.
  • Tax assessed value — the county's assessment in taxAssessmentYear. It is not a market price and is often years stale.

How do I scrape Zillow listings? (4 steps)

  1. Set your input — a Location (q), a bounding box, or a list of ZPIDs.
  2. Pick a mode — Listings, Property detail, Photos, Agent contacts or Media & tours, depending on which slice of the data you want.
  3. Pick options — listing type, home types, price/beds/baths minimums, Include details, and a maxItems cap. Keep the default Residential (US) proxy.
  4. Run & export — click Start, then download the dataset as JSON/CSV/Excel or fetch it via the API.

What input does it take?

FieldTypeDefaultDescription
modeenumsearchWhich slice of the data to return: search (listing rows), detail (full per-property detail), photos (one row per photo), agent (listing-agent contacts), media (tour / floor-plan / 3D links), market (one row per city/ZIP — see Market stats).
regionsarray of string–Market stats mode only: the cities, ZIPs or neighborhoods to report on, one row each. Ignored by every other mode.
qstringAustin, TXCity, ZIP, neighborhood or street address. The main search input, resolved to a map area automatically. Zillow search-page URLs are not accepted — see the FAQ.
zpidsarray of string–Look up full detail for these Zillow property IDs. If set, the location/search inputs are ignored. The input for detail mode.
northstring–Advanced: raw bounding-box north latitude (e.g. 30.35). Optional power-user override — supply all four of north/south/east/west to search an exact map box instead of a Location.
southstring–Advanced: raw bounding-box south latitude (e.g. 30.20). Optional power-user override; used only when all four of north/south/east/west are given.
eaststring–Advanced: raw bounding-box east longitude (e.g. -97.70). Optional power-user override; used only when all four of north/south/east/west are given.
weststring–Advanced: raw bounding-box west longitude (e.g. -97.85). Optional power-user override; used only when all four of north/south/east/west are given.
priceMininteger–Minimum price (USD)
priceMaxinteger–Maximum price (USD)
bedsMininteger–Minimum bedrooms
bathsMininteger–Minimum bathrooms
bedsMaxinteger–Maximum bedrooms
bathsMaxinteger–Maximum bathrooms
sqftMininteger–Minimum finished living area, in square feet
sqftMaxinteger–Maximum finished living area, in square feet
lotSizeMininteger–Minimum lot size in square feet, not acres (one acre is 43,560 sqft)
lotSizeMaxinteger–Maximum lot size in square feet
yearBuiltMininteger–Earliest year built, inclusive
yearBuiltMaxinteger–Latest year built, inclusive
daysOnZillowMaxinteger–Only listings new to Zillow within this many days. On Austin, 1 day matched 107 listings against 6,511 unfiltered; 7 days matched 394
keywordsstring–Free-text keyword search over the listing, as Zillow's own keyword box does it — pool, casita, no HOA. A word Zillow does not index matches nothing, and the run says so
sortByenumdaysOnOrder Zillow returns listings in: daysOn (newest), price, beds, baths, livingArea, lotArea, yearBuilt, recentlyChanged, featured, relevance. Plus rentalPriorityScore, rentalDays, rentalPriorityScoreCandidate — for-rent searches only
sortDescendingbooleanfalseReverse the sort. relevance ignores it, and price ascending puts listings with no published price (auctions) first
includeSectionsenum array[]Extra per-property data, eleven to choose from. Child rows: priceHistory, taxHistory, schools, nearbyHomes, comparables, offerStrength, demandByPrice. Extra columns: climate, neighborhood, provenance, engagement. All eleven come from one Zillow request, so picking eleven costs the same as picking one — see sections
regionsarray of string–Market stats mode only: the cities or ZIPs to report on
useCodesenum array["uc_sfrcondo"]Market stats mode only: which property types the figures cover. Each type is its own request and its own row
includeDetailbooleanfalseEnrich each search result with the bed/bath breakdown, agent contact, Zestimate and listing-type flags from the GraphQL HDP endpoint (slower, one extra request per property).
maxItemsinteger100Cap on properties (0 = all matches) — the same unit you are billed in. In photos mode one property is several rows, so maxItems: 20 returns 20 listings' photos, not 20 photos.
deepSearchbooleanautoSplit the search to get past Zillow's 1,000-listing cap. Unset, it turns on when maxItems is 0 or above 1,000.
maxTileDepthinteger4How many times deep search may halve the area. Each level quadruples the tile count and every candidate tile costs one free, row-less count request. Austin-wide used all 4 levels and still had 6 tiles needing a price-band split.
listingTypeenum–What kind of listings to search for — one of fsba,fsbo,newConstruction,comingSoon,auction, forRent, recentlySold, foreclosure,preforeclosure,foreclosed
homeTypesenum array[]Restrict results to these property types. Leave empty for all types. Values: singleFamily, multiFamily, condo, townhome, apartment, manufactured, land
proxyConfigurationobjectApify ProxyRoute requests through a proxy. Residential (US) is the default and recommended — Zillow's PerimeterX blocks datacenter IPs.

Example input

{
"mode": "search",
"q": "Austin, TX",
"deepSearch": true,
"maxTileDepth": 4,
"zpids": [],
"includeDetail": false,
"maxItems": 100,
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": [
"RESIDENTIAL"
]
}
}

What does a row look like?

Each result is a JSON object like this:

{
"zpid": 454188962,
"url": "https://www.zillow.com/homedetails/454188962_zpid/",
"streetAddress": "Madison-321 Plan, The San Francisco Shipyard : Madison",
"city": "San Francisco",
"state": "CA",
"zipcode": "94124",
"price": 554000,
"currency": "usd",
"pricePerSquareFoot": 773,
"bedrooms": 1,
"bathrooms": 1,
"livingArea": 717,
"lotSize": null,
"lotSizeUnit": null,
"yearBuilt": 2026,
"propertyType": "condo",
"listingStatus": "forSale",
"marketingStatus": "active",
"daysOnZillow": 0,
"latitude": 37.7281,
"longitude": -122.3706,
"brokerName": null,
"agentName": null,
"agentBusinessName": null,
"agentPhone": null,
"taxAssessedValue": null,
"taxAssessmentYear": null,
"photoCount": 13,
"photos": [
"https://photos.zillowstatic.com/fp/d90d5a51db965cc028d2d54e6a887bb4-p_f.jpg",
"https://photos.zillowstatic.com/fp/8a8bf73710dc2133ccc047446ce33a58-p_f.jpg",
"…+11 more"
],
"hasVideos": false,
"has3DTour": true,
"bathroomsFloat": 1,
"bathroomsFull": 1,
"bathroomsThreeQuarter": 0,
"bathroomsHalf": 0,
"bathroomsOneQuarter": null,
"bathroomsPartial": null,
"homeStatusText": "Active",
"trueStatus": "Active",
"contingentListingType": null,
"newConstructionType": null,
"isZillowOwned": false,
"timeZone": "America/Chicago",
"zestimate": 409100,
"rentZestimate": null,
"priceChange": null,
"virtualTourUrl": null,
"floorPlanImageUrl": null,
"isFsba": true,
"isFsbo": false,
"isPending": false,
"isNewHome": false,
"isForeclosure": false,
"isComingSoon": false,
"isBankOwned": false,
"isForAuction": false,
"isOpenHouse": true
}

Which fields are in the dataset?

FieldTypeDescription
zpidnumberZPID
urllinkURL
streetAddresstextStreet address
citytextCity
statetextState
zipcodetextZIP
pricenumberPrice
currencytextCurrency
pricePerSquareFootnumberPrice / sqft
priceChangeAmountnumberPrice change ($, signed) — negative is a cut, positive a rise
priceChangeDatetextDate the price last changed (YYYY-MM-DD)
bedroomsnumberBeds
bathroomsnumberBaths
livingAreanumberLiving area (sqft)
lotSizenumberLot size
lotSizeUnittextLot size unit
yearBuiltnumberYear built
propertyTypetextProperty type
listingStatustextListing status
marketingStatustextMarketing status
daysOnZillownumberDays on Zillow
latitudenumberLatitude
longitudenumberLongitude
brokerNametextBroker
agentNametextAgent
agentBusinessNametextAgent business
agentPhonetextAgent phone
agentIdtextZillow's stable id for the agent, so rows from different runs join on the person
agentProfileUrllinkThe agent's Zillow profile page
taxAssessedValuenumberTax assessed value
taxAssessmentYeartextTax year
photoCountnumberPhoto count
photosarrayPhotos (full-res)
hasVideostextHas videos
has3DTourtextHas 3D tour
bathroomsFloatnumberBathrooms as a decimal
bathroomsFullnumberFull bathrooms
bathroomsThreeQuarternumberThree-quarter bathrooms
bathroomsHalfnumberHalf bathrooms
bathroomsOneQuarternumberQuarter bathrooms
bathroomsPartialnumberPartial bathrooms
homeStatusTexttextStatus (display)
trueStatustextMLS status
contingentListingTypetextContingent type
newConstructionTypetextNew construction type
isZillowOwnedbooleanZillow owned
timeZonetextTime zone
zestimatenumberZestimate
rentZestimatenumberRent Zestimate
priceChangetextPrice change
virtualTourUrllinkVirtual tour
floorPlanImageUrllinkFloor-plan image (PNG)
isFsbabooleanFor sale by agent
isFsbobooleanFor sale by owner
isPendingbooleanPending
isNewHomebooleanNew home
isForeclosurebooleanForeclosure
isComingSoonbooleanComing soon
isBankOwnedbooleanBank owned
isForAuctionbooleanFor auction
isOpenHousebooleanOpen house
countrytextCountry
hdpVarianttextListing variant — Zillow's own FOR_SALE / NEW_CONSTRUCTION / OFF_MARKET
lastSoldDatetextLast sold date (ISO-8601)
listedOnZillowAttextFirst listed on Zillow (ISO-8601 UTC)
mlsStatustextMLS status, as the MLS itself reports it
mlsProviderLogoUrllinkMLS provider logo, for attribution
canShowAddressbooleanWhether the feed permits showing the exact address
canShowOnMapbooleanWhether the feed permits showing the pin
listingCategorytextZillow's listing category
subdivisiontextSubdivision / community name
subdivisionIdnumberSubdivision ID
isFeaturedbooleanFeatured listing
isShowcaseListingbooleanZillow Showcase listing
isPreforeclosureAuctionbooleanPre-foreclosure auction
isUnmappablebooleanZillow could not place it on the map
hasFloorPlanbooleanA floor plan exists for this listing
rentalApplicationsAcceptedbooleanRentals only — whether applications are open. null on sale listings

zestimate, rentZestimate, the bathrooms* breakdown, priceChange, virtualTourUrl, floorPlanImageUrl, trueStatus, contingentListingType, homeStatusText, newConstructionType, isZillowOwned, the five agent* columns and the is* listing-type flags come from the property-detail endpoint, so they are populated when Include details is on and on ZPID lookups (which always return the full detail set). timeZone no longer needs it — the search record carries one of its own.

Two of these columns are legitimately empty rather than missing. mlsStatus and mlsProviderLogoUrl only exist where Zillow sourced the listing from an MLS, which was 1 of 3 listings sampled — a new-construction listing straight from the builder has no MLS record to report. And rentalApplicationsAccepted is null on every sale listing deliberately: Zillow sends false there, which would read as "applications are closed" for a house that was never for rent.

Every column above is a flat, deliberately mapped value — the dataset carries no nested API payload, so CSV and Excel exports are usable as-is. If you need a Zillow field this table does not cover, open an issue rather than digging through a raw blob: the fields this endpoint can actually return are tracked in issue #8.


How much does it cost?

This actor uses pay-per-event pricing — you are charged per property delivered, not per run, not per hour, and not per row.

From 29 October 2026

Each mode is priced for what it returns. Prices are per 1,000 properties, and tier is your own Apify plan:

ModeEvents chargedFreeBronzeSilverGold+
Listingsproperty-row$2.66$2.00$1.84$1.69
Photos+ photo-row$3.33$2.50$2.30$2.12
Media & tours+ media-row$3.99$3.00$2.76$2.54
Property detail+ detail-row$5.32$4.00$3.68$3.39
Agent contacts+ agent-row$6.65$5.00$4.60$4.23

No platform-usage charge and no run minimum from that date: the event price is the whole price, so you pay only for the properties the run delivers.

Until then

$1.00 per 1,000 property rows and $0.50 per 1,000 detail rows, with Apify platform usage (compute, proxy traffic, data transfer) billed on top out of your own plan, and a $0.10 minimum run charge.

what you runbilled
Listingsproperty row only
Property detail, or Include details on any modeproperty row + detail row
Extra data (any of the eleven sections)property row + detail row — one detail row per property however many sections you pick, because they all come from the same single request
Agent contacts, Media & toursproperty row + detail row
Photosproperty row only — the photos are already in the search response, so there is no second request to pay for
Market statsone row per place, billed as a row. market-row is not priced yet, so it falls back to the property-row price
A ZPID lookup (zpids)property row + detail row — always. The lookup returns the full detail set whether or not Include details is on, so it is always charged for it

What is and is not charged

Charging is per property, not per row — a listing with 40 photos yields 40 rows in Photos mode and is charged once. Four things are never charged:

  • count requests, which plan a Deep-search split and return no rows;
  • duplicate properties, seen on a seam between two tiles;
  • a property that returns nothing in the chosen mode — a listing with no photos in Photos mode;
  • the mode surcharge on a row whose detail call failed, since billing follows the data that actually arrived, not the option you ticked.

maxItems caps properties, so it bounds the bill before you start. If a run hits your maximum cost setting it stops and says so in its status message rather than ending quietly short.


How do I run it from my own code?

Run it programmatically with the Apify API:

curl -X POST "https://api.apify.com/v2/acts/zillow-api-scraper/runs?token=YOUR_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"mode": "search",
"q": "Austin, TX",
"deepSearch": true,
"maxTileDepth": 4,
"zpids": [],
"includeDetail": false,
"maxItems": 100,
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": [
"RESIDENTIAL"
]
}
}'

Or with the Apify CLI: apify call zillow-api-scraper -i input.json -o.


FAQ

Can I paste a Zillow search URL?

No, and deliberately. A Zillow search-page URL carries its filters in a searchQueryState blob that the web app rewrites whenever it ships a redesign. This Actor used to accept those URLs and reproduced only 2 of the 11 filters a realistic one carries — price and beds — silently dropping sqft, year built, lot size, HOA, garage, pool, days-on-Zillow, keywords and sort. The run succeeded, returned plenty of rows, and they were the wrong rows.

Set the filters explicitly instead: q for the location, plus listingType, homeTypes, priceMin/priceMax, bedsMin and bathsMin. To reproduce a panned map use the bounding box (north/south/east/west), and to fetch specific listings use zpids. A run that still passes searchUrls now stops and says so rather than searching somewhere else.

Do I need a Zillow account or API key?

No. The actor carries the Zillow Android app's own credentials and calls Zillow's mobile API for you — all you need is an Apify account.

Do I need a residential proxy?

Yes, and it is the default. Zillow fronts its endpoints with PerimeterX, which blocks datacenter IPs, so the input ships with Apify Proxy set to Residential, country US. Leave it alone unless you are supplying your own US residential pool.

How do I search a whole city, a ZIP or one exact address?

All three go in the Location field. A city or ZIP resolves to Zillow's own map region and is paginated 40 listings at a time. A full street address resolves to that one property, and the actor looks that property up directly instead of searching its neighbourhood — so you get the home you asked for, not its block.

Zillow's search box matches its own record of an address, and its idea of which city a property is in is not always the Post Office's — 7945 Lasalle Blvd is filed under Hollywood FL, not Miramar FL. So the actor also retries the street line on its own, with the city and ZIP cut off, and still checks the house number, street, ZIP and state of whatever comes back before accepting it. If an address does not resolve at all, the run finishes with a message saying so: check the street spelling, or search the ZIP code instead.

Can I scrape sold homes and rentals, not just for-sale?

Yes. Set Listing type to recentlySold for sold homes, forRent for rentals, or the foreclosure bucket for foreclosures, pre-foreclosures and bank-owned. Zillow search-page URLs are not an input — see the FAQ above for why, and use q plus the filters instead.

What does "Include details" actually add?

One extra request per property against Zillow's property-detail endpoint, which fills in the Zestimate, the rent Zestimate, the bathroom breakdown, price-change info, virtual-tour and floor-plan URLs, the MLS status, and the isFsba/isFsbo/isPending/isForeclosure-style flags. It is slower and billed as a second event per row, so leave it off if the search fields are enough.

Can I schedule it and only pick up what changed?

Yes — run it on Apify's scheduler and join runs on zpid, which is stable across relistings. daysOnZillow, marketingStatus and priceChangeAmount are the fields to diff (priceChangeAmount is signed, so a cut and a rise are distinguishable; the priceChange column beside it is Zillow's own display label and carries no direction). Results come back sorted by days on Zillow ascending, so the newest listings arrive first.

How is the cost bounded?

Two dials and a backstop. maxItems is the hard ceiling on properties for a run (0 = every match), which is the unit you are charged in, and the mode decides whether a property carries a surcharge at all. Together those bound the bill before you start — and from 29 October 2026 the event price is the whole price, with no platform-usage charge on top and no run minimum. maxTileDepth bounds how many requests a Deep search makes. The backstop is the run's own max total charge setting: when a run hits it the actor stops and says so in the status message, rather than exiting SUCCEEDED with a short dataset that looks like the end of the results.

Is scraping this legal?

You are responsible for how you use the data. Scrape only public real-estate listing data and comply with Zillow.com's Terms and applicable law (e.g. GDPR/CCPA for personal data).


How does it work under the hood?

The actor impersonates Zillow's official Android app and calls the same private endpoints the app does — mobile search for listings, mobile lookup for ZPIDs, and Zillow's GraphQL property-detail query for the extras — then normalises each response into the flat record above. Location strings go through the app's own autocomplete to get a region ID and viewport, which is cached for the run so a paginated job resolves once. No browser, no HTML scraping — just clean, structured data.


Good to know

  • Scrape only public real-estate listing data and respect Zillow.com's Terms of Service and robots policy.
  • For personal data, comply with GDPR/CCPA and applicable law — you are the data controller.
  • Fields can be null when Zillow doesn't expose them for a given record.

Global real estate toolkit

Part of the Global real estate toolkit — real-estate listing data across major US, European, and Australian property portals:

  • Redfin API Scraper — Scrape Redfin listings — address, price, beds/baths, sqft, status, agent.
  • realestate.com.au API Scraper — Scrape realestate.com.au property listings (buy/rent/sold) with full detail, media, and agent contact.
  • ImmoScout24 API Scraper — Scrape ImmoScout24 real-estate listings (Germany) with full detail, images, and agent contact.

Found this useful?

Give the Zillow Property Listings Scraper a star on Apify and check out my other apify-*-api-scraper actors.