# Zillow Search Scraper (`mlg14/zillow-search-scraper`) Actor

Search public Zillow property listings by ZIP code. Export sale, rental, and recently sold homes with prices, addresses, photos, property facts, and listing links.

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

## Pricing

$1.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.

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 Search Scraper

Scrape Zillow property listings by US ZIP code, including homes for sale, rentals, and recently sold properties. Export Zillow search data to JSON, CSV, or Excel with prices, addresses, listing links, property facts, photos, and status fields. This provides a structured Zillow search data source for recurring market research and listing comparisons.

Search results are collected from public search pages. Each dataset item represents a search card: a home, sold property, or rental building. The actor follows result pages, removes repeated identifiers within each ZIP code, and stops at the limits you set. A run can cover several ZIP codes. When you select more than one listing category, results are collected in this order: for sale by agent, for sale by owner, for rent, then recently sold. The result limit for a ZIP code applies across those categories.

### What data can you extract from Zillow?

The output uses one stable record shape across sale, rent, and sold searches. A field is `null` when the public search card does not supply it. For instance, a sold property has `listingSoldPrice` and may have no active `listingPriceAmount`; a rental building can have `rentalUnits` instead of one bedroom count. These values describe the search card at collection time, not a complete property history.

| Field | Description | Example |
| --- | --- | --- |
| `zpid` | Listing or building identifier | `458671486` |
| `providerListingId` | Provider identifier for rental buildings when present | `2336vc4z7pn5g` |
| `propertyUrl` | Public property or building page URL | Property link |
| `sourceSearchUrl` | Search URL that yielded the listing | Search link |
| `searchZipCode` | Requested ZIP code | `10014` |
| `listingStatus` | forSale, forRent, or sold | `forSale` |
| `statusText` | Status shown on the search card | `Active` |
| `homeType` | Property type such as CONDO | `CONDO` |
| `cardType` | home or building | `home` |
| `listingPriceAmount` | Sale price or starting monthly rent | `85000000` |
| `listingPriceCurrency` | ISO price currency | `USD` |
| `listingPriceFormatted` | Displayed sale or rental price | `$85,000,000` |
| `listingSoldPrice` | Sold price for recently sold listings | `2285000` |
| `addressFull` | Full displayed address | `125 Perry St #Phe, New York, NY 10014` |
| `addressStreet` | Street address | `125 Perry St #Phe` |
| `addressUnit` | Unit or apartment | `# PHE` |
| `addressCity` | City | `New York` |
| `addressState` | Two-letter state code | `NY` |
| `addressZipCode` | Listing ZIP code | `10014` |
| `addressCountry` | Country | `USA` |
| `latitude` | Latitude | `40.734997` |
| `longitude` | Longitude | `-74.007034` |
| `bedrooms` | Bedroom count | `6` |
| `bathrooms` | Bathroom count | `10` |
| `livingArea` | Living area | `7700` |
| `livingAreaUnit` | Living area unit | `sqft` |
| `lotArea` | Lot area | `2500` |
| `lotAreaUnit` | Lot area unit | `sqft` |
| `daysOnZillow` | Days shown in structured listing data | `300` |
| `dateSold` | Sold date in YYYY-MM-DD format | `2026-09-25` |
| `zestimate` | Estimated home value when supplied | `2923300` |
| `rentZestimate` | Estimated monthly rent when supplied | `4500` |
| `taxAssessedValue` | Tax assessed value when supplied | `211115` |
| `priceChange` | Most recent price change amount | `-100000` |
| `priceChangedAt` | Most recent price change date | `2026-09-15` |
| `priceReduction` | Displayed price reduction text | `$100,000 (Sep 15)` |
| `hasImage` | Whether an image is available | `true` |
| `has3DModel` | Whether a 3D tour is available | `false` |
| `hasVideo` | Whether a video is available | `false` |
| `mainImage` | Main listing photo URL | Photo link |
| `listingPhotos` | Photo URLs and captions from the search card | `[{"url": "...", "caption": null}]` |
| `photoCount` | Number of listed photos | `17` |
| `isZillowOwned` | Whether the listing is marked as owned by Zillow | `false` |
| `isFSBA` | For-sale-by-agent flag | `true` |
| `isFSBO` | For-sale-by-owner flag | `false` |
| `isComingSoon` | Coming-soon flag | `false` |
| `isPending` | Pending flag | `false` |
| `isOpenHouse` | Open-house flag | `true` |
| `isNewHome` | New-home flag | `false` |
| `isForAuction` | Auction flag | `false` |
| `isForeclosure` | Foreclosure flag | `false` |
| `isBankOwned` | Bank-owned flag | `false` |
| `contingencyType` | Contingent listing type | `null` |
| `brokerName` | Listing brokerage shown on the card | `Compass` |
| `marketingTagline` | Short card highlight | `Outdoor kitchen` |
| `cardHighlight` | Card highlight category | `homeInsight` |
| `openHouse` | Open-house details or displayed open-house time | `Tue. 4:30-5:30pm` |
| `factsAndFeatures` | Additional card facts when supplied | Feature object |
| `marketingTreatments` | Marketing labels on the listing | `["paid"]` |
| `priceIncludesMonthlyFees` | Whether shown rent includes required monthly fees | `false` |
| `isRentalWithBasePrice` | Whether rental price is a base price | `true` |
| `availabilityDate` | Rental availability date when supplied | `2026-10-07 00:00:00` |
| `availabilityCount` | Available unit count on a rental building | `2` |
| `rentalUnits` | Available unit summaries for a rental building | Unit summaries |
| `isBuilding` | Whether the card represents a rental building | `false` |
| `scrapedAt` | UTC collection timestamp | UTC timestamp |
| `isValid` | Whether identifier, URL, and address are present | `true` |

