Skip to content

How to Scrape Zillow Data: Prices, Zestimates and Home Facts by City or URL

Give a ready-made Actor a city, a Zillow search or home URL, or a street address, and get one row per listing with price, beds, baths, living area and Zestimate. This guide covers the no-code steps, the API, a real run and its cost.

Published
Reading time
8 min read
Pixel-art robot with an orange >_ on its terminal head pulls a listing card out of a grey house next to a yard sign with the blue Zillow logo and drops it into a crate
In this article
  1. What data you can get from Zillow
  2. How to scrape Zillow without code
  3. How to scrape Zillow with the API
  4. With an AI agent (MCP)
  5. A real run: Homes for sale in Greenville, SC
  6. What it costs
  7. Limits and common problems
  8. Is it legal to scrape Zillow?
  9. Alternatives
  10. FAQ
  11. Does Zillow have an API?
  12. Can you scrape Zestimates from Zillow?
  13. Can you scrape sold or rental listings?
  14. Can you scrape Zillow by address?
  15. How many listings can one Zillow search return?

To scrape Zillow data, give a ready-made Actor a city, a Zillow search URL, a home URL or a street address, and export one row per listing: price, beds, baths, living area, year built, Zestimate, photos and MLS details. On Scrape.ly that takes a few clicks, or one API call.

This guide shows which fields you get, the no-code steps, and the same job in curl, Python, JavaScript and through an AI agent. Then it walks through a real run from 22 October 2026, five homes for sale in Greenville, South Carolina, with the log and the exact cost.

What data you can get from Zillow

The Zillow Detail Scraper returns one row per listing. Fields that Zillow doesn't show for a home come back as null.

Field What it holds
zpid, propertyUrl Zillow's property ID and the listing URL
address, listingAddress The full address and its parts (street, city, state, ZIP)
coordinates Latitude and longitude
price, listingPrice List price as a number, and as an object with currency and formatted text
bedrooms, bathrooms, bathroomsDetail Bed and bath counts, with full and half baths
livingArea, lotArea, pricePerSquareFoot Living area in square feet, lot size and price per square foot
yearBuilt, homeType Year built and type, such as SINGLE_FAMILY
listingStatus, listingType Status (forSale) and flags such as pending, new home, foreclosure or for sale by owner
zestimate, rentZestimate Zillow's estimated value and rent, when shown
monthlyHoaFee, propertyTaxRate Running costs, when shown
description, photoCount, mainImage, listingPhotos Listing text and photos
mls, agent, broker, attributionInfo MLS name and ID; listing agent and broker when the page shows them
resoFacts, parking Structured facts such as garage spaces and parking features
isValid, scrapedAt Row status and timestamp

What you won't get: price history, tax history, nearby schools and nearby homes come back empty, and parcel IDs, tax amounts and school districts aren't included. Listings behind a Zillow login aren't available either.

How to scrape Zillow without code

  1. Open the Zillow Detail Scraper in the Scrape.ly Store.
  2. Pick the Homes for sale in Greenville, SC, 5 results preset. It fills in this input:
json
{
  "startUrls": [],
  "searchQueries": [
    "Greenville, SC"
  ],
  "maxItems": 5,
  "propertyStatus": "FOR_SALE",
  "addresses": [],
  "extractBuildingUnits": "disabled"
}
  1. Click Start. Our run finished in under 6 seconds.
  2. Open the dataset and export it as JSON, CSV or Excel.

The input fields that matter most:

  • searchQueries: cities or neighborhoods, typed the way you'd type them into Zillow's search box.
  • startUrls: Zillow home URLs (/homedetails/), search URLs or building pages. A home URL returns one listing; a search URL returns homes up to the maximum.
  • addresses: US street addresses, if you already know which homes you want.
  • maxItems: the most listings to return. Default 10; 0 means no limit.
  • propertyStatus: FOR_SALE, RECENTLY_SOLD or FOR_RENT, applied to location searches. A home URL is read as Zillow published it.
  • searchResultsDatasetId: a dataset whose rows contain propertyUrl, detailUrl or address, so you can feed in URLs from an earlier run.

How to scrape Zillow with the API

Create a token under Settings → API & Integrations in the dashboard and store it as SCRAPELY_TOKEN. This preset runs in a few seconds, so the synchronous endpoint can start it and return the listings in one call:

shell
curl -X POST "https://api.scrape.ly/v2/actors/roamandroof~zillow-detail-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"startUrls": [], "searchQueries": ["Greenville, SC"], "maxItems": 5, "propertyStatus": "FOR_SALE", "addresses": [], "extractBuildingUnits": "disabled"}'

