# Zillow Details Scraper (Cheap) (`data_api/zillow-details-scraper-cheap`) Actor

Zillow property details scraper that extracts pricing, features, tax records, and neighborhood data from any listing using URLs, ZPIDs, or addresses, so real estate teams can build datasets without manual lookups.

- **URL**: https://apify.com/data\_api/zillow-details-scraper-cheap.md
- **Developed by:** [Data API](https://apify.com/data_api) (community)
- **Categories:** Real estate, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.99 / 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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Zillow Property Details Scraper

Checking one Zillow home at a time is fine until you have fifty addresses to look at and a deadline. This scraper takes the grind out of it: hand it Zillow detail-page links, ZPIDs, or plain street addresses, and it returns each home as a clean row with price, Zestimate, beds and baths, lot size, tax figures, and the schools nearby. Paste a single address or a list of thousands, anywhere across the US. You only pay for rows that come back.

![Zillow Property Details Scraper](cover.jpg)

### What you get

Each lookup turns into one tidy row with a steady shape, so your columns line up whether you open the results in a sheet or load them into a database. Missing values come back as `null` rather than disappearing. The data falls into a few groups:

- **Identity and location** — `propertyId`, `listingUrl`, `addressLine`, `cityName`, `stateCode`, `postalCode`, `geoLat`, `geoLng`
- **Price and valuation** — `currentPrice`, `estimatedValue`, `estimatedRent`, `assessedValue`, `yearlyTax`
- **Home facts** — `bedroomCount`, `bathroomCount`, `interiorArea`, `lotArea`, `constructionYear`, `propertyType`, `listingStatus`, `daysListed`
- **History and extras** — `priceTimeline`, `taxTimeline`, `nearbySchools`, `photoLinks`, `agentInfo`
- **Run metadata** — `collectedAt`, `lookupSource`, `inputQuery`, `errorNote`

### Quick start

1. Click **Try for free** and open the input form.
2. Add what you want to look up: paste Zillow links into **Listing URLs**, numbers into **Zillow Property IDs (ZPID)**, or addresses into **Street Addresses**. Mix all three if you like.
3. Set a **Maximum homes per run** cap and adjust the timeouts if you are running a big batch.
4. Press **Start**, then download the results as JSON, CSV, Excel, or XML once the run finishes.

![How it works](how-it-works.jpg)

### Use cases

- **Market research** — pull prices, Zestimates, and days-on-market across a neighborhood and compare homes side by side
- **Investment screening** — line up tax assessments, price history, and rent estimates to weigh potential returns without opening every listing
- **Agent prospecting** — look up homes in bulk to build prospect lists with accurate pricing and listing context
- **Portfolio tracking** — re-run on a schedule to watch price moves, status changes, and fresh Zestimates on homes you follow
- **Housing studies** — gather structured Zillow data across ZIP codes for affordability research or data journalism
- **CRM enrichment** — feed a column of addresses and get back geocoded, priced records to attach to existing records

### Input

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `listingUrls` | array of strings | One of `listingUrls`, `propertyIds`, or `addressQueries` | Full Zillow detail-page links. The scraper reads the ZPID straight out of each link. |
| `propertyIds` | array of strings | One of `listingUrls`, `propertyIds`, or `addressQueries` | Raw numeric ZPIDs. The quickest route, since it skips link parsing and address matching. |
| `addressQueries` | array of strings | One of `listingUrls`, `propertyIds`, or `addressQueries` | US street addresses in "street, city, state, ZIP" form. Each one is matched on Zillow. |
| `resultsLimit` | integer | No | Cap on homes processed per run. Default `50`; use `0` for no cap. |
| `runTimeoutSeconds` | integer | No | Longest the whole run may take before it wraps up. Anything collected so far is kept. Default `360`. |
| `requestTimeoutSeconds` | integer | No | How long a single home lookup may take before the scraper moves on. Default `45`. |

#### Example input

```json
{
    "listingUrls": [
        "https://www.zillow.com/homedetails/10791-Gooseberry-Ct-Truckee-CA-96161/19435371_zpid/",
        "https://www.zillow.com/homedetails/512-Pine-St-Boulder-CO-80302/13145520_zpid/"
    ],
    "propertyIds": ["13145520", "29023456"],
    "addressQueries": [
        "742 Evergreen Terrace, Springfield, IL 62704",
        "1600 Pennsylvania Ave, Washington, DC 20500"
    ],
    "resultsLimit": 50
}
```

### Output

Every lookup is stored as its own item in the dataset, and you can export the whole set as JSON, CSV, XML, or Excel. The fields below appear on each row; values Zillow does not have come back as `null`.

#### Example output

```json
{
    "propertyId": "19435371",
    "listingUrl": "https://www.zillow.com/homedetails/10791-Gooseberry-Ct-Truckee-CA-96161/19435371_zpid/",
    "addressLine": "10791 Gooseberry Ct",
    "cityName": "Truckee",
    "stateCode": "CA",
    "postalCode": "96161",
    "currentPrice": 1250000,
    "estimatedValue": 1230000,
    "estimatedRent": 5200,
    "bedroomCount": 4,
    "bathroomCount": 3.5,
    "interiorArea": 2800,
    "lotArea": 10454,
    "constructionYear": 2005,
    "propertyType": "SINGLE_FAMILY",
    "listingStatus": "FOR_SALE",
    "daysListed": 14,
    "assessedValue": 980000,
    "yearlyTax": 12500,
    "geoLat": 39.3277,
    "geoLng": -120.2137,
    "photoLinks": ["https://photos.zillowstatic.com/fp/example-1.jpg"],
    "agentInfo": {"name": "Jane Smith", "phone": "530-555-0100", "brokerage": "Sierra Realty"},
    "priceTimeline": [{"date": "2023-06-15", "price": 1200000, "event": "Listed"}],
    "taxTimeline": [{"year": 2023, "taxPaid": 12500, "assessedValue": 980000}],
    "nearbySchools": [{"name": "Truckee Elementary", "rating": 7, "distance": 1.2, "grades": "K-5"}],
    "collectedAt": "2026-06-29T12:00:00.000000+00:00",
    "lookupSource": "propertyIds",
    "inputQuery": "19435371",
    "errorNote": null
}
```

#### Output fields

| Field | Type | Description |
|-------|------|-------------|
| `propertyId` | string | Zillow's numeric ZPID for the home |
| `listingUrl` | string | Web address of the home's Zillow page |
| `addressLine` | string | House number and street |
| `cityName` | string | Town or city |
| `stateCode` | string | Two-letter US state code |
| `postalCode` | string | Five-digit ZIP code |
| `currentPrice` | number | Asking price or latest Zestimate in USD |
| `estimatedValue` | number | Zillow's computed home value (Zestimate) in USD |
| `estimatedRent` | number | Projected monthly rent in USD |
| `bedroomCount` | integer | Number of bedrooms |
| `bathroomCount` | number | Bathroom count, which may carry a half value such as 2.5 |
| `interiorArea` | number | Finished living space in square feet |
| `lotArea` | number | Parcel size in square feet |
| `constructionYear` | integer | Year the home was first built |
| `propertyType` | string | SINGLE\_FAMILY, CONDO, TOWNHOUSE, MULTI\_FAMILY, and similar |
| `listingStatus` | string | FOR\_SALE, SOLD, PENDING, and similar |
| `daysListed` | integer | Days the current listing has been live |
| `assessedValue` | number | Latest value the tax authority placed on the home |
| `yearlyTax` | number | Yearly property tax in USD |
| `geoLat` | number | Latitude coordinate |
| `geoLng` | number | Longitude coordinate |
| `photoLinks` | array | Web addresses for listing photos |
| `agentInfo` | object | Listing agent name, phone, and brokerage |
| `priceTimeline` | array | Past price moves and sale events |
| `taxTimeline` | array | Year-by-year tax assessment records |
| `nearbySchools` | array | Nearby schools with ratings, distance, and grade ranges |
| `collectedAt` | string | ISO 8601 timestamp of when the row was pulled |
| `lookupSource` | string | Which input lane produced the row: propertyIds, listingUrls, or addressQueries |
| `inputQuery` | string | The exact value you supplied; present on every row, including failures |
| `errorNote` | string | Short reason a lookup did not succeed; `null` on a clean row |

### Tips for best results

- **Start with a handful.** Run 10 inputs before a big batch so any URL-format or address-matching quirks show up early.
- **ZPIDs are the fastest path.** When you already have ZPIDs, use `propertyIds` — it skips link parsing and address search, so those rows return quickest.
- **Spell out addresses fully.** Include the ZIP. Partial addresses can match more than one home and may resolve to the wrong one.
- **Cap test runs with `resultsLimit`.** Set it to 20–50 while you confirm the output fits your pipeline, then drop it to `0` for the full list.
- **Give large batches more time.** Raise `runTimeoutSeconds` for hundreds of homes, and bump `requestTimeoutSeconds` toward 60 on slower connections so individual lookups are not cut short.
- **Failures still return a row.** A bad link or unmatched address comes back with `errorNote` filled in and `inputQuery` echoing what you sent, so nothing silently drops.

### How can I use Zillow property data?

**How can I use the Zillow Property Details Scraper to research a local housing market?**
Paste a batch of addresses or ZPIDs for a target area and the scraper returns each home's price, Zestimate, bed and bath count, lot size, and days on market. Group the rows by ZIP or city to compare pricing, spot stale listings, and build a quick base layer for any Zillow market-research project.

**How can I pull Zillow property data by address in bulk?**
Drop your addresses into `addressQueries` in "street, city, state, ZIP" form. The scraper looks each one up on Zillow, matches it to a ZPID, and returns the full record — so a plain column of addresses becomes a structured dataset with pricing, tax figures, and coordinates you can export to CSV or Excel.

**How can I track Zillow price changes and tax history for a set of homes?**
Feed the same `propertyIds` or `listingUrls` on a schedule. Each run returns the current `currentPrice` and `estimatedValue` along with `priceTimeline` and `taxTimeline`, so you can watch how a home's price and assessments move over time without opening a single listing by hand.

**How can I enrich a CRM with Zillow home data?**
Already have addresses from a CRM or spreadsheet? Set `resultsLimit` to `0` and send the whole list. Every entry returns one row with a geocoded address, price, home facts, and listing status, ready to push back into your records or a Google Sheet for the team to review.

### Is it legal to scrape data?

Our actors are ethical and do not extract any private user data, such as email addresses or private contact information. They only extract what the user has chosen to share publicly. We therefore believe that our actors, when used for ethical purposes by Apify users, are safe.

However, you should be aware that your results could contain personal data. Personal data is protected by the GDPR in the European Union and by other regulations around the world. You should not scrape personal data unless you have a legitimate reason to do so. If you're unsure whether your reason is legitimate, consult your lawyers.

You can also read Apify's blog post on the [legality of web scraping](https://blog.apify.com/is-web-scraping-legal/).

### Support

Questions, feature requests, or a field you'd like added? Reach out at <data.apify@proton.me> and we'll get back to you.

# Actor input Schema

## `listingUrls` (type: `array`):

Paste whole Zillow detail-page links. The scraper reads the ZPID straight out of each link, so standard for-sale pages, for-sale-by-owner pages, and pre-foreclosure pages all work. Copy them right from your browser bar. Shape looks like https://www.zillow.com/homedetails/\[address]/\[zpid]\_zpid/

## `propertyIds` (type: `array`):

Drop in raw ZPIDs as plain numbers. Every Zillow home carries one, shown in the listing link just before \_zpid. This is the quickest route because it skips link parsing and address matching. Paste hundreds at once if you like.

## `addressQueries` (type: `array`):

Type addresses the usual US way: street, city, state, ZIP. The scraper looks each one up on Zillow and returns the home it matches. Works for single-family houses, condos, townhomes, and multi-family units. Leaving out the ZIP can bring back several candidates, so the fuller the address the better.

## `resultsLimit` (type: `integer`):

Caps how many homes a single run will touch, so a big paste doesn't quietly run up your bill. Use 0 (or leave it blank) to process everything you supplied.

## `runTimeoutSeconds` (type: `integer`):

Longest the run is allowed to go before it wraps up. Whatever has already been collected is kept in the dataset. Give it more room when you queue up hundreds of homes.

## `requestTimeoutSeconds` (type: `integer`):

How long one home lookup may take before the scraper gives up on it and moves on. Bump it up if slow connections cause repeated misses.

## Actor input object example

```json
{
  "listingUrls": [
    "https://www.zillow.com/homedetails/10791-Gooseberry-Ct-Truckee-CA-96161/19435371_zpid/"
  ],
  "propertyIds": [
    "13145520"
  ],
  "addressQueries": [
    "742 Evergreen Terrace, Springfield, IL 62704"
  ],
  "resultsLimit": 50,
  "runTimeoutSeconds": 360,
  "requestTimeoutSeconds": 45
}
```

# 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 = {
    "listingUrls": [
        "https://www.zillow.com/homedetails/10791-Gooseberry-Ct-Truckee-CA-96161/19435371_zpid/",
        "https://www.zillow.com/homedetails/512-Pine-St-Boulder-CO-80302/13145520_zpid/"
    ],
    "propertyIds": [
        "13145520",
        "29023456"
    ],
    "addressQueries": [
        "742 Evergreen Terrace, Springfield, IL 62704",
        "1600 Pennsylvania Ave, Washington, DC 20500"
    ],
    "resultsLimit": 50,
    "runTimeoutSeconds": 360,
    "requestTimeoutSeconds": 45
};