`listingPhotos` contains photo objects with `url` and optional `caption`. `rentalUnits` contains the unit summaries shown on building cards, such as a displayed starting rent and bedroom count. Coordinates are separate numeric fields, which makes the CSV export easier to map. Prices remain numeric where the source gives a number; the displayed price is preserved separately because rental cards may show a plus sign or monthly suffix. For sold records, `dateSold` uses a date string when Zillow includes a timestamp in the search card.

### How to scrape Zillow by ZIP code

1. Enter one or more five-digit US ZIP codes in `zipCodes`. Keep leading zeroes: enter `07306` as text, not a number.
2. Select one or more listing categories. The default is homes for sale by agent. Enable rentals or recently sold properties when those are the records you need.
3. Set price, listing-age, and minimum-bedroom filters if needed. For rent searches, price values refer to monthly rent. Use `resultsLimit` to cap each ZIP code and `maxItems` to cap the whole run.
4. Run the actor. Open the default dataset to review the listing records, then download JSON, CSV, or Excel.

The actor searches each ZIP code separately. If you choose multiple categories and want a balanced number from each, run them separately: the first selected category may fill that ZIP code's result limit. A listing that appears in more than one selected category is included once for that ZIP code. Results from distinct ZIP code searches remain separate so each output row keeps its `searchZipCode` context.

### Input

| Name | Type | Default | What it controls |
| --- | --- | --- | --- |
| `zipCodes` | string list | Required | US ZIP codes to search. |
| `forSaleByAgent` | boolean | `true` | Homes offered for sale by agents. |
| `forSaleByOwner` | boolean | `false` | Owner-listed sale homes, where available. |
| `forRent` | boolean | `false` | Rental homes and apartment building cards. |
| `sold` | boolean | `false` | Recently sold properties. |
| `priceMin` | integer | Unset | Minimum sale price or monthly rent. |
| `priceMax` | integer | Unset | Maximum sale price or monthly rent. |
| `daysOnZillow` | selection | Any | One of the public search time windows, from one day to 36 months. |
| `minBeds` | integer | Unset | Minimum bedroom count. |
| `resultsLimit` | integer | `100` | Maximum unique results per ZIP code across selected categories; `0` removes this cap. |
| `maxItems` | integer | `0` | Maximum output rows across the run; `0` removes this cap. |
| `proxyConfiguration` | object | Platform proxy | Connection configuration for public pages. |

Example for a sale search in two ZIP codes:

```json
{
  "zipCodes": ["10014", "07306"],
  "forSaleByAgent": true,
  "forSaleByOwner": false,
  "forRent": false,
  "sold": false,
  "priceMax": 5000000,
  "minBeds": 2,
  "resultsLimit": 100
}
```

Example for rental research:

```json
{
  "zipCodes": ["10014"],
  "forSaleByAgent": false,
  "forRent": true,
  "priceMin": 3000,
  "priceMax": 8000,
  "resultsLimit": 50
}
```

Select at least one listing category. If both price bounds are provided, the minimum must not exceed the maximum. An empty ZIP code list or a value that is not five digits stops the run with an input error. A ZIP code can have zero results for a narrow category, especially owner-listed homes or tight price filters.

### Output example

This shortened item is from the verified sale run for ZIP code `10014`. The actual dataset row also includes optional flags, valuation fields, photo objects, and collection metadata.