The call waits up to 45 seconds and returns a JSON array. Add ?format=csv for CSV. A job with many searches or URLs can take longer. Then the call answers 408 while the run keeps going, and calling again would start a second run. For those, start the run with POST /v2/actors/roamandroof~zillow-detail-scraper/runs, poll /v2/actor-runs/RUN_ID?waitForFinish=60 until the status is SUCCEEDED, and read /v2/actor-runs/RUN_ID/dataset/items. The API docs show the full flow.

In Python, install the client with pip install scrapely-client-python. call() waits for the run however long it takes:

python
import os
from scrapely_client import ScrapelyClient

client = ScrapelyClient(os.environ["SCRAPELY_TOKEN"], api_url="https://api.scrape.ly")

run = client.actor("roamandroof/zillow-detail-scraper").call(
    run_input={
        "searchQueries": ["Greenville, SC"],
        "maxItems": 5,
        "propertyStatus": "FOR_SALE",
    }
)
print(run.status)  # SUCCEEDED, FAILED, ABORTED or TIMED-OUT
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item["price"], item["bedrooms"], item["bathrooms"], item.get("zestimate"))

In JavaScript or Node.js, install it with npm install scrapely-client:

javascript
import { ScrapelyClient } from 'scrapely-client';

const client = new ScrapelyClient({ token: process.env.SCRAPELY_TOKEN, baseUrl: 'https://api.scrape.ly' });

