# US Property Sales Scraper (`scrapyx/us-property-sales-scraper`) Actor

Recorded property sale prices from official open data: New York City (Department of Finance) and Cook County incl. Chicago (Assessor). Address, price, date, property class, units, square feet; Cook adds buyer, seller and deed. Non-market sales filtered.

- **URL**: https://apify.com/scrapyx/us-property-sales-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.84 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

## US Property Sales Scraper (NYC, Cook County / Chicago)

**What properties actually sold for**, from official recorded-sales data:
**New York City** (Department of Finance rolling sales) and **Cook County,
Illinois, including Chicago** (Assessor parcel sales). Sale date and price,
address and ZIP, property class; NYC adds borough, neighborhood, units,
land and gross square feet and year built; Cook County adds buyer, seller,
deed type, and every parcel in a multi-parcel sale.

Non-market transfers are filtered out by default. No key, no login, no proxy.

### What it is for

- **Comparables** for valuation and appraisal.
- **Investor and agent research** — who is buying what, where, for how much.
- **Market tracking** — prices by neighborhood, class and month.

### Input

| field | what it does |
| --- | --- |
| `markets` | `nyc`, `cook_county` — one search each. |
| `dateFrom` / `dateTo` | Sale dates. Default: the last 120 days (both sources lag). |
| `minSalePrice` / `maxSalePrice` | Default minimum $10,000 (see note 1); `0` keeps everything. |
| `nycBoroughs`, `nycNeighborhoodContains`, `nycBuildingClassContains`, `nycZipCodes` | NYC filters. |
| `cookPropertyClasses` | Assessor class codes or prefixes (`2` residential, `299` condos, `5` commercial). |
| `includeFlaggedSales` | Cook County: keep sales the Assessor itself flags. |
| `sortBy`, `maxItems` | Newest or oldest first; per market, default 200. |

### Things about this data worth knowing

#### 1. A third of NYC "sales" are transfers, not sales

29,750 of the 82,345 rows in NYC's rolling year have a price under $1,000 —
$0 and $10 deeds between relatives, into trusts and the like. The default
minimum of $10,000 leaves them out.

#### 2. Cook County repeats one price on every parcel of a deed

A quarter of Cook County's 2026 sale rows belong to multi-parcel deeds
(a condo with its parking space, several lots) and each row carries the
**whole** price. Adding them up overstates the market. The Actor returns
one sale per deed with a `parcels` list (192 of 1,500 sales in a test run
covered more than one parcel).

#### 3. The Assessor flags its own doubtful sales

Cook County marks sales under $10,000, non-standard deeds (such as
quit-claims) and repeat sales within a year. They are excluded unless you
turn on `includeFlaggedSales`; each sale carries the flags.

#### 4. Cook County sales have no address

The sales data has only the parcel number (PIN). The Actor looks up each
parcel's property address in the Assessor's address data — not the owner's
mailing address.

#### 5. Both lag

On 25 September 2026 NYC's newest sale was 31 August (the file is refreshed
monthly) and Cook County's 14 July. Each summary shows `newestSaleInData`.

### Output

One `PROPERTY_SALE` row per sale (the source record(s) under `source`) and
one `SEARCH_SUMMARY` per market.

```json
{
  "recordType": "PROPERTY_SALE",
  "market": "nyc",
  "saleDate": "2026-08-31",
  "salePrice": 1162500,
  "address": "109 WEST 26TH STREET, 8B",
  "borough": "MANHATTAN",
  "neighborhood": "CHELSEA",
  "zip": "10001",
  "propertyClass": "10 COOPS - ELEVATOR APARTMENTS",
  "yearBuilt": 1910,
  "parcelId": "1-802-31"
}
```

### Limits

- Two markets for now. Connecticut's statewide sales data was evaluated and
  left out: its newest recorded sale was a year old.

# Actor input Schema

## `markets` (type: `array`):

One search per market.

## `dateFrom` (type: `string`):

YYYY-MM-DD. Default: 120 days ago (both sources lag by weeks).

## `dateTo` (type: `string`):

YYYY-MM-DD.

## `minSalePrice` (type: `integer`):

Default $10,000 removes $0 / $10 transfers between related parties (a third of NYC's rows). 0 = keep them.

## `maxSalePrice` (type: `integer`):

Optional.

## `nycBoroughs` (type: `array`):

MANHATTAN, BRONX, BROOKLYN, QUEENS, STATEN ISLAND.

## `nycNeighborhoodContains` (type: `string`):

e.g. 'chelsea', 'park slope'.

## `nycBuildingClassContains` (type: `string`):

e.g. 'condo', 'one family', 'coops'.

## `nycZipCodes` (type: `array`):

3-5 digits.

## `cookPropertyClasses` (type: `array`):

Assessor class codes or prefixes, e.g. '2' (residential), '299' (condominium), '5' (commercial).

## `includeFlaggedSales` (type: `boolean`):

The Assessor flags sales under $10k, non-standard deed types and repeat sales within a year.

## `sortBy` (type: `string`):

By sale date.

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

0 = every match.

## `minRequestInterval` (type: `number`):

Never below 1 (the portals' robots.txt Crawl-delay).

## Actor input object example

```json
{
  "markets": [
    "nyc",
    "cook_county"
  ],
  "dateFrom": "2026-06-01",
  "minSalePrice": 10000,
  "includeFlaggedSales": false,
  "sortBy": "newest",
  "maxItems": 200,
  "minRequestInterval": 1
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "markets": [
        "nyc",
        "cook_county"
    ],
    "dateFrom": "2026-06-01"
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/us-property-sales-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 = {
    "markets": [
        "nyc",
        "cook_county",
    ],
    "dateFrom": "2026-06-01",
}

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/us-property-sales-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 '{
  "markets": [
    "nyc",
    "cook_county"
  ],
  "dateFrom": "2026-06-01"
}' |
apify call scrapyx/us-property-sales-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/us-property-sales-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/R4WO1qy8I12ZgGtTl/builds/eOvZ4RyzudaBUmaH7/openapi.json
