# Zillow Data Scraper — US Homes for Sale, Rent & Sold (`devil_port369-owner/zillow-scraper`) Actor

Scrape Zillow property listings by city, ZIP, or URL — for sale, for rent, and recently sold. Get price, Zestimate, beds/baths, sqft, agent/broker, photos, and coordinates as flat JSON, CSV, or Excel. No official API key needed.

- **URL**: https://apify.com/devil\_port369-owner/zillow-scraper.md
- **Developed by:** [DataFusionX](https://apify.com/devil_port369-owner) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 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 Data Scraper – US Homes for Sale, Rent & Sold

> **Scrape Zillow property listings without an API key.** Extract US homes for sale, for rent, and recently sold — by city, ZIP, or Zillow URL — into flat JSON with price, Zestimate, beds/baths, sqft, agent/broker, photos, and coordinates.

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-orange.svg)](https://apify.com)

***

### What does Zillow Data Scraper do?

**Zillow Data Scraper extracts live US property listings — for sale, for rent, and recently sold — directly from Zillow, with no official API key required.** Give it a city, ZIP code, or a Zillow search/detail URL, and it returns a flat record per property: list price, Zestimate, beds, baths, square footage, lot size, home type, address, coordinates, listing agent/broker, and photos. It's built for anyone who has searched *"how to scrape Zillow"* and wants working structured data today, not a months-long integration project.

### Who is Zillow Data Scraper for?

- **Real estate investors & analysts** pulling comps, Zestimates, and inventory across multiple markets
- **PropTech teams** feeding listing data into dashboards, valuation tools, or search products
- **Market researchers** tracking price trends, days-on-market, and inventory mix by city or ZIP
- **Developers & data teams** who need structured Zillow data without maintaining a scraper themselves

### Why use Zillow Data Scraper?

- No Zillow login or official API key to manage
- Flat output schema — no nested response objects to unpack before you can use the data
- Includes **Zestimate and rent Zestimate** fields where Zillow exposes them — useful for valuation workflows most listing scrapers skip
- Filter by price, beds, baths, home type, sort order, and listing recency in a single request

***

### What data can you extract?

| Category | Fields |
|---|---|
| **Listing** | status, listing type, ZPID, listing ID, marketing status, days on Zillow |
| **Pricing** | list price, Zestimate, rent Zestimate, tax-assessed value |
| **Property** | beds, baths, sqft, lot size, home type, year built |
| **Location** | formatted address, street, city, state, ZIP, latitude, longitude |
| **Agent & broker** | broker name, agent name, agent phone (where Zillow exposes it on the listing) |
| **Listing flags** | for-sale-by-agent/owner, new construction, coming soon, auction, foreclosure, showcase listing |
| **Media** | photo count, thumbnail, full photo URL list |
| **Metadata** | source, country, location query, scrapedAt timestamp |

***

### How much does it cost?

> Pricing for this Actor is set on its Pricing tab according to the plan tier — starts from $1.75 per 1000 results

***

### Key Features

| Feature | Detail |
|---|---|
| **Location search** | City, ZIP, or neighborhood |
| **Bulk Zillow URLs** | Mix search pages and `homedetails` URLs in one run |
| **Listing types** | `for_sale`, `for_rent`, `sold` |
| **Deep filters** | Price, beds, baths, home type, days on Zillow |
| **Sort control** | Relevance, price, newest, beds, baths, sqft, lot size |
| **Zestimate included** | Zestimate and rent Zestimate returned where available |
| **Per-location limits** | `maxItems` applies per location/URL; `0` fetches up to Zillow's own page cap |
| **No Zillow API key needed** | Nothing to authenticate on your end |

***

### Input Parameters

Provide **at least one** of `locations` or `startUrls`.

| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `locations` | string\[] | No\* | — | City, ZIP, or neighborhood (e.g. `Austin, TX`, `90210`, `Brooklyn, NY`) |
| `startUrls` | string\[] | No\* | — | Zillow search or `homedetails` URLs; search URLs should include filters when possible |
| `listingType` | string | No | `for_sale` | `for_sale` | `for_rent` | `sold` |
| `maxItems` | integer | No | `200` | Max listings **per location or search URL**; `0` = unlimited, up to Zillow's own page cap (~800 per search) |
| `sortBy` | string | No | `newest` | `relevance` | `price_asc` | `price_desc` | `newest` | `beds` | `baths` | `sqft` | `lot` |
| `priceMin` / `priceMax` | integer | No | — | Minimum / maximum list, rent, or sold price in USD |
| `bedsMin` | integer | No | — | Minimum bedrooms |
| `bathsMin` | number | No | — | Minimum bathrooms |
| `homeTypes` | string\[] | No | — | `house`, `condo`, `townhome`, `multi_family`, `lot`, `manufactured`, `apartment`; empty = all types |
| `daysOnZillow` | string | No | `""` (any time) | Restrict to listings posted within a window: `1`, `7`, `14`, `30`, `90` days, or `6m`–`36m` |

\*One of `locations` or `startUrls` is required.

Example input:

```json
{
  "locations": ["Austin, TX"],
  "startUrls": ["https://www.zillow.com/austin-tx/"],
  "listingType": "for_sale",
  "maxItems": 200,
  "sortBy": "newest",
  "priceMin": 300000,
  "priceMax": 900000,
  "bedsMin": 3,
  "homeTypes": ["house", "condo"],
  "daysOnZillow": "30"
}
```

***

### Output Examples

Each output item is one flat property record — no nested objects to unpack.

```json
{
  "country": "US",
  "source": "zillow",
  "locationQuery": "New York, NY",
  "listingType": "for_sale",
  "zpid": "32347603",
  "propertyUrl": "https://www.zillow.com/homedetails/108-Ramblewood-Ave-Staten-Island-NY-10308/32347603_zpid/",
  "status": "FOR_SALE",
  "statusText": "House for sale",
  "listPrice": 899999,
  "priceDisplay": "$899,999",
  "zestimate": 886600,
  "rentZestimate": 3393,
  "taxAssessedValue": 634000,
  "beds": 4,
  "baths": 2,
  "sqft": 1716,
  "lotSqft": 3800,
  "homeType": "SINGLE_FAMILY",
  "daysOnZillow": 11,
  "formattedAddress": "108 Ramblewood Ave, Staten Island, NY 10308",
  "city": "Staten Island",
  "state": "NY",
  "zipCode": "10308",
  "latitude": 40.544956,
  "longitude": -74.15479,
  "brokerName": "Ashford Homes LLC",
  "isFsba": true,
  "isShowcaseListing": true,
  "has3DModel": true,
  "photoCount": 25,
  "thumbnail": "https://photos.zillowstatic.com/fp/86087c4049ebfcb2e4639abaaf136fab-p_e.jpg",
  "photos": [
    "https://photos.zillowstatic.com/fp/86087c4049ebfcb2e4639abaaf136fab-p_e.jpg",
    "https://photos.zillowstatic.com/fp/8af9f97b2bb0144f9bb8522add145576-p_e.jpg"
  ],
  "scrapedAt": "2026-09-22T07:55:08.497902+00:00"
}
```

> Note: `zestimate`, `rentZestimate`, `agentName`, and `agentPhone` are only populated when Zillow exposes them on that specific listing — some listings (e.g. new "Coming Soon" postings, or agent-withheld contact info) legitimately return `null` or an empty string for these fields rather than an error.

Export as JSON, CSV, Excel, or XML from the Apify dataset UI or API.

***

### Tips for Best Results

- **Filter before you fetch.** `priceMin`/`priceMax`, `bedsMin`, and `homeTypes` cut out noise and reduce billed results — broad, unfiltered city searches return the most data but also the most irrelevant rows.
- **Know Zillow's own page cap.** Setting `maxItems: 0` returns as many results as Zillow's search itself exposes per query — typically up to roughly 800 listings per location/URL, not unlimited. For very large or dense metros, narrow with `priceMin`/`priceMax` or `daysOnZillow` and run multiple sub-searches instead of one broad one.
- **Use `daysOnZillow` for monitoring.** Set it to `1` or `7` for a daily/weekly "what's new" run instead of re-scraping the same inventory repeatedly.
- **Mix `startUrls` and `locations`** when you need both a broad area sweep and a specific set of known listings in the same run.
- **Zestimate availability varies by listing** — don't assume every row will have one; check for `null` before using it in downstream valuation logic.

### Supported Locations

This Actor covers **the United States only**, via Zillow. It accepts city, ZIP/postal code, and neighborhood-level searches, plus any Zillow search or `homedetails` URL for markets Zillow itself covers.

***

### How to Scrape Zillow Listings

1. **Configure input** — add cities, ZIPs, or Zillow URLs to `locations` / `startUrls`, then set listing type and filters.
2. **Run the Actor** — click "Start" in the Apify Console, or trigger it via the Apify API, Python, or JavaScript SDK.
3. **Preview results** — listings stream into the dataset in real time as they're scraped.
4. **Export or automate** — download as JSON, CSV, or Excel, or pipe results into your CRM or database via Zapier, Make, n8n, or webhooks.

### Integrations

Results export natively to JSON, CSV, Excel, and XML, and connect directly to no-code tools like n8n, Zapier, and Make. Push scraped listings into HubSpot, Salesforce, Google Sheets, or a data warehouse (Postgres, BigQuery, Airtable) via the Apify API, SDKs, or scheduled webhook triggers.

### Use Zillow Data Scraper with MCP and AI Agents

This Actor can be called by AI agents through the [Apify MCP Server](https://mcp.apify.com/), letting an MCP-compatible assistant run it and reason over live Zillow data as part of a larger workflow — for example, an agent comparing Zestimate vs. list price across a market to flag potentially underpriced listings.

### Using the Apify API

**Node.js**

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });

const run = await client.actor('devil_port369-owner/zillow-scraper').call({
  locations: ['Austin, TX'],
  listingType: 'for_sale',
  maxItems: 100,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")

run = client.actor("devil_port369-owner/zillow-scraper").call(run_input={
    "locations": ["Austin, TX"],
    "listingType": "for_sale",
    "maxItems": 100,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

**cURL**

```bash
curl "https://api.apify.com/v2/acts/devil_port369-owner~zillow-scraper/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"locations": ["Austin, TX"], "listingType": "for_sale", "maxItems": 100}'
```

> Replace `devil_port369-owner/zillow-scraper` above with this Actor's actual published slug if it differs once live.

***

### Use This Data in AI-Agent and LLM Workflows

- **Valuation sanity-checks** — feed a batch of listings into an LLM prompt to flag properties where `listPrice` diverges significantly from `zestimate`, as a first-pass screen before deeper analysis.
- **Market summarization** — generate a plain-English market summary (price range, typical sqft, inventory mix, days-on-market trend) for a given city from a run's dataset.
- **Natural-language querying** — pass a run's export as context so an LLM can answer questions like "which of these are new construction under $600k?" without custom filtering code.
- **Cross-source enrichment** — combine this Actor's output with a Realtor.com-sourced dataset (e.g. this developer's own [USA Real Estate Scraper](https://apify.com/devil_port369-owner/usa-real-estate-scraper)) and have an LLM reconcile listings that appear on both platforms.

### Build a Production Real Estate Data Workflow

1. **Schedule recurring runs** per target market using Apify's Scheduler rather than triggering manually.
2. **Dedupe on `zpid`** — scheduled runs will re-encounter previously seen listings; use `zpid` as the stable key when merging into your own store.
3. **Combine multi-source coverage** — pair this Actor with a Realtor.com-sourced feed to catch listings that appear on one platform but not the other, and reconcile on address or coordinates.
4. **Push to storage via webhook** — configure an Apify webhook on run completion to send new dataset items to your own ingestion endpoint or directly into Postgres/BigQuery/Airtable.
5. **Monitor for silent failures** — set up a run-failure notification (Slack, email, or webhook) so a blocked run surfaces immediately.
6. **Version your input configs** — keep one input JSON per market/segment so scheduled runs stay reproducible and auditable.

### Is It Legal to Scrape Zillow?

This Actor only collects data Zillow already displays publicly on pages anyone can view without logging in. In the US, courts have generally held that scraping publicly accessible web data does not, by itself, violate the Computer Fraud and Abuse Act (see *hiQ Labs v. LinkedIn*). That said, Zillow's own Terms of Use place restrictions on automated data collection and reuse of its data (including Zestimate values, which Zillow treats as proprietary), and Zillow has pursued legal action over data-usage disputes in the past. Compliance also depends on how you use the data afterward — internal market research and bulk redistribution carry very different risk profiles.

This isn't legal advice. Review Zillow's current Terms of Use and consult a lawyer familiar with data-scraping and IP law for your specific use case, especially if you plan to redistribute or resell Zestimate data commercially.

***

### Frequently Asked Questions (FAQ)

#### How do I scrape Zillow?

Enter a location (city, ZIP, or neighborhood) or a Zillow URL into the Actor's input, choose a listing type, and run it. No coding, proxies, or Zillow login is required.

#### Does this include Zestimate data?

Yes, where Zillow exposes it on the listing — `zestimate` and `rentZestimate` fields are included. Some listings (particularly new or off-market-adjacent ones) may return `null` for these.

#### Can I scrape sold or rental listings, not just for-sale?

Yes — set `listingType` to `sold` or `for_rent`. All three market segments use the same input shape.

#### Why does `maxItems: 0` not return every listing in a city?

Zillow itself caps how many results a single search exposes (roughly 800 per search), independent of this Actor. For dense metros, narrow your search with price or date filters and run multiple sub-searches instead.

#### How is this different from a Realtor.com scraper?

Zillow and Realtor.com list overlapping but not identical inventory, and each source has fields the other doesn't — Zillow uniquely offers Zestimate valuations, for instance. Using both sources together (see the production-workflow section above) typically gives broader market coverage than either alone.

#### Can I integrate this with my CRM or spreadsheet tools?

Yes — use the Apify API, webhooks, or Python/JS SDKs to push listings into HubSpot, Salesforce, Google Sheets, or your own data warehouse.

***

### SEO Keywords

zillow scraper, scrape zillow, zillow data scraper, how to scrape zillow, zillow web scraper, zillow api alternative, zillow listings export, zillow zestimate scraper, real estate market data scraper

***

Built by **DataFusionX**

# Actor input Schema

## `locations` (type: `array`):

City, ZIP, or neighborhood (e.g. Austin, TX / 90210 / Brooklyn, NY).

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

Optional Zillow search or homedetails URLs. Search URLs should include filters when possible.

## `listingType` (type: `string`):

Market segment to query.

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

Max listings PER location or search URL. 0 = unlimited (until Zillow page cap ~800).

## `sortBy` (type: `string`):

Result ordering.

## `priceMin` (type: `integer`):

Minimum list / rent / sold price.

## `priceMax` (type: `integer`):

Maximum list / rent / sold price.

## `bedsMin` (type: `integer`):

Minimum bedrooms.

## `bathsMin` (type: `number`):

Minimum bathrooms.

## `homeTypes` (type: `array`):

Leave empty for all types.

## `daysOnZillow` (type: `string`):

Only listings listed within this window (for\_sale / for\_rent).

## Actor input object example

```json
{
  "locations": [
    "Austin, TX"
  ],
  "startUrls": [
    "https://www.zillow.com/austin-tx/"
  ],
  "listingType": "for_sale",
  "maxItems": 200,
  "sortBy": "newest",
  "daysOnZillow": ""
}
```

# 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 = {
    "locations": [
        "Austin, TX"
    ],
    "startUrls": [
        "https://www.zillow.com/austin-tx/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("devil_port369-owner/zillow-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 = {
    "locations": ["Austin, TX"],
    "startUrls": ["https://www.zillow.com/austin-tx/"],
}

# Run the Actor and wait for it to finish
run = client.actor("devil_port369-owner/zillow-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 '{
  "locations": [
    "Austin, TX"
  ],
  "startUrls": [
    "https://www.zillow.com/austin-tx/"
  ]
}' |
apify call devil_port369-owner/zillow-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,devil_port369-owner/zillow-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/s2k0Bym6GjEKw2FE6/builds/bolugR9dPo7narIMR/openapi.json