const run = await client.actor('roamandroof/zillow-detail-scraper').call({
  searchQueries: ['Greenville, SC'],
  maxItems: 5,
  propertyStatus: 'FOR_SALE',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);

The Python client, JavaScript client and dataset API docs cover paging through large datasets.

With an AI agent (MCP)

Scrape.ly runs a hosted MCP server at https://api.scrape.ly/mcp. In Cursor, add it to ~/.cursor/mcp.json:

json
{
  "mcpServers": {
    "scrapely": {
      "url": "https://api.scrape.ly/mcp",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

In Claude Code, run claude mcp add --transport http scrapely https://api.scrape.ly/mcp, then /mcp to sign in with OAuth. The agent uses search-actors, fetch-actor-details, call-actor and get-actor-output. Try a prompt like: "Use scrapely to run roamandroof/zillow-detail-scraper for homes for sale in Greenville, SC, 5 results, and give me a table of price, beds, baths, living area and Zestimate." The MCP docs list the other clients.

A real run: Homes for sale in Greenville, SC

We ran the preset on 22 October 2026. It saved 5 listings, the run took 5.7 seconds, and it was charged $0.0062 in total. We removed the street addresses of these private homes and any agent names from the sample below.

address price bedrooms bathrooms livingArea zestimate
[street withheld], Greenville, SC 29605 369,000 3 2 1,542 —
[street withheld], Greenville, SC 29617 170,000 2 1 1,123 158,700
[street withheld], Greenville, SC 29607 1,087,750 5 5 4,351 1,034,500
[street withheld], Greenville, SC 29605 1,700,000 3 5 4,607 1,779,400
[street withheld], Greenville, SC 29605 375,000 3 2 1,325 366,000
Text
2026-10-22T15:23:09.198Z [Scrapely] Container started (setup 0.8 s)
2026-10-22T15:23:10.291Z [scrapely.actor] INFO  [Scrapely] Initializing API storage for run aec640d809a248a09
2026-10-22T15:23:10.420Z [scrapely.proxy_configuration] INFO  Platform proxy access check succeeded.
2026-10-22T15:23:10.420Z [scrapely.actor] INFO  Fetching with proxy pool DATACENTER
2026-10-22T15:23:13.685Z [scrapely.actor] INFO  Saved 5 listings. Requests 6. Transferred 4829117 bytes.
2026-10-22T15:23:13.685Z [scrapely.actor] INFO  [Status message]: Saved 5 listings from 1 inputs. Reached the maxItems limit of 5.
2026-10-22T15:23:14.092Z [Scrapely] Container state: status=exited exitCode=0 oomKilled=false

All five were single-family homes for sale, priced from $170,000 to $1,700,000. Four had a Zestimate: three sat below the list price and one above it ($1,779,400 against $1,700,000). The first listing had none. Its full record shows a 2021 build on 0.48 acres at $239 per square foot, 48 photos and the Greater Greenville AOR as its MLS. As the limits below say, listingPriceHistory and nearbySchools came back empty, and daysOnZillow was null. The run made 6 requests and moved 4,829,117 bytes (about 4.8 MB) for five listings.

What it costs

The Actor has one charge event: result-item, at $0.001 for each listing saved. URLs or addresses that can't be read aren't charged.

This run: 5 listings × $0.001 = $0.005 in event fees. The run was charged $0.0062 in total, because platform usage added $0.000754 of compute, $0.000399 of datacenter proxy traffic and $0.000005 of dataset writes.

At 1,000 listings: 1,000 × $0.001 = $1.00 in event fees, plus platform usage for compute and proxy traffic.

Limits and common problems

  • One results page per search. A location search returns the first results page Zillow shows for it (about 40 homes). For bigger jobs, add more searches (for example, by neighborhood) or paste home URLs.
  • Missing blocks. Price history, tax history, nearby schools and nearby homes are loaded by a separate request this Actor doesn't make, so they come back empty. If you need them, this is the wrong tool.
  • Empty fields. Zillow doesn't show every field on every listing, so values such as agent contact details, days on Zillow or the Zestimate can be null, as in the first row above.
  • Bad input. A wrong URL or an address Zillow can't find saves no row and isn't charged. The run still succeeds and lists it under failedInputs in the OUTPUT record.
  • Sync timeouts. This run took 5.7 seconds, but the synchronous endpoint stops waiting at 45 seconds. Use the asynchronous flow for large jobs.

The Actor reads publicly visible listing pages and never logs in. Zillow's Terms of Use (updated October 28, 2025) are clear, though. They forbid "automated queries (including screen and database scraping, spiders, robots, crawlers, bypassing 'captcha' or similar precautions...)". They allow copying information only "without the aid of any automated processes" and only as needed for personal or "Pro" use. Listings also carry more personal data than product pages: a street address points to someone's home, and agent names and phone numbers identify people. Once you collect personal data, privacy laws such as the GDPR and the CCPA apply, which is why we masked our sample. This is not legal advice. Before you use Zillow data commercially, get advice from a lawyer for your case.

Alternatives

  • Zillow's Bridge APIs. Zillow Group's MLS Listings API (Bridge Listing Output) serves MLS listing data over REST, normalized to the RESO data dictionary. Access is invite-only and at the discretion of each MLS. The Public Records API covers parcel, assessment and transaction data, roughly 15 years of it, and is also invite-only. If you're a broker or a business with a data agreement, these are the better route.
  • Zestimates API and Zillow Research data. The Zestimates API returns current property, rental and foreclosure Zestimates for about 100 million US properties, aimed at commercial use. For research, Zillow points to its Research data, which has looser terms. If you need market trends rather than single listings, start there.
  • Your own Python scraper. Scrapfly's guide reads the JSON Zillow embeds in each page (__NEXT_DATA__). Scrapingdog's guide uses generated CSS class names, and its text and code already cite two different versions of the same class. Expect to maintain selectors and handle blocks yourself.
  • A headless browser. Playwright or Selenium gives full control over the page. It's the better choice when you need data a ready-made tool doesn't collect, at the cost of running browsers and proxies yourself.

FAQ

Does Zillow have an API?

Yes. Zillow Group lists close to 20 APIs, but the listing data needs approval: the MLS Listings and Public Records APIs are invite-only, and the Zestimates API is aimed at businesses. Researchers can download aggregate market data from Zillow Research instead.

Can you scrape Zestimates from Zillow?

Yes. Each row carries zestimate and rentZestimate when Zillow shows them. In our run, four of the five listings had a Zestimate and one had none.

Can you scrape sold or rental listings?

Yes. Set propertyStatus to RECENTLY_SOLD or FOR_RENT for a location search. The Recently sold in Greenville, SC preset shows the sold case. A home URL you paste is read as Zillow published it, whatever status you pick.

Can you scrape Zillow by address?

Yes. Put US street addresses in addresses, one per entry, and the Actor finds each home and returns its details. An address Zillow can't find saves no row and isn't charged.

How many listings can one Zillow search return?

One search returns the first results page Zillow shows, about 40 homes. For more, split the area into several searches or collect home URLs and pass them in startUrls or through searchResultsDatasetId.

  • zillow
  • real estate
  • python
  • api

About the author

Actor publisher on the Scrape.ly Store

Homes, stays, trips and cars: listings from 40+ property portals, the big travel and booking sites and the main car marketplaces. For agents, investors, travel analysts and dealers. A Scrapely studio.

//Keep reading\\

The internet is full of data.
Start owning yours.

Scrape.ly gives you the infrastructure, the tooling, and the team to extract, process, and operationalize web data at any scale. Setup takes minutes. The advantage lasts forever.

No credit card. No setup calls.