# Zillow Scraper — Deal Finder, Price Cuts & Market Monitor (`ryanclinton/zillow-scraper`) Actor

Find real estate deals on Zillow. Returns a ranked deal queue — below-comp listings, price cuts, motivated sellers, rental yield, and what changed since last run. Drop-in compatible with maxcopell/zillow-scraper + zillow-detail-scraper.

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

## Pricing

Pay per event

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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 Scraper — Deal Finder, Price Cuts & Market Monitor

![Zillow Scraper: which listings are actually deals, not just rows](https://apifyforge.com/readme-assets/ryanclinton-zillow-scraper/hero.png)

### In one sentence

Zillow Scraper is a real-estate deal-finding engine on Apify that takes a Zillow search or property URLs and returns a ranked deal queue: which listings are below comps, which sellers just cut price, which cash-flow as rentals, and what changed in your market since the last run.

**Category:** Zillow scraper. Real estate deal finder. Price drop tracker and market monitor.
**Primary use case:** Rank a market's listings by which are actually deals, then schedule a watchlist run so the saved market memory compounds. Can also pull comps for specific properties, track price cuts, find cash-flow rentals, and read a ZIP's price trajectory.

**Also known as:** zillow scraper, real estate deal finder, find underpriced homes, zillow price drop tracker, motivated seller finder, rental yield calculator by zip

### Example: one search in, the deals out

A `deals` run turns a Zillow search into a ranked deal queue. One row from that queue (address invented):

- **4218 Sendero Hills Pkwy, Austin, TX** — Deal Score **83** (B), Seller Pressure Index **88**
- **Why now:** 7.4% under comp median; cut twice in 41 days; 71 days on market vs ZIP median 24
- **Recommended action:** Compare to comps and re-check in 7 days

**The real competitor isn't another scraper. It's the spreadsheet.** A raw scraper hands you the ingredients (price history, comps, a rent estimate). This actor cooks the decision and remembers it for next run. The three numbers that carry it are the Deal Score, the Seller Pressure Index, and Market Memory; everything else is advanced detail.

![400 listings in, 5 deals to review first out, each ranked with the reason and the next step](https://apifyforge.com/readme-assets/ryanclinton-zillow-scraper/before-after.png)

### Ready-to-run examples

One-click examples, each on its own page:

- **[Real Estate Deal Analyzer](https://apify.com/ryanclinton/zillow-scraper/examples/zillow-deal-analyzer)** — paste Zillow property URLs and get a deal score, descriptive comp position, and seller-pressure read for each listing.
- **[Rental Property Yield Analyzer](https://apify.com/ryanclinton/zillow-scraper/examples/zillow-rental-yield-analyzer)** — rank Zillow listings by rental yield and cash flow.

[See all examples](https://apify.com/ryanclinton/zillow-scraper/examples).

### What this actor does

- **What it is:** A Zillow scraper with a decision layer that ranks listings by which are deals, not just dumps rows.
- **What it checks:** Where each listing sits versus comparable nearby listings, whether the seller is under pressure (price cuts, days on market, relists), the rental yield, and what moved since the prior run.
- **What it returns:** A ranked deal queue per listing with a Deal Score (0-100), a Seller Pressure Index (0-100), `whyNow`, a `recommendedAction`, descriptive comp position, and saved market memory that grows every run.
- **What it does NOT do:** It does not appraise or value a property, it gives no investment, offer, or transaction advice, and it makes no neighborhood-quality, school, crime, or demographic judgement.
- **Who it's for:** Real-estate investors, flippers, buy-and-hold and BRRRR investors, agents and brokers, and market analysts.

### Overview

Zillow Scraper is an Apify actor that answers the question a raw Zillow scraper leaves on the table: of all these listings, which ones are actually deals? Paste a Zillow for-sale search and the actor returns the best deals in that search ranked, each row carrying a Deal Score, a Seller Pressure Index, a plain-English reason, and a prioritization action, in about a minute. The default first run is a ranked deal queue, not a row dump.

The real competitor isn't another scraper. It's the spreadsheet. A raw scraper hands you price history, comps, a rent estimate, and days-on-market, then leaves you to cook the decision in Excel. This actor cooks it: it computes the comp position, the seller-pressure read, and the yield for you, and it remembers what changed across runs. `maxcopell/zillow-scraper` gives you Zillow rows. This actor tells you which ones are deals.

To find deals on Zillow, paste a for-sale search URL (or property URLs) and run the actor. You get back a ranked deal queue with comp position, a Seller Pressure Index, rental yield, and a recommended next step per listing. Same input shapes and the same field set as `maxcopell/zillow-scraper` + `zillow-detail-scraper` (set `outputProfile: compat`), one actor instead of the suite, plus the decision layer the suite does not ship.

**In short:** Same input as a standard Zillow scraper. Same fields, plus which listings are actually deals.

**Speed:** The default deal queue returns in about a minute. **Pricing:** Pay-per-result; final price on the Store listing. **Output:** JSON, CSV, or Excel.

**Key limitation:** Comp position is a descriptive read of where the list price sits versus comparable public listings, never an appraisal or an automated valuation. The full scope fence is in [What this does NOT do](#what-this-does-not-do). Results can be partial when Zillow restricts automated access from shared IPs; the run reports its coverage honestly rather than hiding the gap.

### The two features that make this not a scraper

Most of what the actor does is interpretation. Two surfaces carry that story, and they show up on every screenshot.

**Seller Pressure Index (SPI).** One 0-100 number any investor instantly gets. SPI 91 means this seller is under pressure; SPI 22 means they are not. It is a deterministic composite of days-on-market, cut count, total percent off the original price, relist count, and how much the market is cooling, and it ships its full component breakdown so you can see how the number was built. A raw scraper structurally cannot give you this. It can only hand you the ingredients.

**Saved Market Memory.** Most Zillow scrapers show what is for sale. This actor remembers what changed. Every run remembers the public price, status, and days-on-market history of every property it touches, and accumulates it across runs. Zillow shows the current state. This actor remembers the trajectory: which listings were relisted three times each lower, which cooled, which are becoming negotiable. A competitor can copy the fields. It cannot copy accumulated trajectory, because the memory clock cannot be backfilled.

### Market Memory Coverage

The moat is visible on every run. Each run emits a `marketMemoryCoverage` block reporting how deep the saved store is so far:

- `propertiesTracked` — properties with accumulated public history
- `zipsTracked` — ZIPs with an accumulated market trajectory
- `opportunitiesTracked` — deals followed from detection to exit

It starts at zero on a fresh market and grows with every scheduled run. A clone can copy the fields; it cannot copy the history, because the trajectory cannot be backfilled. The longer you run it, the deeper the memory and the wider the head start over anyone starting today.

### What you get from one call

**Input:** `{ "mode": "deals", "searchUrls": ["https://www.zillow.com/austin-tx/houses/?searchQueryState=..."], "rankBy": "opportunity" }`
**Returns:**
- A ranked deal queue: the best deals in this search, each with `dealScore` (0-100, with its components), `sellerPressureIndex`, `whyNow`, `address`, and `recommendedAction`
- Descriptive comp position per listing (`compSet`: where the price sits vs comparable nearby listings, marked descriptive, never an appraisal)
- A rental-yield read (`yieldScore`) and a listing archetype (`listingProfile`: deal, motivated-seller, cash-flow, and more)
- Saved market memory (`history`): price trajectory, cut count, and `dealVelocity` (is this deal improving?), plus a run `summary` and a `coverage` record

**Typical time to first result:** about a minute for a default deal queue.
**Typical time to integrate:** minutes if you already read a standard Zillow scraper's output, since the substrate fields match.

![Four features: a ranked deal queue, the Seller Pressure Index, Saved Market Memory, and Deal Velocity](https://apifyforge.com/readme-assets/ryanclinton-zillow-scraper/feature-callouts.png)

### What makes this different

- **A ranked deal queue, not a row dump** — every listing arrives ranked by opportunity, with a Deal Score, a `whyNow`, and a recommended action attached.
- **Seller Pressure Index on every listing** — one sortable 0-100 number that tells you which sellers are under pressure, with its breakdown, which no raw scraper provides.
- **Saved market memory that compounds** — the actor remembers price, status, and deal-score trajectory across runs, so the same input gets smarter every run and the memory cannot be backfilled.

If you were building this yourself, you would need a comp-position engine, a seller-motivation composite, a cross-run price-history store keyed per property and per ZIP, a yield model, and a trust layer that fences descriptive reads from appraisals. This actor produces those as output, ready for investor review, monitoring, and analysis.

### The Market Memory Engine: Find, Track, Learn, Remember

One unifying idea ties the actor together. Zillow shows what's for sale; this actor builds a Market Memory Engine that tracks what changed, which sellers are under pressure, which deals are improving, and how similar opportunities historically played out. Every surface maps to one of four jobs.

- **Find** — the deal queue, the Seller Pressure Index, and the rental-yield read surface the deals in a search right now.
- **Track** — watchlists, the Deal Feed, and Deal Velocity report what moved since the last run and which deals are getting better.
- **Learn** — the Outcomes Engine and Opportunity Survival describe how similar past listings played out (descriptive history, never a forecast).
- **Remember** — the Market Memory Engine accumulates per-property and per-ZIP history, surfaced as `marketMemoryCoverage` so the moat is visible and grows every run.

### What a scheduled watchlist unlocks over time

The first run is useful on its own (a ranked deal queue). The deeper reads switch on as the saved memory accumulates, which is why a scheduled watchlist gets more valuable the longer it runs. It cannot be backfilled, so the clock starts on run one.

- **Run 1** — A ranked deal queue: which listings are below comps, under price pressure, or high-yield right now.
- **After a week** — Watchlist deltas: price cuts, back-on-market, and went-pending since the prior run, as a clean change feed.
- **After a month** — Deal Velocity and emerging deals: `history.dealVelocity` shows which deals are improving, so you catch them before the cut is obvious.
- **Once enough similar deals have closed** — Outcome intelligence activates: descriptive cohort frequencies for how listings like this historically played out (honest `insufficientHistory` until the sample exists, never a fabricated forecast).
- **The longer it runs** — Market Memory Coverage deepens. A competitor cloning the fields still starts from zero, because the trajectory cannot be backfilled.

![What changed since last run: sellers cut price, new below-comp listings, a deal improving, a listing went pending](https://apifyforge.com/readme-assets/ryanclinton-zillow-scraper/change-feed.png)

### Choose your job

Pick the job in plain language with the `intent` control and the actor sets the right mode, ranking, and output for you. Each maps to the real input.

#### Find underpriced deals (`find_deals`)
```json
{ "intent": "find_deals", "searchUrls": ["https://www.zillow.com/austin-tx/houses/?searchQueryState=..."] }
````

Returns the deal queue ranked by opportunity: the listings below comps, recently cut, or high-yield, each with a Deal Score and a reason.

#### Track price drops (`price_cuts`)

```json
{ "intent": "price_cuts", "searchUrls": ["https://www.zillow.com/austin-tx/houses/?searchQueryState=..."] }
```

Surfaces listings with `price_cut` and `repeat_cut` signals by depth, ranked by Seller Pressure Index.

#### Find cash-flow rentals (`rental_yield`)

```json
{ "intent": "rental_yield", "searchUrls": ["https://www.zillow.com/austin-tx/houses/?searchQueryState=..."] }
```

Ranks listings by descriptive rental-yield score (rent estimate vs price), surfacing the cash-flow archetype.

#### Monitor a market over time (`monitor_market`)

```json
{ "intent": "monitor_market", "searchUrls": ["https://www.zillow.com/austin-tx/houses/?searchQueryState=..."], "watchlistName": "austin-east" }
```

Tracks the set across runs and returns what changed: new listings, price cuts, back-on-market, and went-pending.

#### Analyze specific properties (`analyze_properties`)

```json
{ "intent": "analyze_properties", "propertyUrls": ["https://www.zillow.com/homedetails/...", "https://www.zillow.com/homedetails/..."] }
```

Full per-property intelligence: Deal Score, comp position, Seller Pressure Index, yield, and saved history per URL.

#### Pull property comps (`comps`)

```json
{ "intent": "comps", "propertyUrls": ["https://www.zillow.com/homedetails/..."] }
```

Returns each property's descriptive `compSet`: comp median, your price position percent, and competitive rank (e.g. 4th-cheapest of 37).

### The four modes

The `intent` control resolves to one of four modes; advanced users can set `mode` directly.

| Mode | Input | What it returns |
|------|-------|-----------------|
| `deals` | Zillow search URLs | A ranked deal queue over the search: the best deals right now, each with a Deal Score and a reason. |
| `properties` | Zillow `/homedetails/` URLs | Full per-property intelligence: comp position, Seller Pressure Index, yield, history. |
| `market` | ZIP codes or a city/metro | A ZIP/metro's price, inventory, and yield trajectory plus its standout listings. |
| `watchlist` | A `watchlistName` on any mode | What changed since the last run; with property URLs it becomes a tracked portfolio with a priority rollup. |

### Who uses this?

Set `investorType` to re-weight the same deal queue for your strategy. Find your row:

| You are a | You want | Run it like |
|-----------|----------|-------------|
| **Flipper** | Motivated sellers below comps | `investorType: flipper`, `rankBy: sellerPressure` |
| **BRRRR investor** | Below-comp properties to force value | `investorType: brrrr`, `rankBy: opportunity` |
| **Buy-and-hold / cash-flow** | Positive-cash-flow rentals by yield | `investorType: cashflow`, `rankBy: yield` |
| **Agent / broker** | New listings and price cuts in a farm area | `investorType: agent`, `rankBy: priceCut`, a `watchlistName` |
| **Market analyst** | A ZIP's price and inventory trajectory | `mode: market`, ZIP codes |

Same data, different ranking. The investor type changes which signals the Deal Score weights, not what the actor pulls.

### Quick answers

**What is it?** Zillow Scraper is a real-estate deal-finding engine that ranks a Zillow search's listings by which are actually deals and tracks how a market moves across runs.
**How do I find underpriced homes on Zillow?** Paste a Zillow for-sale search URL and run the actor with `rankBy: opportunity` (the default). It returns a deal queue of the listings below comps, recently cut, or high-yield, ranked, each with a Deal Score and a reason.
**What makes it different?** It ships a decision layer on top of the scrape: a Deal Score, a Seller Pressure Index, comp position, rental yield, and saved market memory, instead of raw rows.
**What data sources does it use?** Public Zillow listing and detail pages, fetched over residential proxies because Zillow restricts automated access from shared IPs. No login, no private data.
**What does it return?** A ranked deal queue, price cuts, motivated sellers, cash-flow, market trajectory, and saved market memory, in JSON, CSV, or Excel.
**How much does it cost?** Pay-per-result; the final per-result price is shown on the actor's Store listing.

### At a glance

**Quick facts:**

- **Input:** A Zillow for-sale search URL (`deals`), `/homedetails/` URLs (`properties`/`watchlist`), or ZIP codes / a city (`market`).
- **Output:** Deal queue, comp position, Seller Pressure Index, rental yield, signals, saved history, run summary.
- **Pricing:** Pay-per-result; final price on the Store listing.
- **Batch size:** Up to 50 search URLs or 300 property URLs per run; `limits.maxDetails` caps the render-heavy detail pass.
- **Modes:** deals, properties, market, watchlist.
- **Output profiles:** signals (default), compat (drop-in maxcopell parity), minimal.
- **Investor types:** flipper, buy & hold, BRRRR, cash-flow, agent, analyst (re-weights the ranking).
- **Data source:** Public Zillow pages over residential proxies. No login.

**Input to output:**

- Input: a Zillow search URL, property URLs, or ZIP codes.
- Process: pull the listings, read each against its comps, score seller pressure and yield, detect what changed, persist the market memory.
- Output: a ranked deal queue plus a market read and a run summary.

**Best fit:** Finding underpriced and motivated-seller deals over a search, tracking price cuts, finding cash-flow rentals, monitoring a market across runs.
**Not ideal for:** A certified valuation, a forecast of future price, neighborhood-quality screening, or any private/login-gated data.
**Does not include:** Appraisals, appreciation forecasts, ROI guarantees, Fair-Housing-sensitive scoring, or cross-platform correlation.

**Problems this solves:**

- How to tell which of a search's listings are actually deals without an afternoon in a spreadsheet.
- How to spot motivated sellers with one sortable number instead of eyeballing price history.
- How to find positive-cash-flow rentals by descriptive yield, not manual rent-divided-by-price math.
- How to monitor a market over time and get back only what changed since last run.

**Data trust:** List price, status, days-on-market, Zestimate, and rent Zestimate are observed from the public page. Comp position is descriptive (`metricClass: descriptive`), never an appraisal. Saved history is `historical`. A thin-data listing scores with `confidence.grade: low` and takes a `penalties.lowConfidence` hit on its Deal Score, so a high score on missing data can never look magical.

### Best fit / Less suitable

**Best fit:**

- Investors and flippers ranking a market's listings for the underpriced, motivated-seller deals to review first.
- Buy-and-hold and BRRRR investors looking for positive-cash-flow rentals by descriptive yield.
- Agents and brokers tracking new listings, price cuts, and back-on-market activity in a farm area.
- Analysts reading a ZIP or metro's price, inventory, and yield trajectory across runs.

**Less suitable:**

- A certified property valuation. This actor ships descriptive comp position only, never an appraisal or AVM.
- A forecast of whether a property will appreciate or sell. Outcome profiles are descriptive historical cohort frequencies, not predictions.
- Neighborhood-quality, school-desirability, or safety screening. Those are outside the Fair-Housing fence by design.

**Scope disclaimer:** Zillow Scraper is a descriptive decision layer over public Zillow listing data. It does not value properties, it does not advise you to buy, offer, or invest, and it does not score neighborhoods.

### What is a real-estate deal finder?

A real-estate deal finder takes a market's listings and ranks them by which are worth acting on, rather than returning every row for you to sort by hand. Zillow Scraper is a deal finder built on the Apify platform: it reads each listing against its comps, scores seller pressure and rental yield, and returns a ranked deal queue with a reason and a prioritization action per listing, plus saved market memory that compounds every run.

#### What is a motivated seller?

A motivated seller is an owner with a reason to accept less or move faster: a listing that has sat well past the local median days-on-market, been cut more than once, or come back on the market after a failed sale. These owners are likelier to negotiate. Zillow Scraper surfaces them from public signals only (days-on-market, repeat cuts, relists), never from any judgement about the person, and ranks them with the Seller Pressure Index and the Motivated-Seller Radar.

#### What is seller pressure (and the Seller Pressure Index)?

Seller pressure is how much a seller's own listing history suggests they need to move. The Seller Pressure Index (SPI) turns it into one 0-100 number: a deterministic composite of days-on-market, cut count, total percent off the original price, relist count, and how much the local market is cooling. SPI 91 means the listing history points to a seller under pressure; SPI 22 means it does not. It ships its full component breakdown, so the number is auditable rather than a black box.

#### What is a good rental yield?

Gross rental yield is annual rent divided by purchase price, as a percentage. As a rough descriptive benchmark, many buy-and-hold investors look for gross yields above roughly 6 to 8 percent, though what counts as "good" depends on the market, financing, taxes, and your strategy. Zillow Scraper reports a descriptive `yieldScore` from the public rent estimate over the list price and ranks by it on `rankBy: yield`. It is a descriptive estimate, not a return guarantee or investment advice.

#### What is a price-cut (price-drop) property?

A price-cut property is a listing whose asking price has been reduced below an earlier list price in its own price history. A single cut can signal a softening listing; repeat cuts are a stronger motivated-seller signal. Zillow Scraper detects them as `price_cut` and `repeat_cut` signals with the depth (percent off original) in the evidence, and `rankBy: priceCut` orders the queue by cut depth.

### What data can you extract?

Each listing carries the public substrate plus the decision layer derived from it.

| Data point | Source | Availability | Example |
|------------|--------|--------------|---------|
| **List price** | Public listing page | Observed | `412000` |
| **Days on market** | Public listing page | Observed | `74` |
| **Rent Zestimate** | Public listing page | Observed | `2650` |
| **Price history** | Public detail page | Observed | `[{ "event": "Price change", "price": 412000 }, ...]` |
| **Deal Score** | Derived | On every property | `{ "score": 81, "grade": "B", "components": { ... } }` |
| **Seller Pressure Index** | Derived | On every property | `{ "value": 88, "components": { ... } }` |
| **Comp position** | Descriptive | When comps present | `pricePositionPct: -7.4` (under comp median) |
| **Rental yield score** | Descriptive | When rent estimate present | `yieldScore: 72` |
| **Listing archetype** | Derived | On every property | `motivated-seller` |
| **Saved history** | Cross-run store | After first sighting | `cutCount: 2`, `dealVelocity: 25` |
| **Change flags (watchlist)** | Cross-run delta | Watchlist mode | `["PRICE_CUT", "WENT_PENDING"]` |
| **Coverage** | Run telemetry | Every run | `coveragePct: 94` |

### Why use Zillow Scraper?

Finding deals by hand means scraping the search, scraping each detail page, then sitting in Excel comparing every list price to its comps, eyeballing price history and days-on-market, and computing rent over price one listing at a time. It is hours per market, the comp logic lives in one analyst's head, and it goes stale the moment the market moves. Stitching a search scraper and a detail scraper together gets you the ingredients and none of the decisions.

This actor automates the entire interpretation. Same input shapes a standard Zillow scraper takes, plus a ranked deal queue, a Seller Pressure Index, comp position, rental yield, and a market memory that compounds, all in one run.

**Key difference: Zillow Scraper ships the decision layer and the accumulated market memory that raw Zillow scrapers and the multi-actor suite do not put in your pipeline.**

| Feature | Zillow Scraper | Raw Zillow scrapers | Curated property databases |
|---------|----------------|---------------------|----------------------------|
| Data source | Public Zillow pages | Public Zillow pages | Licensed / modelled datasets |
| Deal ranking (deal queue) | Yes, per listing | No | Varies, in dashboard |
| Seller Pressure Index | Yes, 0-100 with breakdown | No | Not a core feature |
| Comp position | Descriptive, with method | No | Modelled, often as a valuation |
| Rental yield read | Descriptive yield score | No | Sometimes |
| Cross-run market memory | Yes, compounds every run | No | Internal to the product |
| Output format | JSON, CSV, Excel, API | Raw rows | Dashboard, varies by plan |
| Automation and scheduling | Native on Apify | Varies | Limited, UI-first |
| Best for | Automated deal finding and monitoring | Bulk row collection | Human-driven analysis |

*Pricing and features based on publicly available information as of June 2026 and may change.*

Unlike a raw Zillow scraper, which hands you the price history and comps and leaves the decision to you, Zillow Scraper returns a finished deal queue ranked by opportunity. It detects seller pressure and emerging deals directly from accumulated price and status history, rather than relying on a curated database that started from zero.

#### Zillow Scraper vs maxcopell/zillow-scraper

The closest comparison is the Zillow scraper most investors already run. `maxcopell/zillow-scraper` (search) and `zillow-detail-scraper` (per-property) return excellent raw rows. They hand you the ingredients (price history, comps, rent estimate) and leave the decision in your spreadsheet. This actor takes the same input, returns the same fields on demand, and adds the decision layer they do not ship.

| Capability | Zillow Scraper (this actor) | maxcopell/zillow-scraper + zillow-detail-scraper |
|---|---|---|
| Zillow rows: search + detail fields | Yes | Yes |
| One actor instead of a search + detail suite | Yes | No, the two are separate actors |
| Same field set on demand (`outputProfile: compat`) | Yes | n/a (it is the source shape) |
| Deal ranking: a scored deal queue | Yes | No |
| Seller Pressure Index (0-100, with breakdown) | Yes | No |
| Comp position scored, not just returned | Yes | Returns comps; you score them |
| Rental-yield read | Yes | Returns rent estimate; you compute yield |
| Cross-run Market Memory | Yes | No |
| Deal Velocity and emerging deals | Yes | No |
| Watchlist deltas: what changed since last run | Yes | No |

The migration is drop-in: same URLs in, the same fields back with `outputProfile: compat`, plus the deal queue on the default profile. One actor replaces the suite.

![Same data, different output: a typical Zillow scraper returns rows; this actor adds deal ranking, Seller Pressure Index, comp scoring, yield, Market Memory, and watchlist deltas](https://apifyforge.com/readme-assets/ryanclinton-zillow-scraper/comparison.png)

#### Why use this instead of PropStream or BatchLeads?

PropStream and BatchLeads are subscription real-estate databases: you pay a monthly fee for modelled property data and skip-tracing, and you work it in their dashboard. Zillow Scraper is a different shape, and the two can sit side by side.

| | Zillow Scraper | PropStream / BatchLeads (subscription databases) |
|---|----------------|--------------------------------------------------|
| **Cost model** | Pay-per-run on Apify, no subscription | Monthly subscription |
| **Where it runs** | API and dataset, automatable | Dashboard, UI-first |
| **Deal signal** | Transparent Deal Score and Seller Pressure Index with their breakdowns | Filters and lists, scoring varies by product |
| **Valuation** | Descriptive comp position with a stated method, never an appraisal | Often a modelled value / ARV estimate |
| **Cross-run memory** | Public price and status trajectory that compounds every run | Internal to the platform |
| **Data source** | Public Zillow listing data | Licensed / aggregated datasets |

Choose Zillow Scraper when you want an automatable, pay-per-run deal queue with transparent, auditable scoring you can pipe into your own pipeline. Choose a subscription database when you need skip-tracing, owner records, or off-market data, which are outside this actor's public-data scope.

*Comparison based on publicly available information as of June 2026 and may change. Product names are trademarks of their respective owners.*

### Platform capabilities

- **Scheduling** — run a watchlist daily or weekly so the saved market memory compounds.
- **API access** — trigger from Python, JavaScript, or any HTTP client.
- **Residential routing** — Zillow restricts automated access from shared IPs, so the actor defaults to US residential routing.
- **Monitoring** — schedule runs and route the Deal Feed onward to Slack or email on failure.
- **Integrations** — Zapier, Make, Google Sheets, HubSpot, and webhooks.

### Features

The actor layers a deterministic decision envelope on a faithful Zillow substrate. The substrate matches the maxcopell field set; the layers below add the decisions, and there is no LLM anywhere in the decision path, so every score and comp is auditable. Each cluster has its own block.

#### Finds the deals (scoring and ranking)

- **Deal Score (auditable):** a 0-100 master score with its full breakdown (`components`: below-comp, seller motivation, freshness, rental yield, liquidity; `penalties`: small comp set, low confidence, missing rent estimate) and a plain-English `scoreEvidence`.
- **Seller Pressure Index:** a 0-100 composite (days-on-market, cut count, percent off original, relist count, market cooling) that detects which sellers are under pressure, with its component breakdown on every listing.
- **Descriptive comp position:** `compSet` with comp median, your price-position percent (negative = under), price-per-sqft vs comp, and a competitive rank (e.g. 4th-cheapest of 37), marked descriptive and never an appraisal.
- **Rental-yield read:** a `yieldScore` from the public rent estimate over price, marked descriptive, never a return guarantee.
- **Reranking:** `rankBy` orders the queue by opportunity, deal score, emerging, seller pressure, price cut, yield, days-on-market, recency, or price; every listing carries its rank on each axis.

#### Tracks what changed (signals and watchlist)

- **12 typed signal events:** price\_cut, repeat\_cut, below\_comp, stale\_listing, back\_on\_market, high\_yield, new\_listing, status\_change, price\_increase, distressed\_trajectory, emerging\_deal, deal\_ended, each evidenced and decay-aware.
- **8 listing archetypes:** deal, motivated-seller, cash-flow, overpriced, fresh-market, hot, flip-candidate, unclassified.
- **Watchlist delta:** with a `watchlistName`, every record gains `changeFlags` (NEW, PRICE\_CUT, BACK\_ON\_MARKET, WENT\_PENDING, SOLD, UNCHANGED), a `priceChange` block, and a `watchlistState` transition.
- **Deal Velocity:** `history.dealVelocity` reports whether a deal is improving, so a Score 58 listing that is moving can outrank a Score 70 one that is not.

#### Learns and remembers (saved market memory)

- **Saved market memory:** a per-property `history` block (price trajectory, cut count, relist count, peak price, current-vs-peak, `dealEvolution`) that compounds every run and cannot be backfilled.
- **Historical outcome profile:** descriptive cohort frequencies for similar past listings (median days to pending, price-cut probability), labelled as history, never a forecast, with an honest `insufficientHistory` cold-start.
- **Market trajectory:** for `market` mode, a ZIP/metro `marketLifecycle`, `marketMomentum` (-100..+100), and `zipOpportunityScore`.
- **Coverage transparency:** a productised `coverage` record plus `marketMemoryCoverage` (properties, ZIPs, opportunities tracked), so honest gaps are stated and the moat is visible.

#### Honest by construction (trust layer)

- **Data-quality confidence:** every record carries a `confidence` block (overall, grade, drivers, missingData) driven purely by which substrate fields were present.
- **Verb-fenced actions:** `recommendedAction` only ever says Review, Compare to comps, Re-check, Investigate, Add to watchlist, or Flag for analyst, never Buy, Offer, Bid, Invest, or Sell.
- **Fair-Housing fence:** no scoring or filtering by neighborhood desirability, schools, crime, or demographics; deal signals are price, comp, days-on-market, and yield only.

### Find underpriced homes

Use when you have a search's worth of listings and need the few below comps. Run `deals` mode with `rankBy: opportunity`; the deal queue surfaces listings below the comp median, with the margin in `compSet.pricePositionPct`. Key outputs: `dealScore`, `compSet`, `whyNow`, `recommendedAction`.

### Track Zillow price drops

Use when you watch a market for reductions. Run with `rankBy: priceCut`; listings with `price_cut` and `repeat_cut` signals surface by depth. Real-estate investors use this to catch sellers who have already started moving. Key outputs: `signalEvents`, `sellerPressureIndex`, `history.cutCount`.

### Find motivated sellers

Use when you want the sellers most likely to negotiate. The Seller Pressure Index and the Motivated-Seller Radar rank listings by accumulated days-on-market, repeat cuts, and relists. Key outputs: `sellerPressureIndex`, `motivatedSellerRadar`, `riskState`.

### Find cash-flow rentals

Use when you buy for rental income. Run with `rankBy: yield`; each listing carries a descriptive `yieldScore` from the public rent estimate over price. Key outputs: `yieldScore`, `rentZestimate`, `dealScore`.

### Monitor a housing market

Use when you track a market across weeks. Set a `watchlistName` and schedule a run; you get back only what changed, with `changeFlags` and a Deal Feed. Key outputs: `changeFlags`, `priceChange`, `dailyBriefing`.

### Read a ZIP's market trajectory

Use when you choose where to invest. Run `market` mode on ZIP codes; one record carries lifecycle, momentum, and a ZIP opportunity score. Key outputs: `marketLifecycle`, `marketMomentum`, `zipOpportunityScore`.

![Discovery products: a Deal Finder shortlist and a Sellers Under Pressure list, each ranked with the reason](https://apifyforge.com/readme-assets/ryanclinton-zillow-scraper/discovery-cards.png)

### When to use Zillow Scraper

**Best for:**

- Ranking a Zillow search of dozens to a couple hundred listings into a deal queue, on demand or scheduled.
- Tracking price cuts and motivated sellers in a farm area weekly.
- Finding positive-cash-flow rentals across a market by descriptive yield.
- Reading a ZIP or metro's price and inventory trajectory across runs.

**Not ideal for:**

- A certified valuation or AVM. This actor ships descriptive comp position only.
- Neighborhood-quality or safety screening. Those are outside the Fair-Housing fence by design.
- Cross-platform correlation with Redfin or Realtor.com. That is a separate job.

### How to find deals on Zillow

1. **Pick your job** — choose an `intent` (for example, Find underpriced deals) and paste a Zillow for-sale search URL into `searchUrls`, or `/homedetails/` URLs into `propertyUrls`. Leave the targets empty to run the Austin, TX demo.
2. **Configure options (optional)** — set `investorType` to re-weight the ranking for your strategy, or `rankBy` to sort by price cut, yield, or seller pressure. Defaults cover most cases.
3. **Run the actor** — click Start. The default deal queue returns in about a minute.
4. **Download results** — open the Dataset tab and export JSON, CSV, or Excel. The default view is the Deal Queue, sorted by attention priority and Deal Score.

### First run tips

- **Start with the demo or a small search** — leave the fields empty to run the Austin demo, or paste one search URL before scaling to many. See a ranked queue first.
- **Set a watchlist name to start the market memory** — a `watchlistName` turns a one-shot scrape into a tracked market. The saved memory compounds from run 1 and cannot be backfilled, so name it early.
- **Don't expect trajectory on run 1** — `history.dealVelocity`, change flags, and outcome profiles need accumulated runs. The first run banks the baseline; the deeper reads unlock as the memory grows, and `firstSightFallback` tells you when a property is new to the store.
- **Cap the detail pass with limits** — `limits.maxDetails` controls how many property pages are rendered and scored this run, which is the main cost lever.
- **Read coverage before trusting a market read** — Zillow restricts automated access from shared IPs, so the `coverage` record reports how many listings resolved (for example, 386 of 412). Treat the market read against that fraction.

### Typical performance

Observed in internal testing (June 2026, small sample). Values vary by market size, filters, and Zillow access conditions on the day.

| Metric | Typical value |
|--------|---------------|
| Listings per deals run | up to `limits.maxListings` (default 120) |
| Detail pages scored per run | up to `limits.maxDetails` (default 60) |
| Run time (default deal queue) | about a minute to first queue |
| Run time (large detail pass) | a few minutes |
| Detail coverage when access is restricted | reported in the `coverage` record |

### Input parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `intent` | string | No | — | The job in plain language: `find_deals`, `price_cuts`, `rental_yield`, `monitor_market`, `analyze_properties`, `comps`. Sets mode, ranking, and output. |
| `mode` | string | No | resolved from intent | Advanced override: `deals`, `properties`, `market`, `watchlist`. |
| `searchUrls` | array | Conditional | `[]` | Zillow for-sale search URLs (with `?searchQueryState=`) for deals mode. Up to 50. |
| `propertyUrls` | array | Conditional | `[]` | Zillow `/homedetails/` URLs for properties / watchlist mode. Up to 300. |
| `zipCodes` | array | Conditional | `[]` | ZIP codes for market mode. Up to 50. |
| `location` | string | No | `Austin, TX` (prefill) | A city or metro used when you don't paste a search URL. |
| `investorType` | string | No | `analyst` | Re-weights the deal score: `flipper`, `buy_and_hold`, `brrrr`, `cashflow`, `agent`, `analyst`. Changes the ranking, not the data. |
| `rankBy` | string | No | `opportunity` | Sort axis: `opportunity`, `dealScore`, `emerging`, `sellerPressure`, `priceCut`, `yield`, `daysOnMarket`, `recency`, `price`. |
| `watchlistName` | string | No | `""` | Stable name that turns any mode into a monitor; price/status/deal-score history persists keyed on this name. Renaming starts a fresh memory clock. |
| `changeWindowDays` | integer | No | `7` | Window for the "what changed in this market" report. |
| `deltaWindowDays` | integer | No | `7` | Comparison window for delta intelligence vs the prior run. |
| `alerts` | object | No | `{}` | Scheduled-run change feed: `minDealScore`, `onlyNewSinceLastRun`, `priceCutMinPct`, `highYieldMinPct`, `includeStatuses[]`, `alertWhenDealImproves`. Emits only actionable items plus deal cards. |
| `filters` | object | No | `{}` | Property-attribute filters only: `excludeAuction`, `excludePending`, `excludeNoRentEstimate`, `minBeds`, `maxHoaMonthly`, `propertyTypes[]`. No neighborhood/school/crime/demographic filters. |
| `outputPack` | string | No | resolved from intent | Which surfaces lead: `investor`, `agent`, `analyst`, `raw`. |
| `outputProfile` | string | No | `signals` | Field depth: `signals` (full intelligence), `compat` (drop-in maxcopell parity), `minimal`. |
| `limits` | object | No | `{ maxListings: 120, maxDetails: 60 }` | Cost control on the render-heavy detail pass. |
| `maxRuntimeSeconds` | integer | No | `3600` | Soft runtime budget; auto-clamps and emits partial output before a hard stop. |
| `proxyConfiguration` | object | No | US RESIDENTIAL | Apify proxy; residential is required because Zillow restricts automated access from shared IPs. |

#### Input examples

- **Find deals (first run):** `{ "mode": "deals", "searchUrls": ["https://www.zillow.com/austin-tx/houses/?searchQueryState=..."], "rankBy": "opportunity" }`
- **Scheduled price-cut watchlist:** `{ "intent": "price_cuts", "searchUrls": ["https://www.zillow.com/austin-tx/houses/?searchQueryState=..."], "watchlistName": "austin-east", "alerts": { "minDealScore": 70, "onlyNewSinceLastRun": true } }`
- **Drop-in compat pull:** `{ "mode": "properties", "propertyUrls": ["https://www.zillow.com/homedetails/..."], "outputProfile": "compat" }`

#### Input tips

- **Start with intent** — pick the job in plain language and let the actor set the mode, ranking, and output pack.
- **Name your watchlist consistently** — the saved memory keys on `watchlistName`; a stable name keeps the clock running, a rename starts a fresh one.
- **Lower `limits.maxDetails` to control cost** — fewer rendered detail pages is the main lever on a run's cost.
- **Use `outputProfile: compat` to verify migration** — it returns the exact maxcopell field set with validated URLs and zpids, no signal fields.

### Output example

A deal-queue row from a `deals` run (default `signals` profile, abbreviated; address invented):

```json
{
  "schemaVersion": "1.0",
  "recordType": "property",
  "eventId": "29483756",
  "attentionPriority": "high",
  "address": "4218 Sendero Hills Pkwy, Austin, TX 78724",
  "url": "https://www.zillow.com/homedetails/4218-Sendero-Hills-Pkwy-Austin-TX-78724/29483756_zpid/",
  "price": 389000,
  "zestimate": 421500,
  "rentZestimate": 2680,
  "daysOnZillow": 71,
  "dealScore": {
    "score": 83,
    "grade": "B",
    "components": { "belowComp": 26, "sellerMotivation": 24, "freshness": 6, "rentalYield": 19, "liquidity": 8 },
    "penalties": { "smallCompSet": 0, "lowConfidence": 0, "missingRentEstimate": 0 },
    "scoreEvidence": ["7.4% under comp-set median", "2 price cuts in 41 days", "gross yield above ZIP median"]
  },
  "sellerPressureIndex": {
    "value": 88,
    "components": { "daysOnMarket": 27, "cutCount": 22, "pctOffOriginal": 23, "relistCount": 8, "marketCooling": 8 },
    "evidence": ["71 days on market vs ZIP median 24", "cut twice, now 6.1% off original"]
  },
  "compSet": {
    "metricClass": "descriptive",
    "compMedianPrice": 420000,
    "compCount": 34,
    "pricePositionPct": -7.4,
    "competitivePosition": { "rank": 5, "of": 34 },
    "method": "public-comp-position",
    "basis": "public-comp-position"
  },
  "yieldScore": 72,
  "listingProfile": { "label": "motivated-seller", "strength": 0.81, "version": "1.0", "evidence": ["repeat_cut", "stale_listing"] },
  "whyNow": ["3rd-longest on market in this search", "Cut twice, now 7.4% under comp median", "Gross yield above the ZIP median"],
  "recommendedAction": "Compare to comps and re-check in 7 days",
  "signalEvents": [
    { "type": "repeat_cut", "signalStrength": 0.84, "active": true, "decayStatus": "active", "metricClass": "derived", "reason": "Two price cuts on this listing", "evidence": { "cutCount": 2, "pctOffOriginal": 6.1 } },
    { "type": "below_comp", "signalStrength": 0.71, "active": true, "decayStatus": "active", "metricClass": "descriptive", "reason": "List price under the comp-set median", "evidence": { "pricePositionPct": -7.4 } }
  ],
  "history": {
    "metricClass": "historical",
    "daysTracked": 38,
    "firstSeen": "2026-05-14",
    "cutCount": 2,
    "peakPrice": 414000,
    "currentVsPeakPct": -6.0,
    "trajectory": "cooling",
    "dealEvolution": [{ "date": "2026-05-14", "dealScore": 64 }, { "date": "2026-06-18", "dealScore": 83 }],
    "dealVelocity": 19,
    "firstSightFallback": false
  },
  "confidence": { "overall": 0.86, "grade": "high", "drivers": ["priceHistoryPresent", "compSetSize>=5", "rentZestimatePresent"], "missingData": [] },
  "changeFlags": ["PRICE_CUT"]
}
```

A run also emits a `coverage` record (`coveragePct` plus `marketMemoryCoverage`) and a `summary` record (`headline`, `dailyBriefing`, `executiveHighlights`, `dealQueue`).

### Output fields

| Field | Type | Description |
|-------|------|-------------|
| `recordType` | string | `property`, `market`, `marketSnapshot`, `dealCard`, `event`, `dealFeed`, `portfolioSummary`, `coverage`, `summary`, or `error`. |
| `attentionPriority` | string | `high`, `medium`, `low`, `none`; the deal-queue sort. |
| `dealScore.score` | number | Master 0-100 deal score. |
| `dealScore.components` | object | Points from below-comp, seller motivation, freshness, yield, liquidity. |
| `sellerPressureIndex.value` | number | 0-100 seller-pressure composite. |
| `compSet.pricePositionPct` | number | Percent vs comp-set median (negative = under). Descriptive, never an appraisal. |
| `compSet.competitivePosition` | object | `{ rank, of }`, e.g. 5th-cheapest of 34. |
| `yieldScore` | number | 0-100 descriptive rental-yield score. |
| `whyNow` | array | Plain-English reasons this listing needs attention now. |
| `recommendedAction` | string | Verb-fenced prioritization step; never a transaction instruction. |
| `signalEvents` | array | Typed, evidenced, decay-aware events. |
| `listingProfile.label` | string | One of 8 archetypes (deal, motivated-seller, cash-flow, and more). |
| `history.dealVelocity` | number | Rate of deal-score change; positive means the deal is improving. |
| `confidence.grade` | string | Data-sufficiency grade: high, medium, low. |
| `changeFlags` | array | Watchlist deltas: NEW, PRICE\_CUT, BACK\_ON\_MARKET, WENT\_PENDING, SOLD, UNCHANGED. |
| `marketMomentum` | number | Market records: -100..+100 (negative = cooling). |
| `zipOpportunityScore` | number | Market records: 0-100 per ZIP. |
| `coveragePct` | number | Coverage record: share of requested properties that resolved. |

### What this does NOT do

- **Not an appraisal or AVM.** Comp position is a descriptive read of where the list price sits versus comparable public listings, with a stated method. It is never a certified valuation or an automated valuation model.
- **No investment or transaction advice.** The actor never tells you to buy, offer, bid, invest, or sell. `recommendedAction` is prioritization only (Review, Compare, Re-check, Investigate, Add to watchlist, Flag). Yield is a descriptive estimate with a stated basis, never a return guarantee, and there are no appreciation forecasts.
- **Not a neighborhood-quality tool.** No scoring or filtering by neighborhood desirability, schools, crime, or demographics. Deal signals are price, comp, days-on-market, and yield only.
- **Not cross-platform.** Public Zillow data only. No Redfin or Realtor.com correlation.
- **No private data.** No login, no CAPTCHA bypass, no agent-PII outreach lists, no access to anything behind authentication.

![Sample deal-queue output: address, deal score, seller pressure, why now, and the recommended action](https://apifyforge.com/readme-assets/ryanclinton-zillow-scraper/output-table.png)

### How much does it cost to find Zillow deals?

Zillow Scraper uses pay-per-result pricing: you pay per intelligence result the run returns, with raw listing rows kept cheap. The final per-result price is shown on the actor's Store listing.

Set a spending limit on the actor to cap costs; the run stops emitting once the limit is reached and logs why. The detail pass is the main cost driver, so `limits.maxDetails` is your lever on a run's size. Apify's free tier ($5 in monthly credits) lets you test the deal queue before scaling.

### Drop-in migration from maxcopell

Switching from `maxcopell/zillow-scraper` + `zillow-detail-scraper` is frictionless because three things hold:

1. **Same input shapes.** Paste the same Zillow for-sale search URLs (with `?searchQueryState=`) or `/homedetails/` property URLs you already use. The actor accepts both.
2. **Same field set on demand.** Set `outputProfile: compat` and each record carries the exact maxcopell field set (zpid, address, price, homeStatus, beds, baths, livingArea, zestimate, rentZestimate, daysOnZillow, url), with validated URLs and zpids and no signal fields, for clean migration parity.
3. **The decision layer is additive.** The default `signals` profile adds the deal queue, Seller Pressure Index, comp position, yield, and saved history on top. One actor replaces the search-plus-detail suite.

Same input, same fields, plus which listings are actually deals.

### Find Zillow deals using the API

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run = client.actor("ryanclinton/zillow-scraper").call(run_input={
    "mode": "deals",
    "searchUrls": ["https://www.zillow.com/austin-tx/houses/?searchQueryState=..."],
    "rankBy": "opportunity",
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item.get("recordType") == "property":
        ds = item.get("dealScore", {})
        spi = item.get("sellerPressureIndex", {})
        print(f"{item.get('address')}: deal={ds.get('score')} SPI={spi.get('value')} -> {item.get('recommendedAction')}")
```

#### JavaScript

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

const client = new ApifyClient({ token: "YOUR_API_TOKEN" });

const run = await client.actor("ryanclinton/zillow-scraper").call({
    mode: "deals",
    searchUrls: ["https://www.zillow.com/austin-tx/houses/?searchQueryState=..."],
    rankBy: "opportunity",
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const item of items) {
    if (item.recordType === "property") {
        console.log(`${item.address}: deal=${item.dealScore?.score} SPI=${item.sellerPressureIndex?.value} -> ${item.recommendedAction}`);
    }
}
```

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/ryanclinton~zillow-scraper/runs?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "mode": "deals", "searchUrls": ["https://www.zillow.com/austin-tx/houses/?searchQueryState=..."], "rankBy": "opportunity" }'

curl "https://api.apify.com/v2/datasets/DATASET_ID/items?token=YOUR_API_TOKEN&format=json"
```

### How Zillow Scraper works

**Mental model:** Zillow listings to a faithful substrate, substrate to comp position and seller-pressure reads, reads to a deal score against the saved market memory, score to a ranked, routed deal queue, all wrapped in a trust layer.

![The intelligence stack: Zillow listings to comp position, Seller Pressure Index, yield and signals, Deal Score, Market Memory, and a ranked deal queue](https://apifyforge.com/readme-assets/ryanclinton-zillow-scraper/intelligence-layers.png)

#### 1. Pull the listings

The actor reads public Zillow listing and detail pages over residential routing, because Zillow restricts automated access from shared IPs. It parses each into the faithful substrate (price, status, days-on-market, price history, comps, rent estimate), validates that each URL resolves to its zpid, and drops any record whose link does not resolve rather than emitting a broken row.

#### 2. Read comp position, seller pressure, and yield

For each listing it reads where the list price sits versus comparable nearby listings, scores seller pressure from days-on-market, cut count, percent off original, and relists, and computes a descriptive rental-yield read from the public rent estimate. Each read is marked descriptive and carries its evidence.

#### 3. Score and rank against saved memory

The deterministic Deal Score combines the reads, takes penalties for thin data, and is re-weighted by `investorType`. The saved market memory supplies trajectory and Deal Velocity, so a listing that is becoming a deal can outrank a static one. The queue is then ranked by the chosen `rankBy` axis.

#### 4. Route, persist, and report coverage

Each listing gets an attention priority, a `whyNow`, and a verb-fenced `recommendedAction`. The run persists each property's and ZIP's public snapshot to the saved store for the next run, then emits a `coverage` record so the share of listings that resolved is stated honestly.

### Tips for best results

1. **Schedule a watchlist run.** The saved market memory only compounds if you run it. A weekly cadence builds the trajectory and Deal Velocity reads a one-shot scrape cannot.
2. **Match `rankBy` to the job.** Use `priceCut` for reductions, `yield` for cash-flow, `sellerPressure` for negotiable sellers, `emerging` for deals that are becoming deals.
3. **Set the investor type.** A flipper weights below-comp and seller pressure; a cash-flow investor weights yield; an analyst stays neutral.
4. **Read coverage before trusting a market read.** The `coverage` record tells you how many listings resolved this run.
5. **Pipe the Deal Feed onward.** Push the deal queue into complementary actors (see [Combine with other Apify actors](#combine-with-other-apify-actors)) to route only the listings that need a human into a sheet, CRM, or alert.

### Limitations

- **Public data only.** No login, no private listing or agent-portal data, no booking or transaction history.
- **Comp position is descriptive, not a valuation.** It reads where the list price sits versus comparable public listings, with a stated method; it is never an appraisal or AVM.
- **Access varies.** Zillow restricts automated access from shared IPs; some pages may not resolve on a given run, and the `coverage` record reports the fraction that did.
- **Trajectory needs history.** Deal Velocity, change flags, and outcome profiles require accumulated runs; they return `firstSightFallback` or `insufficientHistory` until the memory matures.
- **Renaming a watchlist resets memory.** State keys on `watchlistName`; a new name starts a fresh memory clock that cannot be backfilled.
- **No cross-platform data.** Public Zillow only; no Redfin or Realtor.com correlation.
- **Outcome profiles are descriptive, not predictive.** They report how similar past listings played out, not what this property will do.

### Integrations

- [Zapier](https://apify.com/integrations/zapier) — schedule a daily deal run so the market memory compounds, and route the deal queue onward.
- [Make](https://apify.com/integrations/make) — branch on `attentionPriority` to fire alerts only for the deals that need a human.
- [Google Sheets](https://apify.com/integrations/google-sheets) — append the daily deal queue to a tracking sheet.
- [Apify API](https://docs.apify.com/api/v2) — run Zillow Scraper from any HTTP client and read the dataset.
- [Webhooks](https://docs.apify.com/platform/integrations/webhooks) — push run completion into your own pipeline.
- [LangChain / LlamaIndex](https://docs.apify.com/platform/integrations) — feed the deal queue to an AI agent or real-estate workflow.

### How do I find houses likely to drop in price?

Run the actor with `rankBy: emerging`. Backed by the saved market memory, it surfaces listings whose Deal Score is rising (days-on-market climbing, cuts increasing, comp gap widening) before the cut is obvious. The `emerging_deal` signal and `history.dealVelocity` carry the evidence. This read needs accumulated runs, so schedule a watchlist to build it.

### How do I track a Zillow listing's price over time?

Run `properties` or `watchlist` mode with the property URLs and a stable `watchlistName`, then schedule a run. Each record's `history` carries the price trajectory and cut count, and `dealEvolution` shows how the Deal Score moved across runs. The trajectory accumulates from your first run forward and cannot be backfilled.

### Troubleshooting

**The deal queue is smaller than the search.** The detail pass is capped by `limits.maxDetails` (default 60) to control cost. Raise it to score more listings, and read the `coverage` record to see how many resolved.

**A listing shows `firstSightFallback: true` and no history.** That property is new to the saved store, so there is no accumulated trajectory yet. Run a watchlist on it and the history builds from this run forward.

**Outcome profiles return `insufficientHistory`.** There are not yet enough similar closed deals in the store to report descriptive frequencies. The field says so rather than guessing; it fills in as the store grows.

**Market read looks thin.** Zillow restricts automated access from shared IPs, so some pages may not resolve on a given run. The `coverage` record states the fraction that did; treat the market read against it and re-run if needed.

### Recent updates

- **Seller Pressure Index** — one 0-100 number per listing, with its full component breakdown, that reads which sellers are under pressure.
- **Saved Market Memory** — per-property and per-ZIP public history that compounds every run, surfaced as `marketMemoryCoverage`.
- **Deal Velocity and emerging deals** — `history.dealVelocity` and the `emerging_deal` signal surface listings that are becoming deals.
- **Drop-in compat profile** — `outputProfile: compat` returns the exact maxcopell field set for clean migration.
- **Productised coverage** — a first-class `coverage` record states how many listings resolved, instead of hiding gaps.

### Responsible use

- Zillow Scraper extracts publicly available real-estate listing data from Zillow. It does not bypass authentication, CAPTCHAs, or access restricted content, and it performs no in-platform actions.
- Users are responsible for ensuring their use complies with applicable laws and platform terms, including data protection regulations in their jurisdiction.
- Comp position is a descriptive read of public list prices, never an appraisal or valuation, and the actor gives no investment, offer, or transaction advice. Do not use its output as the sole basis for a financial decision.
- The actor performs no scoring or filtering by neighborhood desirability, schools, crime, or demographics, in line with Fair Housing principles. Do not use extracted agent contact data for spam or unauthorized outreach.
- For guidance on web scraping legality, see [Apify's guide](https://blog.apify.com/is-web-scraping-legal/).

### Combine with other Apify actors

After the deal queue, route the listings that matter into your own pipeline:

| Actor | How to combine |
|-------|---------------|
| [Website Contact Scraper](https://apify.com/ryanclinton/website-contact-scraper) | Pull contact details from a listing agent's own brokerage site. |
| [Company Deep Research](https://apify.com/ryanclinton/company-deep-research) | Research the brokerage or investment firm behind a set of listings. |
| [Website Change Monitor](https://apify.com/ryanclinton/website-change-monitor) | Track an agent or brokerage site alongside the Zillow deal queue. |
| [HubSpot Lead Pusher](https://apify.com/ryanclinton/hubspot-lead-pusher) | Push deal-flagged listings into your CRM pipeline. |
| [WHOIS Domain Lookup](https://apify.com/ryanclinton/whois-domain-lookup) | Resolve ownership of a brokerage's linked domains. |
| [Website Content to Markdown](https://apify.com/ryanclinton/website-content-to-markdown) | Convert listing or brokerage pages to markdown for an LLM workflow. |

The real competitor isn't another scraper. It's the spreadsheet. This actor exists to retire it: paste a search, get the deals ranked with the reason and the next step, and let the market memory compound every run.

### FAQ

**What is the difference between a Zillow scraper and a Zillow deal finder?** A scraper returns raw listing rows. Zillow Scraper runs as a deal finder: it ranks listings by which are actually deals, scores seller pressure and yield, and remembers what changed across runs, so you get a decision rather than a dataset.

**Can I migrate from maxcopell/zillow-scraper without changing my input?** Yes. Paste the same search or property URLs you already use, and set `outputProfile: compat` for the exact maxcopell field set with validated URLs and zpids. The decision layer is additive on the default profile. It is a practical alternative to running the search-plus-detail suite when you want the deals, not just the rows.

**How do I find underpriced homes on Zillow?** Paste a for-sale search URL and run with `rankBy: opportunity`. The deal queue surfaces listings below the comp median, with the margin in `compSet.pricePositionPct` and the reason in `whyNow`.

**How do I find motivated sellers?** Sort by `rankBy: sellerPressure`. The Seller Pressure Index ranks listings by accumulated days-on-market, repeat cuts, and relists, and the Motivated-Seller Radar carries the drivers.

**Is the comp position an appraisal?** No. Comp position is a descriptive read of where the list price sits versus comparable public listings, marked `metricClass: descriptive` with a stated method. It is never an appraisal, an automated valuation, or a certified value.

**Does this actor give investment advice?** No. It describes listings and ranks them by deal signals. `recommendedAction` is prioritization only (Review, Compare, Re-check, Investigate, Add to watchlist, Flag), never Buy, Offer, Bid, Invest, or Sell, and it makes no return or appreciation claims.

**How does the saved market memory work?** Every run records the public price, status, and days-on-market of every property it touches, keyed per property and per ZIP, and accumulates it across runs. Later runs read trajectory, Deal Velocity, and what changed from that store. The clock cannot be backfilled, so the value compounds the longer you run it.

**How many properties does a run cover?** The detail pass is capped by `limits.maxDetails` to control cost, and Zillow restricts automated access from shared IPs, so some pages may not resolve. The `coverage` record states exactly how many resolved (for example, 386 of 412), so you always know the fraction behind a market read.

**Can I rank the same data for my strategy?** Yes. Set `investorType` (flipper, buy & hold, BRRRR, cash-flow, agent, analyst) to re-weight the deal score. A flipper weights below-comp and seller pressure; a cash-flow investor weights yield. Same data, different ranking.

**Does it cover schools, crime, or neighborhood quality?** No. By design the actor performs no neighborhood-quality, school, crime, or demographic scoring or filtering, in line with Fair Housing principles. Deal signals are price, comp, days-on-market, and yield only.

**Can I monitor a market and get only what changed?** Yes. Set a stable `watchlistName` and schedule a run. Each record's `changeFlags` and the Deal Feed report new listings, price cuts, back-on-market, and went-pending since the prior run.

**Is it legal to scrape Zillow data?** Zillow Scraper accesses only public listing content and performs no in-platform actions or auth circumvention. Whether your specific use is permitted depends on your jurisdiction and intended use, including data protection rules and Zillow's terms; consult legal counsel for your situation. See [Apify's guide on scraping legality](https://blog.apify.com/is-web-scraping-legal/).

### Help us improve

If you encounter issues, you can help us debug faster by enabling run sharing in your Apify account:

1. Go to [Account Settings > Privacy](https://console.apify.com/account/privacy)
2. Enable **Share runs with public Actor creators**

This lets us see your run details when something goes wrong, so we can fix issues faster. Your data is only visible to the actor developer, not publicly.

### Support

Found a bug or have a feature request? Open an issue in the Issues tab on this actor's page. For custom solutions or enterprise integrations, reach out through the Apify platform.

# Actor input Schema

## `intent` (type: `string`):

Pick the job in plain language and the actor sets the right mode, ranking, and output for you. Leave the targets below empty to run the demo (deals in Austin, TX).

## `mode` (type: `string`):

Override the mode chosen by Intent. 'deals' ranks the listings in a search. 'properties' explains specific property URLs. 'market' returns a ZIP/metro's trajectory plus standout listings. 'watchlist' tracks a set across runs and returns what changed.

## `searchUrls` (type: `array`):

Deals mode: paste Zillow for-sale search URLs (the ones with ?searchQueryState=). The actor ranks the listings by which are actually deals.

## `propertyUrls` (type: `array`):

Properties / watchlist mode: Zillow /homedetails/ URLs. Each result carries its deal score, comp position, Seller Pressure Index, and history. With a watchlist name set, a tracked property set becomes a portfolio with a priority rollup. Leave everything empty to run the built-in demo on a few sample listings.

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

Market mode: one or more ZIP codes to read a market's price/inventory/yield trajectory plus its standout listings.

## `location` (type: `string`):

A city or metro to resolve to a search area, e.g. 'Austin, TX'. Used in market mode, or in deals mode when you don't paste a search URL.

## `investorType` (type: `string`):

Re-ranks the same data for your strategy. A flipper weights below-comp + seller pressure; a cash-flow investor weights rental yield; an analyst keeps it neutral. Changes the ranking, not the data.

## `rankBy` (type: `string`):

How to order the deal queue. 'opportunity' (default) blends deal score, seller pressure, and momentum. 'emerging' surfaces listings becoming deals. 'sellerPressure' sorts by the Seller Pressure Index.

## `watchlistName` (type: `string`):

Give this run a stable watchlist name to track the set across runs. Price/status/deal-score history persists in a named store keyed on this name, so the next run reports what changed since last time. Rename = a fresh memory clock.

## `changeWindowDays` (type: `integer`):

The window for the 'what changed in this market' report, even on a first watchlist run.

## `deltaWindowDays` (type: `integer`):

The comparison window for delta intelligence (what changed since the prior run).

## `alerts` (type: `object`):

When set, the run emits only actionable items plus automation-ready deal cards. Fields: minDealScore, onlyNewSinceLastRun, priceCutMinPct, highYieldMinPct, includeStatuses\[], alertWhenDealImproves (fire on a deal-score jump, not just a price cut). Turns a watchlist run into a clean change feed instead of a re-dump.

## `filters` (type: `object`):

Property-attribute filters only: excludeAuction, excludePending, excludeNoRentEstimate, minBeds, maxHoaMonthly, propertyTypes\[]. By design this actor does NOT filter on neighborhood desirability, schools, crime, or demographics.

## `outputPack` (type: `string`):

Curates which decision surfaces lead the output. 'investor' = deal queue + motivated sellers + cash flow + price cuts. 'agent' = new listings + price cuts + back on market + went pending. 'analyst' = market snapshot + inventory + DOM + ZIP comparison. 'raw' = compat fields.

## `outputProfile` (type: `string`):

'signals' (default) = full envelope: comp position + scores + Seller Pressure Index + signals + history + change story. 'compat' = exact maxcopell field set with validated URLs/zpids, no signal fields (migration parity). 'minimal' = zpid + URL + price + deal score.

## `limits` (type: `object`):

Cost control on the render-heavy detail pass: { maxListings, maxDetails }. maxDetails caps how many property pages are rendered and scored this run.

## `maxRuntimeSeconds` (type: `integer`):

Soft runtime budget. The actor auto-clamps against the platform timeout and emits partial output before a hard stop.

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

Apify proxy. Defaults to US RESIDENTIAL, which Zillow requires because it restricts automated access from shared IPs. Leave as default unless you have a reason to change it.

## Actor input object example

```json
{
  "searchUrls": [],
  "propertyUrls": [
    "https://www.zillow.com/homedetails/12400-Cedar-St-Austin-TX-78732/125904370_zpid/",
    "https://www.zillow.com/homedetails/Austin-TX-78745/29500899_zpid/"
  ],
  "zipCodes": [],
  "location": "",
  "investorType": "analyst",
  "watchlistName": "",
  "changeWindowDays": 7,
  "deltaWindowDays": 7,
  "alerts": {},
  "filters": {},
  "outputProfile": "signals",
  "limits": {
    "maxListings": 120,
    "maxDetails": 60
  },
  "maxRuntimeSeconds": 3600,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `dealQueue` (type: `string`):

Every buyer's 5-second read: the best deals in this run ranked, with what to review first and why.

## `ignoreQueue` (type: `string`):

Rows the actor deprioritized (above comp, no movement, low seller pressure) and why.

## `priceCuts` (type: `string`):

Listings with price-cut or repeat-cut signals, by depth.

## `motivatedSellers` (type: `string`):

Listings ranked by the Motivated-Seller Radar and Seller Pressure Index.

## `cashFlow` (type: `string`):

Rental investors: listings ranked by descriptive yield score.

## `marketTrajectory` (type: `string`):

Market records with lifecycle, momentum, ZIP opportunity score, and percentiles.

## `newSinceLastRun` (type: `string`):

Watchlist subscribers: rows with a non-empty change set since the prior run.

## `dealFeed` (type: `string`):

Slack / Zapier / agent consumption: events by importance and the consolidated deal feed.

## `compat` (type: `string`):

Substrate fields with maxcopell-compatible names, validated URLs and zpids, no signals.

## `belowComps` (type: `string`):

Listings with below-comp or back-on-market signals.

## `coverageErrors` (type: `string`):

The coverage block plus per-record data quality and any errors.

## `summary` (type: `string`):

Exec one-glance: the run rollup, daily briefing, executive highlights, and leaderboards.

## `allItems` (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 = {
    "searchUrls": [],
    "propertyUrls": [
        "https://www.zillow.com/homedetails/12400-Cedar-St-Austin-TX-78732/125904370_zpid/",
        "https://www.zillow.com/homedetails/Austin-TX-78745/29500899_zpid/"
    ],
    "zipCodes": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("ryanclinton/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 = {
    "searchUrls": [],
    "propertyUrls": [
        "https://www.zillow.com/homedetails/12400-Cedar-St-Austin-TX-78732/125904370_zpid/",
        "https://www.zillow.com/homedetails/Austin-TX-78745/29500899_zpid/",
    ],
    "zipCodes": [],
}

# Run the Actor and wait for it to finish
run = client.actor("ryanclinton/zillow-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchUrls": [],
  "propertyUrls": [
    "https://www.zillow.com/homedetails/12400-Cedar-St-Austin-TX-78732/125904370_zpid/",
    "https://www.zillow.com/homedetails/Austin-TX-78745/29500899_zpid/"
  ],
  "zipCodes": []
}' |
apify call ryanclinton/zillow-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=ryanclinton/zillow-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Zillow Scraper — Deal Finder, Price Cuts & Market Monitor",
        "description": "Find real estate deals on Zillow. Returns a ranked deal queue — below-comp listings, price cuts, motivated sellers, rental yield, and what changed since last run. Drop-in compatible with maxcopell/zillow-scraper + zillow-detail-scraper.",
        "version": "1.0",
        "x-build-id": "R4QEuxIPoYgW22WhY"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/ryanclinton~zillow-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-ryanclinton-zillow-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/ryanclinton~zillow-scraper/runs": {
            "post": {
                "operationId": "runs-sync-ryanclinton-zillow-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/ryanclinton~zillow-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-ryanclinton-zillow-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "intent": {
                        "title": "What do you want to do?",
                        "enum": [
                            "find_deals",
                            "price_cuts",
                            "rental_yield",
                            "monitor_market",
                            "analyze_properties",
                            "comps"
                        ],
                        "type": "string",
                        "description": "Pick the job in plain language and the actor sets the right mode, ranking, and output for you. Leave the targets below empty to run the demo (deals in Austin, TX)."
                    },
                    "mode": {
                        "title": "Mode (advanced — usually set by Intent)",
                        "enum": [
                            "deals",
                            "properties",
                            "market",
                            "watchlist"
                        ],
                        "type": "string",
                        "description": "Override the mode chosen by Intent. 'deals' ranks the listings in a search. 'properties' explains specific property URLs. 'market' returns a ZIP/metro's trajectory plus standout listings. 'watchlist' tracks a set across runs and returns what changed."
                    },
                    "searchUrls": {
                        "title": "Zillow search URLs",
                        "maxItems": 50,
                        "type": "array",
                        "description": "Deals mode: paste Zillow for-sale search URLs (the ones with ?searchQueryState=). The actor ranks the listings by which are actually deals.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "propertyUrls": {
                        "title": "Zillow property URLs",
                        "maxItems": 300,
                        "type": "array",
                        "description": "Properties / watchlist mode: Zillow /homedetails/ URLs. Each result carries its deal score, comp position, Seller Pressure Index, and history. With a watchlist name set, a tracked property set becomes a portfolio with a priority rollup. Leave everything empty to run the built-in demo on a few sample listings.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "zipCodes": {
                        "title": "ZIP codes (market mode)",
                        "maxItems": 50,
                        "type": "array",
                        "description": "Market mode: one or more ZIP codes to read a market's price/inventory/yield trajectory plus its standout listings.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "location": {
                        "title": "City / metro (market or deals mode)",
                        "type": "string",
                        "description": "A city or metro to resolve to a search area, e.g. 'Austin, TX'. Used in market mode, or in deals mode when you don't paste a search URL.",
                        "default": ""
                    },
                    "investorType": {
                        "title": "Investor type (re-weights the deal score)",
                        "enum": [
                            "analyst",
                            "flipper",
                            "buy_and_hold",
                            "brrrr",
                            "cashflow",
                            "agent"
                        ],
                        "type": "string",
                        "description": "Re-ranks the same data for your strategy. A flipper weights below-comp + seller pressure; a cash-flow investor weights rental yield; an analyst keeps it neutral. Changes the ranking, not the data.",
                        "default": "analyst"
                    },
                    "rankBy": {
                        "title": "Rank by",
                        "enum": [
                            "opportunity",
                            "dealScore",
                            "emerging",
                            "sellerPressure",
                            "priceCut",
                            "yield",
                            "daysOnMarket",
                            "recency",
                            "price"
                        ],
                        "type": "string",
                        "description": "How to order the deal queue. 'opportunity' (default) blends deal score, seller pressure, and momentum. 'emerging' surfaces listings becoming deals. 'sellerPressure' sorts by the Seller Pressure Index."
                    },
                    "watchlistName": {
                        "title": "Watchlist name (turns any mode into a monitor)",
                        "type": "string",
                        "description": "Give this run a stable watchlist name to track the set across runs. Price/status/deal-score history persists in a named store keyed on this name, so the next run reports what changed since last time. Rename = a fresh memory clock.",
                        "default": ""
                    },
                    "changeWindowDays": {
                        "title": "Change window (days)",
                        "minimum": 1,
                        "maximum": 90,
                        "type": "integer",
                        "description": "The window for the 'what changed in this market' report, even on a first watchlist run.",
                        "default": 7
                    },
                    "deltaWindowDays": {
                        "title": "Delta window (days)",
                        "minimum": 1,
                        "maximum": 90,
                        "type": "integer",
                        "description": "The comparison window for delta intelligence (what changed since the prior run).",
                        "default": 7
                    },
                    "alerts": {
                        "title": "Alerts (scheduled-run change feed)",
                        "type": "object",
                        "description": "When set, the run emits only actionable items plus automation-ready deal cards. Fields: minDealScore, onlyNewSinceLastRun, priceCutMinPct, highYieldMinPct, includeStatuses[], alertWhenDealImproves (fire on a deal-score jump, not just a price cut). Turns a watchlist run into a clean change feed instead of a re-dump.",
                        "default": {}
                    },
                    "filters": {
                        "title": "Filters (property attributes only)",
                        "type": "object",
                        "description": "Property-attribute filters only: excludeAuction, excludePending, excludeNoRentEstimate, minBeds, maxHoaMonthly, propertyTypes[]. By design this actor does NOT filter on neighborhood desirability, schools, crime, or demographics.",
                        "default": {}
                    },
                    "outputPack": {
                        "title": "Output pack (which decision surfaces)",
                        "enum": [
                            "investor",
                            "agent",
                            "analyst",
                            "raw"
                        ],
                        "type": "string",
                        "description": "Curates which decision surfaces lead the output. 'investor' = deal queue + motivated sellers + cash flow + price cuts. 'agent' = new listings + price cuts + back on market + went pending. 'analyst' = market snapshot + inventory + DOM + ZIP comparison. 'raw' = compat fields."
                    },
                    "outputProfile": {
                        "title": "Output profile (field depth)",
                        "enum": [
                            "signals",
                            "compat",
                            "minimal"
                        ],
                        "type": "string",
                        "description": "'signals' (default) = full envelope: comp position + scores + Seller Pressure Index + signals + history + change story. 'compat' = exact maxcopell field set with validated URLs/zpids, no signal fields (migration parity). 'minimal' = zpid + URL + price + deal score.",
                        "default": "signals"
                    },
                    "limits": {
                        "title": "Limits (cost control)",
                        "type": "object",
                        "description": "Cost control on the render-heavy detail pass: { maxListings, maxDetails }. maxDetails caps how many property pages are rendered and scored this run.",
                        "default": {
                            "maxListings": 120,
                            "maxDetails": 60
                        }
                    },
                    "maxRuntimeSeconds": {
                        "title": "Max runtime (seconds)",
                        "minimum": 60,
                        "maximum": 7200,
                        "type": "integer",
                        "description": "Soft runtime budget. The actor auto-clamps against the platform timeout and emits partial output before a hard stop.",
                        "default": 3600
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Apify proxy. Defaults to US RESIDENTIAL, which Zillow requires because it restricts automated access from shared IPs. Leave as default unless you have a reason to change it.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ],
                            "apifyProxyCountry": "US"
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
