# Zillow Search Scraper — listings by city or ZIP (`mu0i/zillow-search-scraper`) Actor

Scrape Zillow listings for sale, for rent or recently sold by typing a city, ZIP or neighborhood — no search URL needed. Get price, beds, baths, sq ft, year built, Zestimate, tax value, photos and a link per listing, up to 1,000 per place. Export JSON, CSV or Excel, run via API or on a schedule.

- **URL**: https://apify.com/mu0i/zillow-search-scraper.md
- **Developed by:** [Mu0i](https://apify.com/mu0i) (community)
- **Categories:** Real estate, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 listings

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

**Scrape Zillow listings** by typing a place the way you would into Zillow's search box — **"Austin, TX"**,
**"78701"**, **"Pacific Heights, San Francisco, CA"**, a county — choose **for sale**, **for rent** or
**recently sold**, and get one clean row per listing: price, beds, baths, living area, lot, year built, home
type, **Zestimate**, tax assessment, days on Zillow, open houses, photos and a link. No Zillow search URL to
hand-craft, no browser, no proxy setup. Export to **JSON, CSV, Excel or XML**, run it on a schedule, call it
from the **API**, or plug it into Make, Zapier, Google Sheets or an AI agent through Apify's integrations.

- 🏠 **Up to 1,000 listings per place** — Zillow's own ceiling per search, walked to the last row. The
  website's search stops at 820.
- 🔎 **Real inputs** — a place, a listing type, a sort. Not a `searchQueryState` URL you have to build.
- 🏢 **Rentals done right** — apartment buildings come back as one row with the rent range and a per-bedroom
  breakdown, single homes as another; every row says which.
- 📄 **Full property record on request** — tick one box and each listing carries its facts, price and tax
  history, schools, agent and photo gallery.
- 💵 **Pay per listing**, never for a run that found nothing.

### What Zillow data can you extract?

| You type | You get, per listing |
|---|---|
| A city, ZIP, neighborhood or county, plus **for sale** | price, price per sq ft, price changes, beds, baths, living area, lot size, home type, year built, listing flags (FSBO, open house, new construction…), days on Zillow, listed date, Zestimate and rent Zestimate, tax assessment, open houses, 3D tour / floor plan links, photos, link |
| … plus **for rent** | the same for single homes and units, and for apartment **buildings**: building name, rent range, base rent range, required monthly fees, unit count and a per-bedroom price breakdown |
| … plus **recently sold** | the sold price and sale date on top of the listing fields |
| … plus **Add the full property record** | everything the [Zillow Property Scraper](https://apify.com/mu0i/zillow-property-scraper) returns, in a `details` field: 23 curated facts, price history, tax history, schools with ratings, listing agent and broker, MLS id, walk/transit/bike scores, nearby homes, description, engagement |

### How to scrape Zillow listings

1. Click **Try for free** at the top of this page. Apify creates a free account if you do not have one — no card is needed to run this, and the free credit covers about 3,300 listings.
2. Put the places to search into **Places to search** — one per line, as you would type them into Zillow.
3. Pick the **Listing type**, set **Max results per place** to bound the run, then press **Start**. You are billed per row, so this field is also your budget.
4. Rows show up in the **Dataset** tab *while the run is still going*. Export to JSON, CSV, Excel or XML from there, or read them over the API.

Tick **Add the full property record to every row** to get the facts, price and tax history, schools, agent
and photo gallery for every listing in a `details` field — the same record the **Zillow Property Scraper**
returns, without a second run.

### What this Zillow scraper measured

Everything this Actor promises was measured on live Zillow data, and the numbers are the ones below — not
round numbers from a brochure.

**One search is at most 1,000 listings — Zillow's rule, not ours.** Zillow's API rejects any page past
1,000 results and says so in its own error. Measured on Austin, TX (5,673 for sale): pages 1–25 at 40 per
page all returned 40 rows with distinct leading zpids; page 26 and beyond is refused.

| | |
|---|---|
| page 25 × 40 (row 1,000) | 40 rows, distinct |
| page 26 × 40 | refused — at most 1,000 results per search |

So **Max results per place** is capped at 1,000, and the Actor walks to exactly there. For more in a big
city, list its ZIP codes or neighborhoods as separate places — each is its own 1,000.

**A rental search returns two shapes, and every row says which.** Zillow counts an apartment *building* as
one result, with a rent range and a per-bedroom breakdown, alongside single homes and units. Measured on
ZIP 78704 for rent: 25 buildings and 15 homes in the first 40. Rows carry `resultType: "home"` or
`"rentalBuilding"`; a building's fields (`title`, `minPrice`, `maxPrice`, `units`, `unitCount`) are null on a
home and a home's (`price`, `bedrooms`, `livingArea`…) are null on a building, rather than the two being
squeezed into one shape.

**The sort orders are Zillow's own.** *Newest* returned listings at 0 days on Zillow; *Price: high to low*
led with $24.9M. *Price: low to high* puts Zillow's **$0 "price on request"** and auction listings first —
that is the data as Zillow holds it, passed through with its `+` suffix (`price: 0, priceSuffix: "+"`)
rather than hidden.

### Input

| field | | |
|---|---|---|
| `locations` | required | One place per line: city, ZIP, neighborhood or county, as typed into Zillow. Each is searched separately and tagged in the output |
| `status` | `forSale` | `forSale` (agent listings, FSBO, coming soon, new construction, auctions), `forRent`, or `sold` (recently sold) |
| `maxResultsPerLocation` | 100 | Up to 1,000 — Zillow's own ceiling per search |
| `sortBy` | `relevance` | `relevance`, `newest`, `price_desc`, `price_asc`, `most_bedrooms`, `largest`, `largest_lot`, `newest_built` |
| `enrichWithDetails` | off | Attach the full property record to each row as `details`. Billed as `property` instead of `result` — see pricing |

Every field, with its default — paste the whole block and delete what you do not need:

```json
{
  "locations": [
    "Austin, TX"
  ],
  "status": "forSale",
  "maxResultsPerLocation": 100,
  "sortBy": "relevance",
  "enrichWithDetails": false
}
```

### Output example

Every row carries `location` (the place it was found for), `status` and `resultType`, so several places can
share one dataset.

**Every row** — `zpid`, `url`, `street`, `city`, `state`, `zip`, `buildingId`, `lat`, `lng`, `currency`,
`listingStatus`, `listedAt`, `photo`, `photoCount`, `photos[]`, `has3DTour`, `hasVideo`, `isShowcase`,
`isFeatured`.

**Homes** (`resultType: "home"`) — `price`, `priceSuffix`, `pricePerSqft`, `priceChange`, `priceChangedAt`,
`bedrooms`, `bathrooms`, `fullBathrooms`, `halfBathrooms`, `livingArea`, `lotSize`, `lotSizeUnit`,
`propertyType`, `yearBuilt`, `marketingStatus`, `listingFlags[]` (Zillow's own flags, verbatim — `isFSBA`,
`isOpenHouse`, `isNewConstruction`…), `daysOnZillow`, `lastSoldAt` (sold only), `zestimate`, `rentZestimate`,
`taxAssessedValue`, `taxAssessmentYear`, `openHouses[]`, `hasFloorPlan`, `virtualTourUrl`, `floorPlanUrl`,
`isPreforeclosureAuction`.

**Rental buildings** (`resultType: "rentalBuilding"`) — `title` (the building's name), `minPrice`,
`maxPrice`, `minBaseRent`, `maxBaseRent`, `requiredMonthlyFees`, `unitCount`, `units[]` (`bedrooms`,
`minPrice` per bedroom count), `groupType`, `isRoomForRent`, `isBuildToRent`, `acceptsApplications`.

**With full records** — `details`, the same object the Zillow Property Scraper returns: `facts` (beds, baths,
area, lot, year, type, parking, heating, cooling, appliances, HOA, tax…), `priceHistory[]`, `taxHistory[]`,
`schools[]`, `agent`, `broker`, `mls`, `scores` (walk/transit/bike), `nearbyHomes[]`, `photos[]`,
`description`, `engagement`.

One home row, exactly as it lands in your dataset (photo list shortened):

```json
{
  "location": "78701",
  "status": "forSale",
  "resultType": "home",
  "zpid": "83816995",
  "url": "https://www.zillow.com/homedetails/83816995_zpid/",
  "street": "805 Congress Ave",
  "city": "Austin",
  "state": "TX",
  "zip": "78701",
  "lat": 30.270214,
  "lng": -97.74169,
  "price": 18999000,
  "pricePerSqft": 1218,
  "priceChange": -901000,
  "priceChangedAt": "2026-08-25T05:00:00.000Z",
  "currency": "USD",
  "bedrooms": 3,
  "bathrooms": 8,
  "fullBathrooms": 4,
  "halfBathrooms": 4,
  "livingArea": 15600,
  "lotSize": 3920.4,
  "lotSizeUnit": "squareFeet",
  "propertyType": "singleFamily",
  "yearBuilt": 1871,
  "listingStatus": "forSale",
  "marketingStatus": "active",
  "listingFlags": ["isFSBA"],
  "daysOnZillow": 131,
  "listedAt": "2026-05-15T21:59:20.730Z",
  "zestimate": null,
  "rentZestimate": null,
  "taxAssessedValue": 4768097,
  "taxAssessmentYear": "2025",
  "openHouses": [],
  "photo": "https://photos.zillowstatic.com/fp/d298abe245ea58946a69a3b205ac2c06-p_f.jpg",
  "photoCount": 39,
  "photos": ["https://photos.zillowstatic.com/fp/d298abe245ea58946a69a3b205ac2c06-p_f.jpg", "…"],
  "has3DTour": false,
  "hasVideo": false,
  "hasFloorPlan": false,
  "isShowcase": false,
  "isFeatured": false,
  "isPreforeclosureAuction": false,
  "note": null
}
```

And one rental building, from `"Austin, TX"` for rent:

```json
{
  "location": "Austin, TX",
  "status": "forRent",
  "resultType": "rentalBuilding",
  "zpid": "446904283",
  "url": "https://www.zillow.com/homedetails/446904283_zpid/",
  "title": "Lookout",
  "street": "13059 Four Star Blvd",
  "city": "Austin",
  "state": "TX",
  "zip": "78737",
  "minPrice": 1305,
  "maxPrice": 2660,
  "minBaseRent": 1249,
  "maxBaseRent": 2604,
  "requiredMonthlyFees": 56,
  "unitCount": 46,
  "units": [
    { "bedrooms": 1, "minPrice": 1305 },
    { "bedrooms": 2, "minPrice": 1875 },
    { "bedrooms": 3, "minPrice": 2640 }
  ],
  "groupType": "apartmentComplex",
  "listingStatus": "forRent",
  "photoCount": 3,
  "…": "home-only fields are null"
}
```

### How much does it cost to scrape Zillow?

Pay per result — you are billed for rows you receive, not for time or compute.

| Event | Per 1,000 | What one unit is |
|---|---:|---|
| `result` | $1.50 | one listing row in the dataset |
| `property` | $2.00 | one listing row carrying the full property record (`enrichWithDetails` on) — instead of `result`, never on top of it. A listing whose full record could not be fetched, or came back only partially, keeps its search fields and is charged as a `result` |

**Paid Apify plans pay less than this.** The table above is the free-plan rate. Starter, Scale and Business each get a lower per-unit price — up to **40% off** on Business — and the Actor's Pricing tab shows every tier before you run anything.

**Is scraping Zillow free to try?** Yes: Apify's free plan comes with monthly credit, enough for roughly 3,300 listings at the rate above, and no card is needed.

**Starting a run costs $0.00001** — a cent per thousand runs — and there is no run minimum. **You are never billed for a run that found nothing.** If a place has no listings of that type, or Zillow does not know the place, the run says so in a row of the dataset — never a silently empty result, and never a charge. A run that fails because the data could not be *reached* fails loudly, because that one is worth retrying. Your run budget (`maxTotalChargeUsd`) is met exactly: the run delivers what fits and stops, never one row over.

### Use cases

- **Market analysis** — inventory, price per square foot and days on market by ZIP, refreshed on a schedule.
- **Investor and agent lead lists** — FSBO listings, price cuts, long days-on-market, new construction — the `listingFlags` and `priceChange` fields are made for filters.
- **Rental comps** — building rent ranges and per-bedroom prices across a city in one run.
- **Portfolio and watchlist monitoring** — schedule the same places daily and diff the dataset.
- **Datasets for analysis and AI** — clean, typed rows with stable field names; add the full record for training-grade detail.

### Integrations and API

Every Actor is an API endpoint. This starts a run, waits for it, and returns the rows in one call — swap in your token from **Settings → Integrations**:

```bash
curl -X POST 'https://api.apify.com/v2/acts/mu0i~zillow-search-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{ "locations": ["Austin, TX"], "status": "forSale", "maxResultsPerLocation": 100 }'
```

From JavaScript with the Apify client:

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('mu0i/zillow-search-scraper').call({
    locations: ['Austin, TX'], status: 'forSale', maxResultsPerLocation: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

From Python:

```python
from apify_client import ApifyClient

client = ApifyClient('YOUR_TOKEN')
run = client.actor('mu0i/zillow-search-scraper').call(run_input={
    'locations': ['Austin, TX'], 'status': 'forSale', 'maxResultsPerLocation': 100,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

Schedule it, get a **webhook** when a run finishes, or connect it to **Make, Zapier, Google Sheets, Slack**
and the rest of Apify's integrations from the Actor's Integrations tab. Every Apify Actor is also a tool for
AI agents through the **Apify MCP server**, so an assistant can search Zillow for you in a sentence.

### Good to know

**1,000 listings per place.** Zillow's API refuses anything deeper (see above). The Actor stops there and
says so in the log; it does not page in circles or pad the count. Split a big city into ZIPs or
neighborhoods to go past it.

**No price, beds or baths filter yet.** Zillow's search body carries filter blocks that have not been
measured end to end, and a filter that quietly does nothing is worse than no filter. Sort by price (either
direction) and cut the list; filters will be added once each is confirmed to genuinely narrow the results.

**Zestimates are Zillow's to withhold.** `zestimate` and `rentZestimate` are null where Zillow's own search
does not carry one — on many luxury and new-construction listings. The value is the API's, never estimated
here.

**`url` is Zillow's short canonical link** (`/homedetails/<zpid>_zpid/`); it redirects to the full address
URL. **A sold home's `price` is the price on Zillow's sold card**, and `lastSoldAt` is the sale date. Rows
are deduplicated by zpid within a run, per place. **Runs do not time out on you:** if your run timeout is
about to hit, the Actor stops ahead of it with everything collected so far and says so in the status
message. A temporary problem on our side is retried for about half a minute, then per listing, before a row
is given up on; rows already delivered always stand.

### Related Zillow Actors

| Actor | What it does |
|---|---|
| [Zillow Property Scraper — full record by zpid](https://apify.com/mu0i/zillow-property-scraper) | The full record for any zpid or listing URL — facts, price and tax history, schools, agent, scores, photos. Works for off-market homes. |

### FAQ

**Is it legal to scrape Zillow?** This Actor reads listing data that is publicly available to any visitor
without logging in, and scraping public data is generally lawful. What you do with it is your
responsibility: comply with Zillow's terms where they apply to you, treat agent names and phone numbers as
personal data under GDPR and CCPA, and do not republish listings wholesale. This is not legal advice.

**Do I need a Zillow API key?** No. Zillow's public API for listings has been closed for years; this Actor
needs no key, no account and no proxy setup.

**How many Zillow listings can I get?** Up to 1,000 per place per run, which is Zillow's own ceiling per
search. A city with more listings is split into its ZIP codes or neighborhoods, each its own 1,000.

**How fresh is the data?** Every run reads Zillow live at the moment it runs; nothing is cached between
runs. Schedule the Actor to keep a dataset current.

**Can I search by address, or get one home?** Use the [Zillow Property Scraper](https://apify.com/mu0i/zillow-property-scraper)
with the home's zpid or Zillow URL; it returns the full record, including for off-market homes.

**Why is `zestimate` null on some rows?** Zillow withholds it on some listings, notably luxury and new
construction. The Actor never estimates a value itself.

**What export formats are there?** JSON, CSV, Excel and XML from the Dataset tab, or any of them over the API.

**Something is wrong.** Open a ticket in the **Issues** tab of this Actor with the run id from the run's
URL; every run carries one, and it is enough to find exactly what happened.

# Changelog

This Actor's version history is a separate document: https://apify.com/mu0i/zillow-search-scraper/changelog.md

# Actor input Schema

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

One per line, as you would type it into Zillow: a city ("Austin, TX"), a ZIP ("78701"), a neighborhood ("Pacific Heights, San Francisco, CA") or a county. Each is searched separately and tagged in the output. No search URL needed.

## `status` (type: `string`):

For sale covers agent listings, FSBO, coming soon, new construction and auctions. Sold is recently sold. For rent returns apartment buildings as one row each with their unit ranges (see README).

## `maxResultsPerLocation` (type: `integer`):

Zillow's API serves at most 1,000 results per search (it rejects anything deeper), so this is capped at 1,000. For a big city, list its ZIP codes or neighborhoods as separate places to get more. You are billed per row, so this is also your budget.

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

Zillow's own sort orders. Relevance is Zillow's default ranking for the listing type. Newest is by days on Zillow, ascending.

## `enrichWithDetails` (type: `boolean`):

Fetches the full Zillow record for each listing (facts, price and tax history, schools, agent, scores, photo gallery) into a `details` field. Rows with details are billed as `property` instead of `result` — see Pricing. Slower: about a second per listing, run 20 at a time.

## Actor input object example

```json
{
  "locations": [
    "Austin, TX"
  ],
  "status": "forSale",
  "maxResultsPerLocation": 100,
  "sortBy": "relevance",
  "enrichWithDetails": false
}
```

# Actor output Schema

## `listings` (type: `string`):

One row per listing, tagged with the place it was found for.

# 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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mu0i/zillow-search-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"] }

# Run the Actor and wait for it to finish
run = client.actor("mu0i/zillow-search-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"
  ]
}' |
apify call mu0i/zillow-search-scraper --silent --output-dataset

```

## MCP server setup

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