# Zillow Detail Scraper 💰 From $1.4/1K · URL, ZPID or Address (`memo23/zillow-detail-scraper`) Actor

Paste Zillow property URLs, ZPIDs or plain addresses and get the full record for each: price, beds, baths, sq ft, lot size, description, agent and broker contacts, schools, zoning, price and tax history. Works on sale, rent and sold listings. JSON, CSV or Excel export.

- **URL**: https://apify.com/memo23/zillow-detail-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Real estate, Lead generation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## 🏠 Zillow Detail Scraper

Hand it **Zillow property URLs, ZPIDs, or plain street addresses** and get the complete record for each one — price, beds, baths, sq ft, lot size, the full description, listing agent and broker contacts, schools, zoning, and the price and tax history.

No search to configure and no filters to set. Paste a list, press Start, and get one fully detailed row per property as JSON, CSV, or Excel.

### ⭐ Why use this Zillow detail scraper?

- **Three ways in** — a `/homedetails/` URL, a bare ZPID, or an address like `2307 Cheyenne Cir, Austin, TX`. Mix all three in one run.
- **Everything on the page** — description, photos, price history, tax history, Zestimate, schools, HOA, lot size, zoning, and agent/broker contact details.
- **Other Zillow URL types too** — building (`/b/`), new-construction community, and agent (`/profile/`) URLs are detected automatically and sent to the right parser.
- **Works on sale, rent and sold** — the same column set whatever the listing status.
- **Agent email enrichment (optional)** — match the listing agent against a 1,200,000+ US agent database and add their direct email and phone.
- **Proxies included on paid plans** — on any paid Apify plan the actor routes itself through its own residential proxy pool at no extra cost, and the `proxy` field is not read at all. Free-plan runs go out through Apify's default datacenter proxy, which Zillow refuses, so on the free plan put your own residential proxy URLs in the `proxy` field, or pick Apify Proxy with the RESIDENTIAL group.

### 📋 What data does it extract?

| Field group | Columns |
| --- | --- |
| **Identity** | `ID` (ZPID), `URL`, `Address`, `City`, `State`, `Zip Code`, `Latitude`, `Longitude` |
| **Listing** | `Price`, `Status`, `Property Subtype`, `Acres`, `Description` |
| **People** | `Agent Name`, `Agent Number`, `Broker Name`, `Broker Number` |
| **Land & legal** | `Zoning`, `Zoning Description` |
| **Run metadata** | `Date` (the date the run executed, not a listing date), `Source` |
| **Full record** | `details` — Zillow's property object: price history, tax history, Zestimate, photos, schools, HOA, resoFacts (~200 fields) |
| **Raw Zillow keys** | `zpid`, `detailUrl`, `price`, `address`, `beds`, `baths`, `area`, `latLong`, `imgSrc`, `statusType` — Zillow's own field names, carried next to the columns above on property rows |

The standard column set is fixed, so a few of its columns always arrive empty on this actor: `Change Type` and `Previous Status` (monitoring columns), `School 1`–`School 3`, and `Subtype 1`–`Subtype 4`. The school data itself is inside the `details` blob, as `details.schools` and `details.assignedSchools`.

### 🧾 Supported inputs

| Input | Example |
| --- | --- |
| Property URL | `https://www.zillow.com/homedetails/2307-Cheyenne-Cir-Austin-TX-78745/58313397_zpid/` |
| ZPID | `58313397` |
| Address | `2307 Cheyenne Cir, Austin, TX` |
| Building | `https://www.zillow.com/b/the-tower-new-york-ny-5XsKjq/` |
| Community | `https://www.zillow.com/community/farmlore-villas/30499782_plid/` |
| Agent profile | `https://www.zillow.com/profile/BrettHull` |

A property URL, ZPID or address returns one row. Two kinds of input expand instead: a `/community/` page becomes one row per home in that community, and a Zillow search URL or an `/apartments/` page carrying search results fills rows up to `maxItems`. Every one of those rows counts against `maxItems` (default 100), so raise it before pasting anything that is not a single property.

### 💡 Use cases