```json
{
  "zpid": "458671486",
  "propertyUrl": "https://www.zillow.com/homedetails/125-Perry-St-PHE-New-York-NY-10014/458671486_zpid/",
  "sourceSearchUrl": "https://www.zillow.com/homes/10014_rb/",
  "searchZipCode": "10014",
  "listingStatus": "forSale",
  "statusText": "Active",
  "homeType": "CONDO",
  "listingPriceAmount": 85000000,
  "listingPriceCurrency": "USD",
  "listingPriceFormatted": "$85,000,000",
  "addressFull": "125 Perry St #Phe, New York, NY 10014",
  "addressCity": "New York",
  "addressState": "NY",
  "addressZipCode": "10014",
  "latitude": 40.734997,
  "longitude": -74.007034,
  "bedrooms": 6,
  "bathrooms": 10,
  "livingArea": 7700,
  "livingAreaUnit": "sqft",
  "daysOnZillow": 300,
  "hasImage": true,
  "photoCount": 17,
  "brokerName": "Compass",
  "isFSBA": true,
  "isValid": true
}
```

The displayed price and availability can change after collection. Use `scrapedAt` to distinguish snapshots, and use `zpid` with `searchZipCode` to compare repeated runs. A rental building may use a location-like identifier rather than a numeric home identifier; treat `zpid` as text. `providerListingId` can give an additional rental identifier when the public card contains one.

### Use cases

- Compare asking prices, living area, and bedroom counts across nearby ZIP codes. Keep the source link beside each row so a reviewer can inspect a listing.
- Track recently sold prices in a neighborhood and compare them with currently listed asking prices. Run sale and sold searches separately when you need a fixed number of each.
- Build a rental inventory by ZIP code, including building cards with available unit summaries and starting monthly rents.
- Monitor new listings within a selected public time window. Store periodic exports and compare identifiers between runs to find additions and removals.
- Prepare a property research sheet for analysts, investors, or local service teams using CSV or Excel. Filter on `listingStatus`, `homeType`, price, and location fields.
- Review public listing coverage and data quality. Optional fields and `isValid` help identify which cards have enough information for a downstream workflow.

These are listing discovery uses. The actor does not open every property detail page, assess investment value, verify legal title, or provide a complete sold transaction record. Confirm decisions against current source pages and independent records.

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

The actor is configured at **$0.001 per delivered result**, or **$1 per 1,000 results**, with pay-per-result charging. A run returning 100 rows has $0.10 in result charges; 1,000 rows has $1.00; 5,000 rows has $5.00. Account usage and any applicable platform charges can appear separately in your run details. The result limit and account budget can stop a run before all accessible pages are collected.

A request for 100 results is a cap, not a guarantee: the selected ZIP code and filters may yield fewer. Conversely, selecting multiple ZIP codes can produce a larger total than one ZIP code's `resultsLimit`. Set `maxItems` when you need a strict total result count and a predictable maximum result charge. Filtering narrowly before a run also avoids spending result charges on records you do not plan to use.

### Tips for best results

Begin with one ZIP code and one listing category to check coverage. If the ZIP code has many results, set `resultsLimit` to the number you actually need. Search several ZIP codes in one run when the same filters apply to all of them. For different price ranges or category mixes, use separate runs so each output is easy to interpret.

The price filter is applied by the public search page. For-sale amounts are property prices, while rental amounts are monthly prices shown on rental cards. A rental building can present several unit prices; `listingPriceAmount` is parsed from the first displayed unit, and `rentalUnits` retains the unit summaries. For a precise unit-level comparison, inspect those summaries rather than assuming every unit costs the card's starting price.

Use the listing-age selection for a shorter view of current activity. Zillow's public time-window choices vary by category; verify the chosen period against the output, especially for sold listings. `daysOnZillow` is copied from the search card when present and can differ from a simple calculation based on the current date. A missing value is represented as `null`, not zero.

For a regular snapshot, keep the same ZIP codes and filters across runs. Compare `zpid` and `listingStatus`, and use `scrapedAt` to order snapshots. A changed price or missing listing in a later result does not by itself prove a sale: it can also reflect revised filters, ranking, temporary availability, or a public page change.

### Limits

The actor reads structured data embedded in public Zillow search pages. Search card fields are less complete than detail pages. Some listings omit living area, valuation, tax value, broker, photo gallery, exact unit details, or sold date. Recently sold cards may show map or street-view imagery rather than property photos; `mainImage` is then empty. Building cards may omit home type, beds, baths, and living area while supplying available units.

The actor follows up to 20 result pages per ZIP code and category. That protects runs from unbounded paging, but a broad sold search can have more results than those pages expose. Narrow the search by price, listing age, or ZIP code when you need better coverage. The public site can also limit, reorder, or change its result pages. Repeated identifiers within a ZIP code are removed. This makes output stable for one run but does not guarantee every listing in the wider market appears.

