Zillow Property Listings Scraper
Pricing
from $1.00 / 1,000 results
Zillow Property Listings Scraper
Scrape Zillow search results by city, region or ZIP: about 41 listings per location with zpid, numeric price plus the formatted priceText, beds, baths, areaSqft, address split into street, city, state and zipcode, status, brokerName, photo and exact coordinates. For sale, for rent or sold. No login.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
String
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
14 days ago
Last modified
Categories
Share
What does the Zillow Property Listings Scraper do?
Give this Actor a list of US locations — Austin, TX, Denver, CO, a ZIP code, a neighbourhood
name — and it returns the listings on Zillow's search results for each one as flat rows. One Zillow
request per location, around 41 listings back from each. Prices, bed and bath counts, floor area and
coordinates come back as numbers rather than as display strings, because the Actor reads the
__NEXT_DATA__ blob Zillow's own page hydrates from instead of parsing the rendered cards.
- For-sale, for-rent and recently sold inventory from the same input, switched with
listingType - Numeric
pricealongside Zillow's formattedpriceText, so you can sort without cleaning first latitudeandlongitudeon every row, ready to plot without a geocoding stepzpidon every row, which is Zillow's stable listing key and the input the Zillow Property Detail Scraper takes- No Zillow account, cookie or API key anywhere in the run
This is the search-sweep half of a pair. It finds listings across a market but returns what a search
result card carries. For one home's full record — year built, lot size, home type and the listing
text — feed the zpid values it produces into the
Zillow Property Detail Scraper.
What data does it extract?
| Field | Type | Description |
|---|---|---|
zpid | string | Zillow's own listing ID, stable across runs. Use it to de-duplicate and as detail-scraper input |
address | string | The full one-line address as Zillow writes it |
street | string | Street line on its own |
city | string | City |
state | string | Two-letter state code |
zipcode | string | ZIP code |
price | number | 500000, not "$500,000" |
priceText | string | The formatted price exactly as the card shows it, including /mo on rentals |
beds | number | Bedroom count |
baths | number | Bathroom count |
areaSqft | number | Floor area in square feet |
status | string | Zillow's own status type, e.g. FOR_SALE, FOR_RENT, SOLD |
latitude | number | Decimal degrees |
longitude | number | Decimal degrees |
brokerName | string | Listing broker, where the card names one |
detailUrl | string | Link to the property's Zillow page |
imageUrl | string | Primary listing photo |
searchLocation | string | The location string from your input that produced this row |
listingType | string | for_sale, for_rent or sold, echoed back from the input |
sourceUrl | string | The Zillow search URL the row was read from |
collectedAt | string | ISO timestamp of the fetch |
Any value Zillow does not publish on a card arrives as null rather than as an empty string or a
zero, so a missing bath count is never mistaken for a studio.
Why scrape Zillow?
- Comps and pricing. Pull sold rows for a ZIP, keep
price,beds,bathsandareaSqft, and you have a price-per-square-foot distribution for the submarket in one run. - Rental yield. Run the same location twice, once as
for_saleand once asfor_rent, and compare asking rents against asking prices on comparable bed counts. - Agent and iBuyer lead lists.
brokerNameplus the address fields is enough to rank brokers by active inventory in a market. - Maps and dashboards. Coordinates are on the row already, so inventory goes onto a map without a geocoding bill.
- Days-on-market and price-cut tracking. Schedule the Actor on a market, key rows by
zpid, and diffpricebetween runs.
How to use it
- Open the Actor and click Try for free, or add it to your Apify account.
- Put your markets in the Locations field, one per line.
Austin, TXand78704both work. - Pick a Listing type. Leave it at
for_sale, or switch tofor_rentorsold. - Set Maximum results if you want a hard ceiling on the dataset, and raise Concurrency if you are running many locations and want them fetched in parallel.
- Click Start. Each location is one fetch, so a ten-location run finishes in seconds.
- Take the rows from the Dataset tab, or export them as JSON, CSV or Excel. The run's
SUMMARYrecord in the key-value store lists any location that failed, with its error.
To keep a market file current, save the input as a task and schedule it. Rows carry collectedAt,
so successive runs stack into a time series without any extra bookkeeping.
Input
| Field | Type | Default | Description |
|---|---|---|---|
locations | array of strings | required | Cities, regions or ZIP codes, e.g. "Austin, TX". Between 1 and 100 entries. Duplicates collapse to one fetch |
listingType | string | for_sale | One of for_sale, for_rent, sold |
maxItems | integer | 1000 | Ceiling on dataset rows, 1 to 50000 |
concurrency | integer | 2 | Locations fetched in parallel, 1 to 5 |
{"locations": ["Austin, TX", "Denver, CO", "78704"],"listingType": "for_sale","maxItems": 1000,"concurrency": 2}
Output
One row per listing. The shape below is illustrative — it shows the fields and their types, not a captured run.
{"zpid": "29502073","address": "6310 Needham Ln, Austin, TX 78739","street": "6310 Needham Ln","city": "Austin","state": "TX","zipcode": "78739","price": 1200000,"priceText": "$1,200,000","beds": 5,"baths": 4,"areaSqft": 3496,"status": "FOR_SALE","latitude": 30.192585,"longitude": -97.89119,"brokerName": "Compass RE Texas, LLC","detailUrl": "https://www.zillow.com/homedetails/6310-Needham-Ln-Austin-TX-78739/29502073_zpid/","imageUrl": "https://photos.zillowstatic.com/fp/a1b2c3d4e5f60718293a4b5c6d7e8f90-p_e.jpg","searchLocation": "Austin, TX","listingType": "for_sale","sourceUrl": "https://www.zillow.com/homes/for_sale/Austin,-TX_rb/","collectedAt": "2026-08-21T09:14:03.512Z"}
Reliability
Measured across 30 US cities in one run: 30 of 30 locations succeeded and 1,230 listings came back, with every field in the schema populated by at least some rows. No column is always empty.
Reading the page's own state blob is what makes that possible. The rendered Zillow search page is around 650KB, well past what a model-backed extractor will accept, and the numbers it shows are formatted for display. The blob carries the same results with prices and areas already as integers.
A location that cannot be read is recorded in the run's SUMMARY under failures rather than
quietly returning fewer rows, and a run in which every location failed exits with an error instead
of reporting success on an empty dataset.
There are no retries by design. The String Unblocker behind this Actor owns proxy rotation, session reuse and anti-bot solver selection, so a retry loop out here would re-roll the same block and spend your budget doing it.
Limitations
One search page per location, so this samples a market rather than crawling it end to end. Widen
coverage by listing more ZIP codes rather than expecting deeper paging from a single city. Detail
pages are not opened, so tax history, price history, Zestimate detail, schools and photo galleries
are out of scope. Only zillow.com is targeted, which means US listings only.
How much does it cost?
Pricing is per event: one charge for each result row written to the dataset. A location that fails produces no rows and costs you nothing, and the run never writes a row it could not charge for. The current rate is shown in the pricing panel at the top of this page.
Runs started from an Apify free plan stop at 250 requests and 250 results, and the run says so in
its status message. Any paid plan runs the full input and whatever maxItems you set. The cap
exists because this Actor fetches through String's own infrastructure, which Apify does not
reimburse on free-plan runs; it binds on requests as well as rows so that a long input list cannot
spend those fetches on rows the run will not return.
Using it with the Apify API
import { ApifyClient } from "apify-client";const client = new ApifyClient({ token: "<YOUR_APIFY_TOKEN>" });const run = await client.actor("usestring/zillow-listings").call({locations: ["Austin, TX", "Denver, CO"],listingType: "for_sale",maxItems: 1000});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.length, items[0]);const summary = await client.keyValueStore(run.defaultKeyValueStoreId).getRecord("SUMMARY");console.log(summary?.value);
The Python client and the plain REST endpoints take the same input object; Apify's API documentation has the equivalents.
Is it legal to scrape Zillow?
This Actor reads the public search results page a logged-out visitor sees. There is no account, no session cookie, no paywall and nothing behind a login. Scraping public web pages is broadly lawful in the US and the EU, and courts have repeatedly declined to treat reading a public page as unauthorised access.
A listing row is property data. The only person a row can name is the listing broker, which Zillow publishes as business contact information; no buyer, seller or tenant details are collected. What you do with the data afterwards is your responsibility, and republishing listing content verbatim raises copyright questions that reading the data for analysis does not. None of this is legal advice, so talk to a lawyer if your use goes beyond internal analysis.
Related actors
- Zillow Property Detail Scraper — the other half of
this pair. Feed it the
zpidvalues from here for year built, lot size, home type and the listing description. - Realtor.com Listings Scraper — the same job on Realtor.com, up to 200 listings per location and with the listing agent named.
- LoopNet Commercial Listings Scraper — commercial space rather than homes: office, retail and industrial, for sale or for lease.
- AutoTrader Vehicle Listings Scraper — the same search-page-to-rows shape applied to used vehicles.
FAQ
Do I need a Zillow account, API key or cookies? No. The Actor reads the public search page as a logged-out visitor and never signs in.
How many listings do I get per location? Around 41, which is one Zillow search page. Ten locations therefore return roughly 410 rows for ten requests. There is no deeper paging, so more coverage means more locations, not a bigger page.
Can I get rentals and sold homes?
Yes. Set listingType to for_rent or sold. The default is for_sale.
Is price a number or a string?
A number. priceText carries Zillow's formatted version next to it, and beds, baths, area and
coordinates are numbers too.
How do I de-duplicate across runs?
Key on zpid. It is Zillow's own identifier, it is stable, and it is also what the Zillow Property
Detail Scraper accepts as input.
Does it work outside the US?
No. It targets zillow.com, so US listings only. There is no country or language input.
What happens if one location fails?
The rest of the batch still returns. The failed location and its error are written to the run's
SUMMARY record, and only a run where every location failed exits with an error.
How fresh is the data?
Every row is stamped with collectedAt and reflects what Zillow was showing at that moment.
Schedule the run to build a series.
Feedback
Found a field parsed wrong, a market that fails, or something missing from the schema? Open an issue from the Issues tab on this Actor's Store page. A report that includes the input which reproduces the problem gets fixed fastest.