# Zillow Price Drop & New Listing Monitor (`lukehunter/zillow-price-drop-monitor`) Actor

Watch Zillow ZIP codes for what changed since your last run: new listings, price cuts, price increases, removed listings and back-on-market. Built for a daily or weekly schedule plus a webhook, so you get alerts, not full dumps.

- **URL**: https://apify.com/lukehunter/zillow-price-drop-monitor.md
- **Developed by:** [Luke Hunter](https://apify.com/lukehunter) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 change events

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 Price Drop & New Listing Monitor

**For real-estate investors, agents, analysts and homebuyers tracking selected ZIP codes.** One run returns change events, not a full dump: new listings, price cuts, price increases, listings removed from search results, and listings back on the market. Run it daily or weekly to turn those changes into alerts.
Pay-per-result: **$0.004 per change event — 250 events = $1.** The free per-ZIP summary row never counts toward that.
Try it free with Apify's monthly platform credit.

This is a **real estate price monitor**, not a raw scraper: point it at the same ZIP codes every run and it remembers what it saw last time, so you only pay for what actually changed.

### Quick start (2 minutes)

1. Open the **Input** tab (no Zillow account or API key needed).
2. Use this prefill (swap in your own ZIP codes):

```json
{
  "zipCodes": ["90210", "10001"],
  "maxEvents": 200
}
```

3. Click **Start**. **The first run for a ZIP is always a baseline**: it saves what's currently listed and delivers no change events yet, because there is nothing to compare against. Run it again — on a schedule, or by clicking Start a second time — to start getting new\_listing, price\_cut, price\_increase, removed and back\_on\_market events.

### Run it as a Zillow price drop alert (schedule + webhook)

This Actor is built to sit on a schedule and hand off to a webhook, not to be run once:

1. Set your ZIP codes and click **Schedule** on the run page (or create one under **Schedules** in the Apify Console) — daily for a fast-moving market, weekly for most residential ZIPs.
2. Open the schedule's **Run configuration**, then add a **Webhook** under **Integrations** for the `ACTOR.RUN.SUCCEEDED` event, pointing at:
   - **Slack**: an Apify-to-Slack integration (Apify's own Slack integration, or a Zapier/Make webhook step) that posts the run's dataset items to a channel.
   - **Google Sheets**: the Apify-to-Google-Sheets integration, appending each run's `price_cut` rows to a sheet you watch.
   - Your own endpoint: the webhook payload includes the run's dataset URL, so a small script can pull `.../items?view=overview` and act on `eventType`.
3. Every scheduled run after the first delivers only what changed — new listings and price cuts are usually what buyers filter for; `minPriceChangePct` and `eventTypes` narrow it further so a webhook only fires on the events you care about.

### Example: what a real week looks like

- **Week 1** (baseline): `zip_summary` row only — `listingsNow: 47, isBaseline: true`. Nothing charged.
- **Week 2**: three `new_listing` events, one `price_cut` (a $15,000 reduction), two `removed` events, plus the free `zip_summary` row for that ZIP.
- **Week 3**: one of last week's `removed` listings reappears as `back_on_market` at a lower price.

### Input

```json
{
  "zipCodes": ["90210", "10001"],
  "eventTypes": ["new_listing", "price_cut", "price_increase", "removed", "back_on_market"],
  "minPriceChangePct": 0,
  "maxPagesPerZip": 20,
  "stateStoreName": "zillow-price-drop-monitor-state",
  "emitBaselineAsNew": false,
  "maxEvents": 200
}
```

| Field | Type | Default | Description |
|---|---|---:|---|
| `zipCodes` | string\[] | required | 1–50 US ZIP codes. Zillow for-sale ZIP URLs are also accepted. |
| `eventTypes` | string\[] | all five | Which change event types to deliver and charge for. |
| `minPriceChangePct` | number | 0 | Ignore price changes smaller than this absolute percentage of the previous price. |
| `maxPagesPerZip` | integer | 20 | 1–20. Safety cap on search-result pages fetched per ZIP per run. |
| `stateStoreName` | string | `zillow-price-drop-monitor-state` | Name of the store this Actor remembers each ZIP's last snapshot under. Change it only if you're running several independent watch lists. |
| `emitBaselineAsNew` | boolean | false | Deliver the first run's listings as `new_listing` events instead of only saving a baseline. |
| `maxEvents` | integer | 200 | 1–5000. Hard cap on change events delivered (and charged) per run. |

For most users, the only setting that matters is **ZIP codes** — everything else has a sensible default.

### Change event fields

| Field | Description |
|---|---|
| `eventType` | `new_listing`, `price_cut`, `price_increase`, `removed`, `back_on_market`, or the free `zip_summary`. |
| `zip`, `zpid` | The ZIP code and Zillow's own stable property id. |
| `address`, `url` | Address and Zillow listing URL. |
| `previousPrice`, `currentPrice`, `priceChange`, `priceChangePct` | Price before/after this run and the computed delta. |
| `bedrooms`, `bathrooms`, `livingArea`, `homeType`, `daysOnZillow` | Property details, current if seen this run, otherwise last known. |
| `observedAt`, `previousObservedAt` | When this run and the previous observation happened. |

The free `zip_summary` row adds `listingsNow`, `listingsBefore`, `newCount`, `removedCount`, `priceCutCount`, `medianPriceNow`, `medianPriceBefore`, `isBaseline` and `coverageComplete`.

### Example change events

```json
{
  "eventType": "price_cut",
  "zip": "90210",
  "zpid": "20533678",
  "address": "3084 Franklin Canyon Dr, Beverly Hills, CA 90210",
  "previousPrice": 3995000,
  "currentPrice": 3895000,
  "priceChange": -100000,
  "priceChangePct": -2.5,
  "daysOnZillow": 125,
  "observedAt": "2026-10-04T06:00:00.000Z",
  "previousObservedAt": "2026-09-27T06:00:00.000Z"
}
```

```json
{
  "eventType": "zip_summary",
  "zip": "90210",
  "listingsNow": 44,
  "listingsBefore": 47,
  "newCount": 3,
  "removedCount": 6,
  "priceCutCount": 1,
  "medianPriceNow": 2450000,
  "medianPriceBefore": 2495000,
  "isBaseline": false,
  "coverageComplete": true,
  "observedAt": "2026-10-04T06:00:00.000Z"
}
```

### Pricing and cost control

This Actor uses **pay per delivered change event** pricing.

**Current configured rate: $0.004 per change event delivered.** Check the Apify **Pricing** tab for the latest published rate. The free `zip_summary` row (one per ZIP, per run) is never charged.

| Change events delivered | Cost at $0.004/event |
|---:|---:|
| 100 | $0.40 |
| 250 | $1.00 |
| 1,000 | $4.00 |

There is no charge for a baseline run that finds no changes, and no charge for merely starting a run. `maxEvents` gives you a clear upper bound on the number of billable events per run.

### Important limitations

- **The first run for any ZIP is a baseline.** It saves what's currently listed and delivers no change events by default (turn on `emitBaselineAsNew` if you want the initial listings delivered as `new_listing` events — those are charged normally).
- **`removed` is not the same as sold.** A listing leaving Zillow's search results can mean it sold, was taken off the market, expired, or was relisted under a new listing id — this Actor reports that it left the ZIP's search results, not why.
- **A ZIP with more pages than `maxPagesPerZip` cannot have `removed` detected reliably that run.** When a run doesn't fetch every page Zillow has for a ZIP, `removed` is suppressed and `removedCount`/`coverageComplete` say so, rather than guessing that an unfetched listing is gone.
- **State lives in the named key-value store you configure** (`stateStoreName`). Deleting that store, or changing its name, resets your baseline for every ZIP.
- Zillow inventory and prices change continuously; every row is a point-in-time comparison between two runs, identified by `observedAt`/`previousObservedAt`.
- Individual agent names, phone numbers, and email addresses are not collected.
- The Actor does not use CAPTCHA bypass, proxy rotation, credential capture, or impersonation.
- This is an independent tool and is **not affiliated with, endorsed by, or connected to Zillow Group, Inc.** Zillow is a trademark of its owner. You are responsible for using the data in accordance with applicable terms and laws.

### FAQ

#### Is this a Zillow price change alert I can put on a schedule?

Yes — that's what it's built for. Schedule it daily or weekly and pair the schedule with an Apify webhook (see "Run it as a Zillow price drop alert" above) to push `price_cut`/`new_listing` events into Slack, Google Sheets, or your own endpoint.

#### Why did my first run deliver nothing billable?

The first run for a ZIP is always a baseline (see Limitations). Run it again — the second run compares against the saved baseline and delivers real change events.

#### Does "removed" mean the home sold?

No. See Limitations: `removed` only means the listing left the ZIP's search results this run; it could have sold, been withdrawn, expired, or been relisted.

#### Do I need a Zillow API key or account?

No separate Zillow API key or Zillow login is required.

#### How do I control the cost?

Set `maxEvents` (a hard cap on billable events per run) and `eventTypes`/`minPriceChangePct` to narrow which changes are worth delivering to you.

### Related Actors

Other data tools from the same developer, built to the same standard: official or public sources, hard cost caps, and honest documentation of limits.

- **[County Multifamily Building Permits Surge Radar](https://apify.com/lukehunter/county-multifamily-permit-surge-radar)**: U.S. counties where 5+ unit apartment permits are surging year over year.
- **[Zillow ZIP Code Property Search Scraper](https://apify.com/lukehunter/zillow-zip-scraper)**: for-sale listings by ZIP code with prices, price cuts and days on Zillow.
- **[Rightmove Property Scraper](https://apify.com/lukehunter/rightmove-scraper)**: UK property listings from Rightmove search results, with price, address, bedrooms and agent details.
- **[Federal Register Rule Effective-Date Radar](https://apify.com/lukehunter/federal-rule-effective-date-radar)**: final federal rules about to take effect, with citations, dockets and urgency.
- **[Chicago Contract Recompete & Extension Radar](https://apify.com/lukehunter/chicago-contract-expiration-radar)**: expiring City of Chicago contracts with extension history, for recompete prospecting.
- **[Bank Branch Closings & Merger Radar (FDIC)](https://apify.com/lukehunter/bank-branch-merger-radar)**: new US bank branch closings, openings and mergers from the FDIC.
- **[Chicago New Business License Radar](https://apify.com/lukehunter/chicago-new-business-license-radar)**: newly licensed Chicago businesses as opening leads, grouped by category.
- **[NYC FDNY Contractor Leads & Hearing Radar](https://apify.com/lukehunter/fdny-violation-radar)**: fresh NYC fire-code summonses routed by remediation trade, with hearing dates.

# Actor input Schema

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

US ZIP codes to monitor, five digits each, e.g. "90210". A pasted https://www.zillow.com/homes/for\_sale/90210/ URL is also accepted and reduced to its ZIP. Maximum 50 ZIP codes per run. Run this on a schedule (daily or weekly) to get change events over time.

## `eventTypes` (type: `array`):

Which change event types to deliver and charge for. The free zip\_summary row is always included regardless of this filter.

## `minPriceChangePct` (type: `number`):

Ignore price\_cut/price\_increase events smaller than this absolute percentage of the previous price, e.g. 1 to ignore rounding-sized changes below 1%. Default 0 (report every price change).

## `maxPagesPerZip` (type: `integer`):

Safety cap on how many search result pages are fetched per ZIP code per run (each page holds roughly 41 listings), 1-20, default 20. A ZIP with more pages than this cannot have its removed listings detected reliably that run (see Limitations in the README) — Zillow's own page count for the ZIP stops the run earlier whenever it is smaller.

## `stateStoreName` (type: `string`):

Name of the named, cross-run key-value store this Actor uses to remember each ZIP's last snapshot. Leave as the default unless you are running several independent watch lists and want them to keep separate history.

## `emitBaselineAsNew` (type: `boolean`):

By default, the first run for a ZIP only saves a baseline snapshot and delivers no change events (there is nothing to compare against yet). Turn this on to instead deliver every listing found on that first run as a new\_listing event — useful if you want a complete initial dump, but note it is charged like any other change event.

## `maxEvents` (type: `integer`):

Hard cap on the number of change events delivered (and charged) across all ZIP codes in one run, 1-5000, default 200. Free zip\_summary rows do not count toward this cap.

## Actor input object example

```json
{
  "zipCodes": [
    "90210"
  ],
  "eventTypes": [
    "new_listing",
    "price_cut",
    "price_increase",
    "removed",
    "back_on_market"
  ],
  "minPriceChangePct": 0,
  "maxPagesPerZip": 20,
  "stateStoreName": "zillow-price-drop-monitor-state",
  "emitBaselineAsNew": false,
  "maxEvents": 20
}
```

# Actor output Schema

## `changeEvents` (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 = {
    "zipCodes": [
        "90210"
    ],
    "eventTypes": [
        "new_listing",
        "price_cut",
        "price_increase",
        "removed",
        "back_on_market"
    ],
    "minPriceChangePct": 0,
    "maxPagesPerZip": 20,
    "stateStoreName": "zillow-price-drop-monitor-state",
    "emitBaselineAsNew": false,
    "maxEvents": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("lukehunter/zillow-price-drop-monitor").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": ["90210"],
    "eventTypes": [
        "new_listing",
        "price_cut",
        "price_increase",
        "removed",
        "back_on_market",
    ],
    "minPriceChangePct": 0,
    "maxPagesPerZip": 20,
    "stateStoreName": "zillow-price-drop-monitor-state",
    "emitBaselineAsNew": False,
    "maxEvents": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("lukehunter/zillow-price-drop-monitor").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": [
    "90210"
  ],
  "eventTypes": [
    "new_listing",
    "price_cut",
    "price_increase",
    "removed",
    "back_on_market"
  ],
  "minPriceChangePct": 0,
  "maxPagesPerZip": 20,
  "stateStoreName": "zillow-price-drop-monitor-state",
  "emitBaselineAsNew": false,
  "maxEvents": 20
}' |
apify call lukehunter/zillow-price-drop-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lukehunter/zillow-price-drop-monitor"
        }
    }
}
```

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/68ZePNiGa9V8IpAVu/builds/iFY5uc4ljwifxiDQV/openapi.json