- **Agents & teams** — turn a list of addresses from a farm area or a CRM export into full listing records with contacts.
- **Investors & wholesalers** — pull price history, tax history and lot data for a shortlist before making offers.
- **Appraisers & analysts** — build comparable sets from exact properties rather than whole-market sweeps.
- **Proptech & data teams** — resolve a stream of Zillow URLs into structured rows for an app, map or warehouse.
- **Lead workflows** — enrich a property list with the listing agent's direct email and phone.

### 🚀 How it works

Each input is resolved to a ZPID — an address goes through Zillow's own autocomplete first — and the property is then fetched from Zillow's GraphQL API. When that endpoint is blocked, the actor falls back through a second query shape and finally reads the listing page itself through a managed unblocker, so a row comes back rather than an empty cell. That last step is capped at 60 properties per run; past the cap a blocked property still produces a row, but without its `details` record.

### ⚙️ Input configuration

| Field | Type | Notes |
| --- | --- | --- |
| `startUrls` | array | One Zillow property URL per line |
| `zpids` | array | Bare ZPIDs, one per line |
| `addresses` | array | `123 Main St, City, State` |
| `includePropertyDetails` | boolean | On by default; a property URL, ZPID or address always fetches the full record |
| `includeAgentDetails` | boolean | Adds the agent's own profile record under `details.agentDetails` |
| `includeSaleDetails` | boolean | Adds Zillow's secondary listing hub under `details.saleDetails` |
| `includeNearbyHomes` | boolean | Keeps Zillow's nearby-homes blob inside `details` |
| `flatOutput` | boolean | Adds spreadsheet-friendly columns and drops `details` (the payload stays on the row as `hdpData.homeInfo`) |
| `enrichEmails` | boolean | Agent email lookup, billed per email found |
| `maxItems` | integer | Stop after this many properties; default 100. A `/community/` page counts as one row per home, not one row |

