# Zillow Sold Comps Scraper: Comparable Sales & ARV (`epicscrapers/zillow-sold-comps-scraper`) Actor

Get sold comps from Zillow for any US address: the most similar recent sales nearby with sale price, $/sqft, days on market and a similarity score, plus a comp-based value estimate. ARV for flips, list prices in non-disclosure states.

- **URL**: https://apify.com/epicscrapers/zillow-sold-comps-scraper.md
- **Developed by:** [Epic Scrapers](https://apify.com/epicscrapers) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.30 / 1,000 comp saveds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### Zillow Sold Comps Scraper: comparable sales and ARV for any address

Zillow Sold Comps Scraper finds the best comparable sales for a home in seconds. Give it an address, a Zillow link or a zpid, and it returns the most similar homes **sold nearby recently**, ranked by a similarity score, with sale price, $/sqft, days on market, sale-to-list ratio and the value each comp indicates for your property. Every property also gets a **comp-based value estimate** with a low–high range.

Built for real estate investors, wholesalers and flippers, agents preparing a CMA, appraisers, lenders, iBuyer-style underwriting, and AI agents that need a comp check as a tool.

- **Real comps, not a city dump.** Comps are picked around your property the way an appraiser would: same home type, sold in the last 6 months, within a mile, similar size and bedrooms. Every rule can be changed.
- **Ranked by similarity.** Each comp gets a 0–100 score from distance, living area, sale date, bedrooms, bathrooms, age and lot size, plus its differences from your property (`bedroomsDiff`, `livingAreaDiff`, …) for an adjustment grid.
- **A value estimate per property.** The median value indicated by the comps ($/sqft × your living area), with a 25th–75th percentile range. Outliers such as a luxury penthouse among garden condos are flagged and left out.
- **After-repair value (ARV) in one line.** Add `| 3 bd, 2 ba, 1700 sqft` (or `| +1 bd, +400 sqft`) after an address, and it comps the home as it will be after the renovation. Put the line in with and without it to see the uplift.
- **Works in Texas and other non-disclosure states.** Zillow hides sale prices there. This scraper reads the last asking price before each sale from the price history and uses it (clearly marked) so you still get $/sqft and an estimate.
- **Never too few comps.** When the radius has too few sales, it widens automatically (1 → 2 → 3 miles by default) and still ranks the closest first.
- **Bulk and fast.** Hundreds of addresses in one run, 3 properties in parallel. 30 properties with 10 comps each took 68 seconds in our tests.
- **API- and agent-ready.** Plain JSON in, flat rows out. Call it from the Apify API, Make, Zapier, n8n, or any AI agent through the [Apify MCP server](#use-it-from-ai-agents-mcp).
- **Pay per comp, no subscription.** About 2 cents for 10 comps. No Zillow account, API key or proxy setup. Export to JSON, CSV, Excel or Google Sheets.

**Scope:** This Actor is built around one property at a time. For every home sold, listed or rented in a city or ZIP code, use [Zillow Scraper](https://apify.com/epicscrapers/zillow-scraper) or [Zillow ZIP Code Search Scraper](https://apify.com/epicscrapers/zillow-zip-search-scraper).

### Contents

- [What can you do with it?](#what-can-you-do-with-it) · [How it compares](#how-it-compares) · [Quick start](#quick-start)
- [After-repair value (ARV)](#after-repair-value-arv) · [Input reference](#input-reference) · [Ready-to-run examples](#ready-to-run-examples)
- [Run at scale](#run-at-scale) · [Use through the Apify API](#use-through-the-apify-api) · [Use it from AI agents (MCP)](#use-it-from-ai-agents-mcp)
- [Output](#output) · [How the estimate works](#how-the-estimate-works) · [Cost](#cost) · [FAQ](#faq)

### What can you do with it?

| Goal                           | How                                                                                                         |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| Comp a property for an offer   | Add the address, keep the defaults. Read `estimatedValue` in the Valuation summary and check the top comps. |
| ARV for a flip                 | Add the planned facts after a `\|`: `123 Main St, Austin, TX 78704 \| 3 bd, 2 ba, 1700 sqft`.               |
| Underwrite a deal list         | Paste all addresses. Every property gets its comps and an estimate in one run.                              |
| Screen listings for a discount | Comp homes for sale and compare `listPrice` with `estimatedValue` in the summary.                           |
| Prepare a CMA for a listing    | Set `maxComps` to 5–6 and turn on `includeDetails` for photos, descriptions and agents.                     |
| Check pricing in a fast market | Set `soldWithinDays` to `90` and `rankBy` to `recent`; look at `saleToListRatio` and `daysOnMarket`.        |
| Track a portfolio's value      | Save the addresses as a task and schedule it monthly. Compare `estimatedValue` per `zpid` between runs.     |
| Rural or unusual homes         | Raise `radiusMiles` to 3–5 and `soldWithinDays` to `365`, set `livingAreaMatch` to `40`.                    |
| Give an AI agent a comp tool   | Connect the Apify MCP server with this Actor as a tool. See [AI agents](#use-it-from-ai-agents-mcp).        |

### How it compares

| What you need                   | Zillow Sold Comps Scraper                                | Area-wide sold scrapers | Zillow's "similar homes sold" list | Comping by hand on Zillow |
| ------------------------------- | -------------------------------------------------------- | ----------------------- | ---------------------------------- | ------------------------- |
| Starts from your property       | Yes: address, URL or zpid, on or off market              | No, a city or ZIP list  | Yes                                | Yes                       |
| Your comp criteria              | Radius, sold within, type, beds, baths, sqft, year built | City or ZIP filters     | None, Zillow picks                 | Manual                    |
| Ranked by similarity            | 0–100 score and differences per comp                     | No                      | No                                 | Your judgment             |
| Value estimate and range        | Yes, with outliers removed                               | No                      | No                                 | Spreadsheet work          |
| ARV after repairs               | Yes, one line                                            | No                      | No                                 | Manual                    |
| Texas and non-disclosure states | Last list price before the sale, marked                  | Sales without a price   | Varies                             | Price hidden              |
| Hundreds of properties          | One run                                                  | One run per area        | One property at a time             | Hours                     |

### Quick start

1. Click **Try for free** and sign in to Apify.
2. Add one or more properties: `1916 N 46th St, Seattle, WA 98103`, a Zillow property URL or a zpid.
3. Keep the default comp criteria for a first run.
4. Click **Start**. Open the **Output** tab: **Comparable sales** has the comps, **Valuation summary** the estimate per property.

```json
{
    "subjects": ["1916 N 46th St, Seattle, WA 98103", "https://www.zillow.com/homedetails/119624861_zpid/"],
    "maxComps": 10
}
```

### After-repair value (ARV)

Write the home's facts after the work behind a `|` on its line. Anything you leave out stays as Zillow has it.

| You write                                  | Meaning                                                   |
| ------------------------------------------ | --------------------------------------------------------- |
| `\| 3 bd, 2 ba, 1700 sqft`                 | 3 bedrooms, 2 bathrooms, 1,700 sqft after repairs         |
| `\| +1 bd, +0.5 ba, +400 sqft`             | Add a bedroom, a half bath and 400 sqft to Zillow's facts |
| `\| 4 beds 3 baths 2,200 sq ft built 2026` | Also sets the year built (for a rebuild)                  |
| `\| duplex`, `\| townhome`, `\| house`     | Changes the home type (e.g. a conversion)                 |

Common spellings work: `3bd`, `3 beds`, `3-bed`, `3BR/2BA`, `1500sf`, `1,500 sq ft`. Words the scraper doesn't understand (such as `new roof`) are reported in the log and the rest of the line is still used. The Valuation summary shows each change as `asIs` → `afterRepair`, and each comp row carries the text in `subjectAfterRepair`.

As-is value and ARV of a 2-bed townhome renovated into a 3-bed with 1,700 sqft, in one run:

```json
{
    "subjects": ["1916 N 46th St, Seattle, WA 98103", "1916 N 46th St, Seattle, WA 98103 | 3 bd, 1700 sqft"],
    "maxComps": 5
}
```

In a test run this home comped at **$863,000** as it is (Zestimate $794,600) and **$1,100,000** after the planned work, from five 3-bedroom sales of 1,640–1,844 sqft within 0.35 miles.

The value follows mostly the living area: more bedrooms on the same area picks comps with more bedrooms, but the estimate moves with their $/sqft. Add the square feet you are adding, too.

### Input reference

| Field                         | Type             | Default        | Effect                                                                                                                                          |
| ----------------------------- | ---------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `subjects`                    | Array of strings | Required       | US street addresses, Zillow property URLs or zpids, one per entry. On or off the market. Add `\| …` for ARV.                                    |
| `maxComps`                    | Integer          | `10`           | Comps per property, best first (1–100).                                                                                                         |
| `includeDetails`              | Boolean          | `false`        | Adds description, all photos, agent contacts, price and tax history, schools and 150+ facts to each comp. Charged as a separate event per comp. |
| `soldWithinDays`              | Integer          | `180`          | Only sales from the last N days (1–1,095). Filters by sale date.                                                                                |
| `radiusMiles`                 | Number           | `1`            | Search radius around the property (0.05–25).                                                                                                    |
| `expandRadius`                | Boolean          | `true`         | Double the radius up to `maxRadiusMiles` when too few sales match.                                                                              |
| `maxRadiusMiles`              | Number           | `3`            | The widest the search may go.                                                                                                                   |
| `sameHomeType`                | Boolean          | `true`         | Houses with houses, condos with condos, townhomes with townhomes.                                                                               |
| `bedroomsMatch`               | String           | `"1"`          | `"exact"`, `"1"`, `"2"` or `"any"` (± bedrooms).                                                                                                |
| `bathroomsMatch`              | String           | `"1"`          | `"exact"`, `"0.5"`, `"1"`, `"2"` or `"any"` (± bathrooms).                                                                                      |
| `livingAreaMatch`             | String           | `"25"`         | `"10"`, `"15"`, `"20"`, `"25"`, `"30"`, `"40"`, `"50"` or `"any"` (± percent).                                                                  |
| `yearBuiltMatch`              | String           | `"any"`        | `"5"`, `"10"`, `"20"`, `"30"` or `"any"` (± years).                                                                                             |
| `rankBy`                      | String           | `"similarity"` | `"similarity"` (0–100 score), `"recent"` (newest sale first) or `"closest"`.                                                                    |
| `onlyPricedComps`             | Boolean          | `true`         | Skip sales without any price. Skipped sales are not charged.                                                                                    |
| `useListPriceWhenUndisclosed` | Boolean          | `true`         | In non-disclosure states, value with the last asking price before the sale, marked `valuationBasis: "lastListPrice"`.                           |

Note that the match fields are strings, not numbers: `"livingAreaMatch": "40"`. Invalid lines (a ZIP code, a city page URL, text without a house number) are skipped with a message in the log and the run summary, and the rest of the run goes on.

### Ready-to-run examples

#### Underwrite a deal list

```json
{
    "subjects": [
        "1916 N 46th St, Seattle, WA 98103",
        "https://www.zillow.com/homedetails/119624861_zpid/",
        "7775469",
        "114407255"
    ],
    "maxComps": 6
}
```

Open the **Valuation summary** and sort by `estimatedValue` minus `listPrice` (or your offer) to find the best margins.

#### Loose comps for a rural property

```json
{
    "subjects": ["https://www.zillow.com/homedetails/119624861_zpid/"],
    "radiusMiles": 3,
    "maxRadiusMiles": 10,
    "soldWithinDays": 365,
    "livingAreaMatch": "40",
    "yearBuiltMatch": "20"
}
```

#### Strict comps for an appraisal-style grid

```json
{
    "subjects": ["1916 N 46th St, Seattle, WA 98103"],
    "maxComps": 6,
    "radiusMiles": 0.5,
    "maxRadiusMiles": 1,
    "soldWithinDays": 90,
    "bedroomsMatch": "exact",
    "bathroomsMatch": "0.5",
    "livingAreaMatch": "15",
    "yearBuiltMatch": "10",
    "includeDetails": true
}
```

Use the **Adjustment grid** view: one row per comp with its differences from the property, the price used and the indicated value.

#### Fast-market check: what sold in the last 90 days

```json
{
    "subjects": ["1916 N 46th St, Seattle, WA 98103"],
    "maxComps": 20,
    "soldWithinDays": 90,
    "rankBy": "recent"
}
```

Read `medianSaleToListRatio` and `medianDaysOnMarket` in the summary.

### Run at scale

- **Batch size.** There is no fixed limit on `subjects`. Properties run 3 at a time; plan on roughly 2–3 seconds per property with 10 comps. For thousands, split into runs of a few hundred so one bad batch is easy to rerun.
- **Cap the cost.** Set **Maximum cost per run** in the run options (`maxTotalChargeUsd` in the API). The scraper plans its work against the budget, saves what it covers and stops cleanly; it never charges past the cap.
- **Keep runs cheap.** `includeDetails` costs more than the comp itself; turn it on only for the properties you will look at closely. `onlyPricedComps` (on by default) means you never pay for a comp that can't value the property.
- **Join the outputs.** Every comp row starts with `subjectInput`, `subjectZpid` and `subjectAddress`, and carries `subjectEstimatedValue`, so the dataset alone is enough for most spreadsheets. The low–high range and medians are in the `SUMMARY` record, one entry per input in input order, with an `input` field to match.
- **Schedule it.** Save your input as a task (**Save as a new task**), then add it to a schedule in **Schedules** (for example `0 6 1 * *`, monthly) to track a portfolio's values. Connect Google Sheets, Make, Zapier, n8n or a webhook in the task's **Integrations** tab.
- **Errors don't stop the run.** A property that isn't found gets an `error` in the summary; one with no matching sales gets a `note` saying what to relax. The run's status message counts both.

### Use through the Apify API

Run synchronously and get the comps back in one request (good for up to a few dozen properties; synchronous runs time out after 5 minutes):

```bash
curl "https://api.apify.com/v2/acts/epicscrapers~zillow-sold-comps-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"subjects": ["1916 N 46th St, Seattle, WA 98103"], "maxComps": 10}'
```

For larger batches, start the run, wait for it, and read both the comps and the valuation summary:

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('epicscrapers/zillow-sold-comps-scraper').call(
    {
        subjects: ['1916 N 46th St, Seattle, WA 98103', '1916 N 46th St, Seattle, WA 98103 | 3 bd, 1700 sqft'],
        maxComps: 10,
    },
    { maxTotalChargeUsd: 1 },
);
const { items: comps } = await client.dataset(run.defaultDatasetId).listItems();
const summary = await client.keyValueStore(run.defaultKeyValueStoreId).getRecord('SUMMARY');
for (const s of summary.value) console.log(s.address, s.estimatedValue, s.estimatedValueLow, s.estimatedValueHigh);
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("epicscrapers/zillow-sold-comps-scraper").call(
    run_input={"subjects": ["1916 N 46th St, Seattle, WA 98103"], "maxComps": 10},
    max_total_charge_usd=1,
)
comps = client.dataset(run["defaultDatasetId"]).list_items().items
summary = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("SUMMARY")["value"]
```

The summary is also a plain URL: `https://api.apify.com/v2/key-value-stores/<defaultKeyValueStoreId>/records/SUMMARY?token=YOUR_APIFY_TOKEN`.

### Use it from AI agents (MCP)

AI assistants and agents (Claude, ChatGPT, Cursor, VS Code, n8n and anything that speaks the Model Context Protocol) can call this Actor as a tool through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp). The agent sends an address, the Actor returns the ranked comps and the estimate, and the agent answers with the numbers.

#### Connect

Hosted server (OAuth sign-in, or an `Authorization: Bearer YOUR_APIFY_TOKEN` header), with this Actor and the storage tools enabled:

```
https://mcp.apify.com?tools=epicscrapers/zillow-sold-comps-scraper,storage
```

Claude Code:

```bash
claude mcp add --transport http apify "https://mcp.apify.com?tools=epicscrapers/zillow-sold-comps-scraper,storage" \
  --header "Authorization: Bearer YOUR_APIFY_TOKEN"
```

Claude Desktop, Cursor and other clients with a JSON config (runs the server locally):

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": ["-y", "@apify/actors-mcp-server", "--tools", "epicscrapers/zillow-sold-comps-scraper,storage"],
            "env": { "APIFY_TOKEN": "YOUR_APIFY_TOKEN" }
        }
    }
}
```

The Actor shows up as the tool `epicscrapers-slash-zillow-sold-comps-scraper`, with this page's input schema as its parameters.

#### What to ask

- "Pull sold comps for 1916 N 46th St, Seattle, WA 98103 and tell me what it's worth. Show the 5 closest comps with price, $/sqft and distance."
- "What is the ARV of 123 Main St, Austin, TX 78704 if I add a bedroom and 400 sqft? Compare it with the as-is value."
- "Here are 20 listings from my deal sheet. Comp each one and list the ones priced at least 10% below the estimate."
- "Is https://www.zillow.com/homedetails/119624861_zpid/ overpriced? Compare its list price with recent sales within half a mile."

#### Notes for agent builders

- **Smallest useful call:** `{"subjects": ["<address>"], "maxComps": 5}`. Keep `includeDetails` off: details add descriptions, photos and histories that fill the model's context and cost more.
- **One call for many homes.** Pass every address in one `subjects` array instead of one call per address.
- **Where the answer is.** Each dataset row is one comp and carries `subjectEstimatedValue`, so the default tool output is enough for "what is it worth". The range (`estimatedValueLow`/`estimatedValueHigh`), medians, `listPrice`, and the `error` or `note` of a property without comps are in the `SUMMARY` record of the run's key-value store; with the `storage` tools enabled the agent can read it.
- **No comps is not an error.** A property without matching sales has no dataset rows. Read its `note` in `SUMMARY` and retry with a larger `maxRadiusMiles`, `soldWithinDays` or `livingAreaMatch`.
- **Say how prices were derived.** When `valuationBasis` is `lastListPrice` (Texas and other non-disclosure states), the comp's price is its last asking price, not the sale price. `priceOutlier: true` comps are excluded from the estimate.
- **Set a budget.** Give the agent a `maxTotalChargeUsd` per run; 10 comps cost about $0.02.

You can also hand an assistant an export: download the **Adjustment grid** view as CSV and ask, for example, "Adjust each comp for the bedroom, bathroom and size differences at $X per sqft and $Y per bathroom, then give me an adjusted value range."

### Output

#### Comparable sales (dataset)

One row per comp, best first, grouped by property. Each row starts with the property it is a comp for:

| Field                                                                                                              | Example                                                        |
| ------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
| `subjectInput`, `subjectZpid`, `subjectAddress`, `subjectAfterRepair`, `subjectEstimatedValue`, `subjectListPrice` | `1916 N 46th St, Seattle, WA 98103`, `845000`                  |
| `compRank`, `similarityScore`, `distanceMiles`                                                                     | `1`, `77`, `0.19`                                              |
| `bedroomsDiff`, `bathroomsDiff`, `livingAreaDiff`, `yearBuiltDiff`                                                 | `0`, `-0.5`, `-300`, `13` (comp minus property)                |
| `soldDate`, `soldPrice`, `salePriceDisclosed`, `nominalSale`                                                       | `2026-10-09`, `770000`, `true`, `false`                        |
| `lastListPrice`, `listedDate`, `daysOnMarket`, `saleToListRatio`                                                   | `785000`, `2026-08-05`, `65`, `0.981`                          |
| `valuationPrice`, `valuationBasis`                                                                                 | `770000`, `soldPrice` (or `lastListPrice`)                     |
| `pricePerSqft`, `indicatedValue`, `priceOutlier`                                                                   | `720`, `986400` ($/sqft × the property's living area), `false` |
| `zpid`, `url`, `address`, `streetAddress`, `city`, `state`, `zipcode`, `latitude`, `longitude`                     |                                                                |
| `homeType`, `bedrooms`, `bathrooms`, `livingArea`, `lotSize`, `lotSizeUnit`, `yearBuilt`                           | `townhome`, `2`, `2`, `1070`                                   |
| `zestimate`, `rentZestimate`, `taxAssessedValue`, `taxAssessmentYear`, `photo`, `brokerName`, `mlsName`            |                                                                |

With `includeDetails`, each comp also has `description`, `photos`, `listingAgent`, `coListingAgent`, `broker`, `priceHistory`, `taxHistory`, `schools`, `monthlyHoaFee`, `walkScore`, `climateRisk`, `facts` (150+) and more.

The Output tab has two views: **Comps** and **Adjustment grid** (differences from the property, price used and indicated value). Export as JSON, CSV, Excel, XML or HTML.

#### Valuation summary (key-value store record `SUMMARY`)

An array with one entry per property, in input order:

```json
{
    "input": "1916 N 46th St, Seattle, WA 98103",
    "zpid": "2137390015",
    "address": "1916 N 46th St, Seattle, WA 98103",
    "status": "other",
    "listPrice": null,
    "afterRepair": null,
    "homeType": "townhome",
    "bedrooms": 2,
    "bathrooms": 2.5,
    "livingArea": 1370,
    "estimatedValue": 845000,
    "estimatedValueLow": 822000,
    "estimatedValueHigh": 956000,
    "compsUsedForEstimate": 6,
    "priceOutliers": 0,
    "compsUsingListPrice": 0,
    "medianPricePerSqft": 617,
    "medianSoldPrice": 782475,
    "medianDaysOnMarket": 54,
    "medianSaleToListRatio": 0.995,
    "averageDistanceMiles": 0.28,
    "zestimate": 794600,
    "compsSaved": 6,
    "radiusMilesUsed": 1,
    "soldWithinDays": 180
}
```

`listPrice` is the asking price of a property that is for sale, to compare with the estimate. Properties that could not be found have an `error`, and properties without matching sales a `note` that says what to relax.

#### Notes on fields

- `similarityScore` is 0–100. Weights: distance 25%, living area 25%, sale recency 15%, bedrooms, bathrooms and year built 10% each, lot size 5%.
- `*Diff` fields are the comp minus your property (after repairs, if given): `livingAreaDiff: -300` means the comp is 300 sqft smaller.
- `soldPrice` is `null` where the sale price is private; `valuationPrice` is then the last list price, and `valuationBasis` says which one was used.
- `nominalSale: true` marks transfers under $10,000 (deed transfers, tax sales). They are never used to value.
- `radiusMilesUsed` in the summary shows whether the search had to widen.
- Missing values are `null`.

### How the estimate works

1. Each comp's price (sale price, or the last list price where sale prices are private) is divided by its living area.
2. That $/sqft times your property's living area is the comp's `indicatedValue`.
3. Comps far from the others in $/sqft (outside 1.5 × the interquartile range, with 4 or more comps) are flagged as `priceOutlier` and left out.
4. `estimatedValue` is the median indicated value; `estimatedValueLow` and `estimatedValueHigh` are the 25th and 75th percentiles, rounded to $1,000. At least 3 priced comps are needed.

It is a quick, transparent $/sqft comp valuation for screening and offers, not an appraisal. Condition, views and upgrades are not priced in; use the photos and descriptions (`includeDetails`) to judge them.

### Cost

You pay per comp saved, with no monthly fee and no start fee beyond Apify's standard one. Looking up your properties, skipped unpriced sales and the valuation summary are free.

| Event          | Free plan | Bronze  | Silver  | Gold and higher | Charged when                                          |
| -------------- | --------- | ------- | ------- | --------------- | ----------------------------------------------------- |
| `comp`         | $0.0023   | $0.0020 | $0.0017 | $0.0013         | A comp is saved.                                      |
| `comp-details` | $0.0036   | $0.0030 | $0.0024 | $0.0017         | `includeDetails` is on and the comp's details loaded. |

Example runs:

| Run                                    | Free plan | Gold   |
| -------------------------------------- | --------- | ------ |
| 1 property, 10 comps                   | $0.023    | $0.013 |
| 1 property, 10 comps with full details | $0.059    | $0.030 |
| 100 properties, 10 comps each          | $2.30     | $1.30  |
| 1,000 properties, 5 comps each         | $11.50    | $6.50  |

Set a maximum cost per run; the scraper stops before reaching it and never charges past it. The **Pricing** tab always shows the current prices.

### Tips

- **Non-disclosure states** (Texas, Utah, Idaho, Wyoming, New Mexico, Montana, Kansas, Mississippi, Louisiana, Alaska, North Dakota and some counties elsewhere): `soldPrice` is empty and `valuationBasis` is `lastListPrice`. List prices are usually within a few percent of the sale price, but check `compsUsingListPrice` in the summary.
- **Too few comps?** Raise `maxRadiusMiles`, `soldWithinDays` or the tolerances. The summary's `note` says when nothing matched.
- **New construction** can skew comps for older homes: set `yearBuiltMatch` to `"10"` or `"20"`.
- **Wrong facts on Zillow** (for example a finished basement not counted): write the correct values after a `|`, e.g. `| 2400 sqft`.

### FAQ

#### Which homes can I comp?

Any US home Zillow knows: for sale, off market, recently sold or rented. Use the street address with city and state (or ZIP), the Zillow property URL, or the zpid.

#### Why is a comp missing that I see on Zillow?

It may be outside the criteria (type, size, bedrooms, date, radius), or have no price when `onlyPricedComps` is on.

#### How fast is it?

5 properties with defaults took 15 seconds; 30 properties with 10 comps each took 68 seconds. Full details add one request per comp, loaded in parallel.

#### Why did my run stop early?

If the log says the charge limit was reached, the run hit its maximum cost or your account's spending limit. Everything charged before that is saved. Raise the limit to collect more.

#### Do I need a separate scraper for details?

No. Turn on `includeDetails` and every comp is complete.

#### Can I search all sold homes in a city or ZIP code instead?

Use [Zillow Scraper](https://apify.com/epicscrapers/zillow-scraper) or [Zillow ZIP Code Search Scraper](https://apify.com/epicscrapers/zillow-zip-search-scraper) for area-wide sold lists.

#### Do I need proxies, a Zillow account or an API key?

No. The scraper handles connections and retries itself and doesn't log in.

#### Is it legal?

It collects publicly visible listing data. Agent contact details are personal data: use them lawfully (for example under CAN-SPAM, TCPA or GDPR).

### Support

Report problems or request features in the **Issues** tab. Include the run ID, the input and what you expected. Bugs are fixed quickly.

This Actor is not affiliated with or endorsed by Zillow Group. "Zillow" and "Zestimate" are trademarks of Zillow, Inc.

# Actor input Schema

## `subjects` (type: `array`):

The homes you want comps for, one per line: a US street address (1916 N 46th St, Seattle, WA 98103), a Zillow property URL or a zpid. Works for homes on or off the market; add hundreds at once.

After-repair value (ARV): add a | and the home's facts after the work, e.g. "1916 N 46th St, Seattle, WA 98103 | 3 bd, 2 ba, 1700 sqft". Use + or - for changes: "| +1 bd, +400 sqft". You can also set "built 2026" or a home type (house, townhome, condo, multi-family). Add the same home with and without | to compare as-is and after-repair values.

## `maxComps` (type: `integer`):

How many comparable sales to return for each property, best matches first. 5–10 is typical for an appraisal or CMA; use more for market research.

## `includeDetails` (type: `boolean`):

Adds the full property page to every comp: description, all photos, listing agent and broker contacts, full price and tax history, schools, HOA fee, walk score, climate risk and 150+ facts (condition, heating, parking, renovations…). Useful to judge comp quality. Charged as an extra event per comp.

## `soldWithinDays` (type: `integer`):

Only sales from the last N days. 90 or 180 for current value; 365 in slow or rural markets. Maximum 1,095 (3 years).

## `radiusMiles` (type: `number`):

Distance from the property to look for comps. 0.5 in dense cities, 1 in suburbs, 3–5 in rural areas. Decimals allowed (0.25).

## `expandRadius` (type: `boolean`):

When fewer sales than requested match inside the radius, search again at double the radius, up to the maximum below. Closer comps still rank higher.

## `maxRadiusMiles` (type: `number`):

The widest the search may go when widening is on.

## `sameHomeType` (type: `boolean`):

Compare houses with houses, condos with condos, townhomes with townhomes. Turn off for mixed or unusual properties.

## `bedroomsMatch` (type: `string`):

How far a comp's bedroom count may be from the property's.

## `bathroomsMatch` (type: `string`):

How far a comp's bathroom count may be from the property's (half baths count as 0.5).

## `livingAreaMatch` (type: `string`):

How far a comp's living area may be from the property's, in percent. Appraisers usually stay within ±20–25%.

## `yearBuiltMatch` (type: `string`):

How far a comp's year built may be from the property's. Useful to keep new construction apart from older homes.

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

Which matching sales come first. Best match uses a similarity score (0–100) from distance, size, sale date, bedrooms, bathrooms, age and lot size.

## `onlyPricedComps` (type: `boolean`):

Skip sales without any price, so every comp you pay for can be used to value the property. Unpriced sales are not charged.

## `useListPriceWhenUndisclosed` (type: `boolean`):

Texas, Utah and other non-disclosure states hide sale prices. With this on, comps there use the last asking price before the sale (from the price history) for $/sqft and the value estimate, marked valuationBasis: lastListPrice. The sale price itself is never invented.

## Actor input object example

```json
{
  "subjects": [
    "1916 N 46th St, Seattle, WA 98103",
    "1916 N 46th St, Seattle, WA 98103 | 3 bd, 1700 sqft",
    "https://www.zillow.com/homedetails/119624861_zpid/"
  ],
  "maxComps": 10,
  "includeDetails": false,
  "soldWithinDays": 180,
  "radiusMiles": 1,
  "expandRadius": true,
  "maxRadiusMiles": 3,
  "sameHomeType": true,
  "bedroomsMatch": "1",
  "bathroomsMatch": "1",
  "livingAreaMatch": "25",
  "yearBuiltMatch": "any",
  "rankBy": "similarity",
  "onlyPricedComps": true,
  "useListPriceWhenUndisclosed": true
}
```

# Actor output Schema

## `comps` (type: `string`):

No description

## `summary` (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 = {
    "subjects": [
        "1916 N 46th St, Seattle, WA 98103",
        "1916 N 46th St, Seattle, WA 98103 | 3 bd, 1700 sqft",
        "https://www.zillow.com/homedetails/119624861_zpid/"
    ],
    "maxComps": 10,
    "soldWithinDays": 180,
    "radiusMiles": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("epicscrapers/zillow-sold-comps-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 = {
    "subjects": [
        "1916 N 46th St, Seattle, WA 98103",
        "1916 N 46th St, Seattle, WA 98103 | 3 bd, 1700 sqft",
        "https://www.zillow.com/homedetails/119624861_zpid/",
    ],
    "maxComps": 10,
    "soldWithinDays": 180,
    "radiusMiles": 1,
}

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

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

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

```

## CLI example

```bash
echo '{
  "subjects": [
    "1916 N 46th St, Seattle, WA 98103",
    "1916 N 46th St, Seattle, WA 98103 | 3 bd, 1700 sqft",
    "https://www.zillow.com/homedetails/119624861_zpid/"
  ],
  "maxComps": 10,
  "soldWithinDays": 180,
  "radiusMiles": 1
}' |
apify call epicscrapers/zillow-sold-comps-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,epicscrapers/zillow-sold-comps-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Jz2O45coEChkhk0cP/builds/tc1FMKHvCnW2mbxwn/openapi.json