// Run the Actor and wait for it to finish
const run = await client.actor("data_api/zillow-details-scraper-cheap").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 = {
    "listingUrls": [
        "https://www.zillow.com/homedetails/10791-Gooseberry-Ct-Truckee-CA-96161/19435371_zpid/",
        "https://www.zillow.com/homedetails/512-Pine-St-Boulder-CO-80302/13145520_zpid/",
    ],
    "propertyIds": [
        "13145520",
        "29023456",
    ],
    "addressQueries": [
        "742 Evergreen Terrace, Springfield, IL 62704",
        "1600 Pennsylvania Ave, Washington, DC 20500",
    ],
    "resultsLimit": 50,
    "runTimeoutSeconds": 360,
    "requestTimeoutSeconds": 45,
}

# Run the Actor and wait for it to finish
run = client.actor("data_api/zillow-details-scraper-cheap").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 '{
  "listingUrls": [
    "https://www.zillow.com/homedetails/10791-Gooseberry-Ct-Truckee-CA-96161/19435371_zpid/",
    "https://www.zillow.com/homedetails/512-Pine-St-Boulder-CO-80302/13145520_zpid/"
  ],
  "propertyIds": [
    "13145520",
    "29023456"
  ],
  "addressQueries": [
    "742 Evergreen Terrace, Springfield, IL 62704",
    "1600 Pennsylvania Ave, Washington, DC 20500"
  ],
  "resultsLimit": 50,
  "runTimeoutSeconds": 360,
  "requestTimeoutSeconds": 45
}' |
apify call data_api/zillow-details-scraper-cheap --silent --output-dataset

```

## MCP server setup

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

```

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/qWHhXovzWyQEEfn5t/builds/PdcYH3bjOOqKuuTRM/openapi.json