Every field, with its type and default, is also listed on the actor’s own [input schema page](https://apify.com/memo23/zillow-detail-scraper/input-schema).

### 📦 Output sample

```json
{
  "ID": "58313397",
  "URL": "https://www.zillow.com/homes/58313397_zpid/",
  "Address": "2307 Cheyenne Cir",
  "City": "Austin",
  "State": "TX",
  "Zip Code": "78745",
  "Price": 995000,
  "Status": "FOR_SALE",
  "Property Subtype": "SINGLE_FAMILY",
  "Agent Name": "Brooke Roeder",
  "Agent Number": "(512) 964-8379",
  "Broker Name": "Christies Intl Real Estate Lone Star",
  "Acres": "0.33 Acres",
  "Latitude": 30.224419,
  "Longitude": -97.79659
}
```

That is an excerpt of a default row: the same row also carries the raw Zillow keys listed above and the full `details` blob.

### 🔑 Key output fields

- `ID` — the ZPID on a property row, matching the zpid inside that row's own `URL`. A building (`/b/`) row carries Zillow's building id and a `/b/` URL that holds no zpid.
- `Price` — the asking price, or the sold price on a closed sale. Left blank rather than zero where Zillow publishes no figure.
- `Acres` — Zillow's own lot-size string, copied as given (`8,276 sqft` or `0.29 Acres`).
- `Agent Name` / `Agent Number` — the listing agent, resolved through `listedBy`, then attribution data, then the contact module.
- `Date` — the date the run executed, not the date the home was listed. Zillow's posted date is `details.datePostedString` (and the `Listing Date` column when `flatOutput` is on).
- `details` — Zillow's property record, present unless `flatOutput` is on, which moves the same payload to `hdpData.homeInfo`. The nearby- and similar-homes blobs (`collections`, `nearbyHomes`, `relatedHomes`, `homeRecommendations`, `homeValuation`) are stripped unless `includeNearbyHomes` is on, which puts them back; the `submitFlow` form template is always stripped.

The standard columns are Title Case (`Address`, `Zip Code`, `Agent Name`) so they read cleanly as spreadsheet headers. Every property row also carries Zillow's own raw keys next to them — `zpid`, `detailUrl`, `price`, `address`, `beds`, `baths`, `area`, `latLong`, `imgSrc`, `statusType` and a few more — so a handful of values arrive twice under two names. `flatOutput` adds further Title Case columns but does not remove the raw ones. An agent-profile URL returns the agent's own record instead, which has neither of those two column sets.

### ❓ FAQ

**Can I pass hundreds of URLs?**
Yes. Raise `maxItems` and the run's "Maximum cost per run" limit so the run is not cut short partway.

**Why is `Price` empty on a sold home?**
In non-disclosure states, Texas among them, a sale price is not public record — Zillow publishes no figure there, so the column is left blank rather than filled with a wrong number.

**Do I need a Zillow API key?**
No. Nothing to configure and no login is used.

**Do I need proxies?**
Not on a paid Apify plan — the actor brings its own residential pool at no extra cost, and the `proxy` field is not read there. On the free plan the run goes out through Apify's default datacenter proxy, which Zillow refuses, so supply your own residential proxy URLs in the `proxy` field, or pick Apify Proxy with the RESIDENTIAL group.

**What if an address does not resolve?**
The actor tries progressively cleaned variants of the address before giving up, so unit noise and ranges usually still resolve. If nothing matches, that input is skipped and the rest of the run continues.

**How fresh is the data?**
Every run reads Zillow live at the moment it executes.

### 🛟 Support

Found an issue or missing a field? Open an issue on the actor's **Issues** tab with the run ID — that is all that is needed to trace exactly what happened.

### 🧩 Additional services

Need a custom pipeline (specific columns, delivery to your DB/S3/webhook, merged multi-source feeds)? Open an issue describing what you need or contact the author through the actor page.

### 🔎 Explore more scrapers

Eleven Zillow actors, all maintained by the same author. The six under **Listings and properties** share the same standard columns as this actor — some add extra columns of their own on top — so rows from any of them line up in one sheet.

**Listings and properties**

- [Zillow Scraper](https://apify.com/memo23/zillow-scraper-ppe) — the flagship: search by city, ZIP or filters, for sale, for rent, sold, plus change monitoring.
- [Zillow ZIP Code Search Scraper](https://apify.com/memo23/zillow-zip-code-search-scraper) — sweep a list of ZIP codes instead of one area at a time.
- [Zillow Rentals Scraper](https://apify.com/memo23/zillow-rentals-scraper) — rentals, with the rent range on apartment-complex rows.
- [Zillow Open Houses Scraper](https://apify.com/memo23/zillow-open-houses-scraper) — upcoming open houses with their exact dates and times.
- [Zillow Foreclosure Scraper](https://apify.com/memo23/zillow-foreclosure-scraper) — pre-foreclosure, auction and bank-owned (REO) records with auction dates and amounts.
- [ZILLOW | Bypass Limit of 500 | Search |Agent | with Email](https://apify.com/memo23/zillow-cheerio-scraper) — the earlier search build, with agent emails, kept in step with the flagship.

**People and contacts**

- [Zillow Agents Leads](https://apify.com/memo23/zillow-agents-leads-scraper-ppe) — agent contacts pulled from a database of 1,200,000+ US agents.
- [Zillow Agents Scraper](https://apify.com/memo23/apify-zillow-agents-cheerio) — agent finder and profile pages, with email discovery.
- [Zillow Property Managers Scraper](https://apify.com/memo23/apify-zillow-property-managers-cheerio) — property managers and owners, with emails.
- [Zillow Lenders Scraper](https://apify.com/memo23/zillow-lenders-scraper) — mortgage lenders from Zillow’s own lender directory.

Beyond Zillow: [all actors by memo23](https://apify.com/memo23), 100+ maintained scrapers for real estate, jobs, directories and reviews.

### 🤖 For AI Agents & LLM Apps

This actor is MCP-ready: call it from AI agents (Claude, GPT, LangChain, LlamaIndex, or the Apify MCP server) to answer questions like *"what is the tax history and lot size of 2307 Cheyenne Cir, Austin TX?"*. It takes a plain address, so an agent does not need to construct a Zillow URL first, and the structured JSON with stable field names makes rows directly usable as tool results — `details` for grounded property Q\&A, `Agent Name` / `Agent Number` for outreach steps.

To wire it up: the ready-made [Node.js](https://apify.com/memo23/zillow-detail-scraper/api/javascript) and [Python](https://apify.com/memo23/zillow-detail-scraper/api/python) client snippets call this actor in a few lines, the [MCP tab](https://apify.com/memo23/zillow-detail-scraper/api/mcp) exposes it as a tool to an MCP client, and once you have run the actor, its [integrations tab in your Apify Console](https://console.apify.com/actors/WzFGhGT9uwGkcx0DT/integrations/options) pushes each run straight to Google Sheets, Slack, a webhook or your own storage ([how integrations work](https://docs.apify.com/integrations)).

### ⚠️ Disclaimer

This actor is not affiliated with, endorsed by, or connected to Zillow Group. It extracts only publicly visible listing information for lawful purposes such as market research and personal planning; no login is used and no private data is accessed. You are responsible for how you use the data — respect Zillow’s terms and applicable laws. Apify’s own write-up on the [legality of web scraping](https://blog.apify.com/is-web-scraping-legal/) is a good starting point, and personal data in the output (agent names, phone numbers, emails) falls under rules such as [EU Regulation 2016/679](https://eur-lex.europa.eu/eli/reg/2016/679/oj).

***

*zillow detail scraper · scrape zillow property details · zillow zpid lookup · zillow address to property data · zillow property data extractor · zillow detail API · zillow price history scraper · export zillow property details to excel*

# Actor input Schema

## `startUrls` (type: `array`):

One Zillow property URL per line, e.g. https://www.zillow.com/homedetails/2307-Cheyenne-Cir-Austin-TX-78745/58313397_zpid/ — a property URL returns one fully detailed row. Building (/b/), new-construction community (/community/) and agent profile (/profile/) URLs are detected automatically and go to their own parsers; a community page expands into one row per home, so keep "Maximum properties" high enough to cover that.

## `zpids` (type: `array`):

Scrape individual properties directly by their Zillow Property ID (ZPID), one per line — no URL needed. Found in any property URL as the number before "\_zpid".

## `addresses` (type: `array`):

Addresses to scrape, one per line, in the format 123 Main St, City, State. Each one is resolved through Zillow's own autocomplete before the property is fetched. Progressively cleaned variants of the address are tried, and an address that still does not match is skipped while the rest of the run continues.

## `includePropertyDetails` (type: `boolean`):

Keep this on. A property URL, ZPID or address always fetches the full property record, so the toggle changes nothing for those inputs. It only matters for a URL that expands into a list of homes — a /community/ page, or a search URL pasted into the URL list — where turning it off emits one summary row per home instead of fetching each home's own record.

## `includeAgentDetails` (type: `boolean`):

Attaches the listing agent's profile record (reviews, sales stats, professional information, and an email where one is on file) under details.agentDetails. On an agent-listed property the agent name, agent phone and broker name are already on the row without this flag — they come from the property's own payload (listedBy, then attributionInfo, then the contact module). An owner-posted (FSBO) listing has no agent — Zillow returns every attributionInfo field null — and the row carries the owner's ownerName and ownerPhone instead. Zillow protects agent profile pages aggressively, so enabling this can produce 403s for some agents and slow the run down. With flatOutput on the profile record itself is dropped and only the Agent Email column is taken from it.

## `includeSaleDetails` (type: `boolean`):

Fetches Zillow's secondary listing hub for each property — MLS id, MLS name and disclaimer, last-checked and last-updated timestamps, favorite and page-view counts, open-house schedule — and attaches it under details.saleDetails. Costs one extra request per property. It lives inside details, so it is not in the output when flatOutput is on.

## `includeNearbyHomes` (type: `boolean`):

When enabled, keeps Zillow's `collections` / `nearbyHomes` / `relatedHomes` / `homeRecommendations` / `homeValuation` payloads (full listings for ~10–20 nearby properties) inside each row's `details` blob. When disabled (default), these are stripped — saves 25–70% of the bytes per row, depending on what Zillow returns, with no loss of per-row property data. The `submitFlow` form template is always stripped.

## `flatOutput` (type: `boolean`):

When enabled, each row gains a block of plain scalar columns for the most-requested facts — Listing Date, Days on Zillow, Time on Zillow, Year Built, Beds, Baths, Sq Ft, MLS ID, MLS Status, Annual Tax, Tax Assessed Value, summarized Price History and Tax History, Open House Times, New Roof, Permit History, Detail URL — and the nested `details` blob is dropped. The Agent Email column is filled only where Zillow publishes an email on the listing or `includeAgentDetails` is on; `enrichEmails` adds the agent's email in its own `contactEmail` column instead. The property payload still travels on the row as `hdpData.homeInfo`, so the row stays about the size of a default one; what changes is that the facts above arrive as plain columns.

## `enrichEmails` (type: `boolean`):

If enabled, adds the listing agent's email (and phone) to each property — matched from our database of 1,200,000+ U.S. agents, ~99.9% of which have an email on file. Adds contactEmail, contactPhone and agentEmailSource columns to every property row, left empty where no match is found. Billed per email found; only charged when an email is returned, never for misses. (On an agent-profile URL it works differently: it looks for a contact email on the agent's own website when the profile links one, and otherwise discovers one from the agent or business name, and the row gets contactEmail, contactWebsite and emailEnrichment instead of that trio.)

## `maxItems` (type: `integer`):

Stop after this many properties. One property URL, ZPID or address is one property, but a /community/ page expands into many, so leave this comfortably above the number of inputs.

## `maxConcurrency` (type: `integer`):

Maximum number of requests processed at the same time. Values above 32 are capped at 32: the native HTTP client crashes the run beyond roughly that many parallel sockets.

## `maxRequestRetries` (type: `integer`):

How many times one Zillow request is retried before the actor gives up on it. Values above 5 have no effect — a listing request is clamped to 5 retries, because a 403 is tied to the proxy exit IP and another attempt through the same one does not clear it.

## `proxy` (type: `object`):

On a paid Apify plan, leave this empty — the actor routes its own traffic through a built-in residential proxy pool at no extra cost, and this field is not read. On the free plan the run goes out through Apify's default datacenter proxy, which Zillow refuses, so enter your own residential proxy URLs here, or select Apify Proxy with the RESIDENTIAL group.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.zillow.com/homedetails/2307-Cheyenne-Cir-Austin-TX-78745/58313397_zpid/"
    },
    {
      "url": "https://www.zillow.com/homedetails/2107-Arpdale-St-Austin-TX-78704/29473051_zpid/"
    }
  ],
  "addresses": [
    "18 Zelma Dr, Greenville, SC 29617"
  ],
  "includePropertyDetails": true,
  "includeAgentDetails": false,
  "includeSaleDetails": false,
  "includeNearbyHomes": false,
  "flatOutput": false,
  "enrichEmails": false,
  "maxItems": 100,
  "maxConcurrency": 20,
  "maxRequestRetries": 5
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://www.zillow.com/homedetails/2307-Cheyenne-Cir-Austin-TX-78745/58313397_zpid/"
        },
        {
            "url": "https://www.zillow.com/homedetails/2107-Arpdale-St-Austin-TX-78704/29473051_zpid/"
        }
    ],
    "addresses": [
        "18 Zelma Dr, Greenville, SC 29617"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/zillow-detail-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": [
        { "url": "https://www.zillow.com/homedetails/2307-Cheyenne-Cir-Austin-TX-78745/58313397_zpid/" },
        { "url": "https://www.zillow.com/homedetails/2107-Arpdale-St-Austin-TX-78704/29473051_zpid/" },
    ],
    "addresses": ["18 Zelma Dr, Greenville, SC 29617"],
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/zillow-detail-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://www.zillow.com/homedetails/2307-Cheyenne-Cir-Austin-TX-78745/58313397_zpid/"
    },
    {
      "url": "https://www.zillow.com/homedetails/2107-Arpdale-St-Austin-TX-78704/29473051_zpid/"
    }
  ],
  "addresses": [
    "18 Zelma Dr, Greenville, SC 29617"
  ]
}' |
apify call memo23/zillow-detail-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/zillow-detail-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/WzFGhGT9uwGkcx0DT/builds/Xbjie8boJhrUcibVI/openapi.json
