# Zillow Sold Homes Scraper | $0.60/1K | Dates & Comps (`x_guru/zillow-sold-homes-scraper`) Actor

Scrape Zillow recently sold homes with reported sale prices and dates. Filter candidate comps by sale period, distance, bedrooms and area; export to CSV.

- **URL**: https://apify.com/x_guru/zillow-sold-homes-scraper.md
- **Developed by:** [Hundevmode Labs](https://apify.com/x_guru) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 2 monthly users, 95.1% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 1,000 paid plans: saved sold properties

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

### Zillow Sold Homes Scraper

Search Zillow recently sold homes and export reported sale dates, sale prices and property attributes. Add optional distance, bedroom and living-area criteria to select candidate comparable properties.

**Result pricing:** $0.60/1,000 property rows on paid Apify plans; $1.20/1,000 on Free. Automatic source access recovery is included. No Zillow login or additional service is required.

### What you can collect

- Search sold listings by city, ZIP code or Zillow search URL.
- Filter by an inclusive sale-date range; records with unknown sale dates are excluded when date filtering is enabled.
- Measure distance from subject coordinates and apply bedroom or area tolerances.
- Keep sale-price provenance in the export and distinguish source reports from independently verified transaction data.

### How to use Zillow Sold Homes Scraper

1. Enter locations or sold-search URLs; optionally set sale dates and comparable-property criteria.
2. Set **Maximum results this run** and optional filters.
3. Set a spending limit in Apify run options, then click **Start**.
4. Open the results, review the coverage summary, and download CSV, Excel or JSON.

#### Quick-start input

```json
{
  "locations": [
    "Boston, MA"
  ],
  "maxResults": 10
}
```

This example is a starting point; replace its sources with your own. URL-only runs should leave locations empty.

### Input options

| Input | Purpose | Default |
| --- | --- | --- |
| `locations / startUrls` | Cities with state, ZIP codes or Zillow search URLs; sold search is automatic. | Empty |
| `maxResults` | Maximum new sold-property rows this run. | 100 |
| `soldAfter / soldBefore` | Inclusive `YYYY-MM-DD` sale-date range. | Unset |
| `subject.latitude / longitude` | Required together only when supplying comparable-property criteria. | Unset |
| `subject.radiusMiles` | Maximum distance from the subject, in miles. | 5 when subject is supplied |
| `subject.bedrooms / bedroomTolerance` | Target bedroom count and allowed absolute difference. | Unset / 1 |
| `subject.livingArea / areaTolerance` | Target square feet and allowed relative difference; `0.3` means ±30%. | Unset / 0.3 |
| `resumeFromRunId` | Continue the latest finished batch with the same query. | Unset |
| `advanced` | Optional request, time, download and retry ceilings. | 300 requests, 600 seconds, 50 MB on paid plans |

Manually supplied source lists accept up to 100 entries. You may combine supported input methods; earlier sources can consume the shared result limit before later ones are reached.

### Output: CSV and JSON fields

Each result is one property record. Missing source fields are `null`. The default table uses readable column labels while exports keep stable field names.

| Data | Columns |
| --- | --- |
| Property | `recordId`, `zpid`, `url`, `address`, `city`, `state`, `zipcode` |
| Reported sale | `soldDate`, `soldPrice`, `soldPriceSource`, `transactionPriceVerified` |
| Comparison | `pricePerSquareFoot`, `distanceMiles` |
| Size | `bedrooms`, `bathrooms`, `livingArea` |

Nested attributes, where present, are serialized as JSON strings so CSV remains flat. Errors are not mixed into paid dataset rows. **Coverage and limits** (`RUN_SUMMARY`), **Source issues** (`ERRORS`) and **Continue collection** (`PAGINATION`) are separate run output links. `REQUEST_METRICS` and `CHECKPOINT` are available in key-value storage.

Search pagination and bounded map splitting handle larger areas. Date and comparison filters intentionally exclude records without the fields needed to satisfy them.

### 💳 Pricing and spending limits

| Apify account | Price per 1,000 property rows | Collection allowance |
| --- | ---: | --- |
| Paid plan | $0.60 | Requested count, source availability and run limits |
| Free | $1.20 | 50 rows across one collection and its continuation chain |

Only saved result rows are charged as results. Failed requests and diagnostic records are kept separately.

The platform actor-start event is **$0.00005 per GB of allocated memory, with a minimum of one event**. A 256 MB run therefore pays $0.00005 to start. Each new continuation run and each Resurrect may incur another start event. There are no separate enrichment or access-recovery add-ons. Platform usage is included in the PPE policy; no extra dataset-item event is configured.

Apify authenticates the account plan automatically; input cannot select the cheaper rate. **Free is an Apify account plan, not zero-price output:** results consume available account credits. Free runs also stop at 40 requests, 180 seconds or 10 MB of compressed downloaded responses. These ceilings cannot be raised in the input. Downloads are checked between responses, so one response can exceed the remaining traffic allowance. Independent fresh collections have independent allowances; this is not an account-wide monthly quota.

Minimum permitted Max total charge: $0.00125, covering the start event and at least one result at the highest Free rate. This is a minimum budget setting, not a minimum bill: only the start and saved result events are charged. The same result rates apply to all paid Apify plans.

Set **Max total charge** in Apify run options, or `maxTotalChargeUsd` through the API. The requested result count is not a dollar budget. The SDK checks remaining run budget before saving charged rows, and Apify can stop a run when its budget is reached. Paid-account credit balance and overage settings are separate from this run limit. Rates are configured for the current private release; the actor has not been published in Apify Store.

### 🔁 Continue after a limit or source error

1. Keep the saved dataset and wait until the previous run has finished.
2. Open **Continue collection** (`PAGINATION`) in the run output.
3. If `hasMore=true`, copy the **full `nextInput`** into a new run of this same actor. It includes `resumeFromRunId`.
4. Keep the query, filters and any monitor baseline unchanged. You may change `maxResults` and advanced collection limits; ZIP per-code quotas stay unchanged.
5. Set a new spending budget. Download this batch’s dataset and combine it with your earlier batches.

The next batch contains newly saved rows only. Earlier rows are skipped and are not charged again along that continuation chain. Use the **latest** finished batch: branching twice from an older run can create overlapping datasets. The previous run must belong to your own account and this actor, and its storage must remain available.

| Stop or issue | Next step |
| --- | --- |
| Result, request, time or download ceiling | Use `PAGINATION.nextInput` if `hasMore=true`; adjust the next batch’s limits. |
| Temporary restriction or HTTP 403 | Bounded automatic recovery has already been attempted. Retain progress and try the continuation later. A fresh run does not guarantee that access is restored. |
| Spending limit | Keep saved rows and resume with a new per-run budget. The budget is not shared across the chain. |
| Free collection allowance | Continuation and Resurrect do not replenish it. Upgrade the account before collecting beyond the Free allowance. |
| Invalid input or changed query | Correct the input; keep the original query for continuation or start a separate collection. |
| Missing checkpoint after a hard stop | A usable continuation may be unavailable. Keep any saved rows; a fresh collection cannot guarantee deduplication against that dataset. |

Apify **Resurrect** restarts the same run and storage. `resumeFromRunId` creates a separate batch and dataset. Do not use both methods simultaneously. A requested count is a ceiling, not a promise that Zillow has that many accessible records.

### Run through the Apify API

The same JSON input works in Apify Console and through the [Apify Actor API](https://docs.apify.com/api/v2/actor-runs). Start a run, wait for it to finish, then export its dataset as JSON, CSV or Excel. Use the actor ID from its Console page and your own Apify API token; no Zillow API key is required. Pass `maxTotalChargeUsd` as a run option rather than an input field. Never put API tokens in the input.

### Choose the right Zillow actor

Choose this actor for reported sale dates, price provenance and candidate-comparable filters. Use **Zillow Scraper** for basic sold search results, or **Zillow Property Details Scraper** for available histories of known properties.

### Frequently asked questions

#### Is soldPrice a verified closing price?

No. The actor returns Zillow-reported sale information and marks `transactionPriceVerified=false`. Missing sale data stays `null`; asking prices from active listings are not substituted for sale prices.

#### Does the actor produce a home valuation?

No. The optional criteria select candidate comparables. They do not calculate an appraisal or certify that the properties are suitable valuation comps.

#### What does areaTolerance=0.3 mean?

A property must have living area within 30% of the subject. For a 1,200 sq ft subject, that is 840–1,560 sq ft. Missing area is excluded when this criterion is active.

#### Why are fewer results returned after date or comp filters?

The source may contain listings without usable sale dates, coordinates or size values. Such rows cannot satisfy those criteria and are excluded.

#### Is this an official Zillow API?

No. This is an independent actor callable through the Apify API. It is not affiliated with, sponsored or endorsed by Zillow. Source fields, layouts and access can change; complete extraction and uninterrupted access are not guaranteed.

# Actor input Schema

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

US city and state or five-digit ZIP, one per entry. Leave empty when using URLs only. If both are supplied, both are collected up to the shared result limit.

## `maxResults` (type: `integer`):

Maximum NEW property rows saved in this run (1–100,000). Free: 50 rows per collection across continuation batches. Source availability, time and Max total charge can stop earlier.

## `startUrls` (type: `array`):

Paste a Zillow search URL. This actor applies its selected listing type. Property detail, agent and building URLs are not supported here.

## `minPrice` (type: `number`):

Optional Zillow search filter. Prices are USD; rental prices are monthly. Living area is in square feet.

## `maxPrice` (type: `number`):

Optional Zillow search filter. Prices are USD; rental prices are monthly. Living area is in square feet.

## `minBedrooms` (type: `number`):

Optional Zillow search filter. Prices are USD; rental prices are monthly. Living area is in square feet.

## `maxBedrooms` (type: `number`):

Optional Zillow search filter. Prices are USD; rental prices are monthly. Living area is in square feet.

## `minBathrooms` (type: `number`):

Optional Zillow search filter. Prices are USD; rental prices are monthly. Living area is in square feet.

## `minSquareFeet` (type: `number`):

Optional Zillow search filter. Prices are USD; rental prices are monthly. Living area is in square feet.

## `maxSquareFeet` (type: `number`):

Optional Zillow search filter. Prices are USD; rental prices are monthly. Living area is in square feet.

## `soldAfter` (type: `string`):

YYYY-MM-DD. Records with unknown sale dates are excluded when filtering dates.

## `soldBefore` (type: `string`):

YYYY-MM-DD. Records with unknown sale dates are excluded when filtering dates.

## `subject` (type: `object`):

Optional coordinates and similarity tolerances. Results are candidate comps, not a valuation.

## `resumeFromRunId` (type: `string`):

Optional finished run ID from this actor and your account. Copy the full PAGINATION.nextInput to continue from the latest batch. Keep query filters unchanged; change maxResults or advanced limits if needed. Saved rows are skipped. Continuation does not renew the Free allowance or guarantee recovery from a 403.

## `advanced` (type: `object`):

Request, time and download ceilings. Automatic access recovery is included and requires no additional setup.

## Actor input object example

```json
{
  "locations": [
    "Austin, TX"
  ],
  "maxResults": 3
}
```

# Actor output Schema

## `results` (type: `string`):

No description

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

No description

## `continuation` (type: `string`):

No description

## `errors` (type: `string`):

No description

## `requests` (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 = {
    "locations": [
        "Austin, TX"
    ],
    "maxResults": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("x_guru/zillow-sold-homes-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "locations": ["Austin, TX"],
    "maxResults": 3,
}

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

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

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

```

## CLI example

```bash
echo '{
  "locations": [
    "Austin, TX"
  ],
  "maxResults": 3
}' |
apify call x_guru/zillow-sold-homes-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,x_guru/zillow-sold-homes-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/8JiEWphkhsWt6JzeK/builds/ojZnbYbKiyyWkZ8Pt/openapi.json