Public pages sometimes reject automated traffic. The actor starts with datacenter connections, rotates sessions after blocks, and can fall back to residential connections. That fallback may use paid proxy capacity. If a category has no results, first verify the ZIP code and filters; some owner-listed searches genuinely return zero. If all searches fail, the run reports an error. When one search fails after others succeed, delivered results remain available and the failure appears in the run log.

The actor accepts US five-digit ZIP codes only. It does not support city names, arbitrary map polygons, addresses, saved searches, login-only data, or private account content. Values reflect what the public search page displayed when collected. They should be treated as leads for review rather than certified property records.

### FAQ

#### Is scraping public listings allowed?

Rules depend on the site terms, local law, and how you use the data. Limit collection to publicly visible information, review the applicable terms, and avoid misuse of personal data. This actor is designed for public listing cards and does not sign into accounts. If your use is regulated or high stakes, get appropriate advice before relying on an export.

#### Do I need to configure a proxy?

The default connection setting is ready for normal runs. The actor tries lower-cost connections first and can change route after a block. You can provide a proxy configuration if your account or workflow requires one. Public-site blocking can still vary by time and location.

#### How long does a run take?

It depends on ZIP code count, result pages, filters, and whether the public site blocks a request. A verified 40-result search completed successfully, but larger searches and connection retries take longer. The run log shows progress and any search-level failures.

#### Can I schedule or monitor repeated searches?

Yes. Schedule runs and compare datasets over time. Keep the input fixed if you want comparable snapshots. Monitor both item count and the fill of fields you rely on, because the public page can change its card data without changing the input.

#### Can I export to a spreadsheet?

Yes. Download the default dataset as CSV or Excel, or use JSON for a programmatic workflow. Flattened address, location, price, and property facts make spreadsheet sorting straightforward. Arrays such as `listingPhotos` and `rentalUnits` remain structured values in JSON.

#### Why is a field empty?

The search card may not publish it for that listing type. Building cards, home cards, and sold cards expose different facts. Empty optional fields are `null`, while an available photo or unit list may be an empty array. Open the `propertyUrl` to check the current public page if that field matters.

#### Why are there fewer results than the limit?

`resultsLimit` is a maximum. Tight filters, owner-listed inventory, duplicate cards, the site's pagination cap, and changing public availability can reduce the number delivered. For broader coverage, try a larger time window, fewer filters, or several adjacent ZIP codes.

### Integrations

Use the dataset endpoint to read results from another application, or connect run completion to a webhook. Scheduled runs can feed a spreadsheet, database, or reporting workflow. The standard platform integrations also support common automation services. Keep the input and `scrapedAt` with each export so later comparisons have the right context.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request.

# Actor input Schema

## `zipCodes` (type: `array`):

Five-digit US ZIP codes to search. Keep leading zeroes as text.

## `forSaleByAgent` (type: `boolean`):

Include homes listed for sale by an agent.

## `forSaleByOwner` (type: `boolean`):

Include owner-listed homes. Some ZIP codes have no matching listings.

## `forRent` (type: `boolean`):

Include rental homes and apartment buildings.

## `sold` (type: `boolean`):

Include recently sold properties.

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

Minimum sale price or monthly rent, depending on the listing category.

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

Maximum sale price or monthly rent, depending on the listing category.

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

Show results in Zillow's selected time window; for sold properties this is the sold window.

## `minBeds` (type: `integer`):

Minimum bedroom count; applies to matching listing categories.

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

Maximum unique listings per ZIP code across all selected categories. Zero means all accessible pages.

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

Hard result cap across all ZIP codes. Zero means no additional cap.

## `proxyConfiguration` (type: `object`):

Connection settings for public search pages. Datacenter connections are tried before residential fallback.

## Actor input object example

```json
{
  "zipCodes": [
    "10014",
    "07306"
  ],
  "forSaleByAgent": true,
  "forSaleByOwner": false,
  "forRent": false,
  "sold": false,
  "daysOnZillow": "",
  "resultsLimit": 100,
  "maxItems": 0,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Public listings in the default dataset.

# 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 = {
    "zipCodes": [
        "10014",
        "07306"
    ],
    "resultsLimit": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/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 = {
    "zipCodes": [
        "10014",
        "07306",
    ],
    "resultsLimit": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("mlg14/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 '{
  "zipCodes": [
    "10014",
    "07306"
  ],
  "resultsLimit": 100
}' |
apify call mlg14/zillow-search-scraper --silent --output-dataset

```

## MCP server setup

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