# Property Sales Records - US Home Sale Prices & Comps (`offgrid-labs/property-sales-records`) Actor

Property sales records with recorded sale prices and comps from official public records: NYC (5 boroughs), Cook County IL, Maryland (24 counties), Connecticut (169 towns), Norfolk VA. Sale date, price, address, ZIP, parcel ID, sq ft, year built, arm's-length flag. $2.00 per 1,000 sales.

- **URL**: https://apify.com/offgrid-labs/property-sales-records.md
- **Developed by:** [OFFGRID LABS](https://apify.com/offgrid-labs) (community)
- **Categories:** Real estate, Business, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 property sale records

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

### Property sales records: US home sale prices and comps from official county and state data

**TL;DR:** Property Sales Records returns recorded property sales from the official open-data APIs of **New York City (5 boroughs), Cook County IL (Chicago), Maryland (all 24 counties), Connecticut (all 169 towns) and Norfolk VA** in one unified format: sale date, sale price, address, ZIP, parcel ID, property type and class, year built, building and land area, price per sq ft, assessed value, sales ratio, deed type and an arm's-length flag. It is built for real-estate investors, appraisers, agents, proptech teams, lenders and AI agents that need sold-price comps from the public record rather than listing sites. **No buyer, seller or owner names are ever returned.** You pay **$2.00 per 1,000 sales** saved.

All data comes from official government Socrata open-data APIs. No browser, no login, no proxy, no Zillow scraping.

### Sources

| Source ID | Coverage | Official dataset | Update cadence |
|---|---|---|---|
| `nyc` | New York City, all 5 boroughs | NYC Dept. of Finance Rolling Calendar Sales (data.cityofnewyork.us, `usep-8jbt`) | Monthly, rolling 12 months |
| `cook-county` | Cook County, IL (Chicago and suburbs) | Cook County Assessor Parcel Sales (`wvhk-k5uv`) + parcel addresses (`3723-97qp`) | Weekly, 1-3 month recording lag |
| `maryland` | Maryland, all 24 counties | SDAT Real Property Assessments (opendata.maryland.gov, `ed4q-f8tm`), latest sale per parcel | Weekly |
| `connecticut` | Connecticut, all 169 towns | OPM Real Estate Sales 2001-2024 (data.ct.gov, `5mzw-sjtu`), sales of $2,000+ | Annually (by Oct 1 list year) |
| `norfolk-va` | Norfolk, VA | Property Assessment and Sales (data.norfolk.gov, `qva7-tzrf`) | Current fiscal year |

### What data you get

One dataset row per recorded sale, with the same fields for every source. Values a source does not publish are `null`, never guessed.

| Field | What it is | Example |
|---|---|---|
| `source`, `sourceName`, `state` | Source ID, coverage, state | `maryland`, `Maryland (all 24 counties)`, `MD` |
| `county`, `city`, `neighborhood` | Location as published | `Baltimore`, `GWYNN OAK` |
| `saleDate` | Recorded transfer date (YYYY-MM-DD) | `2026-11-03` |
| `salePrice` | Sale price / consideration, USD | `386000` |
| `pricePerSqft` | `salePrice / buildingAreaSqft` | `337.41` |
| `address`, `unit`, `zip` | Property address (never a mailing address) | `1303 DORCHESTER AVE`, `21207` |
| `parcelId` | Parcel / PIN / BBL / account number | `04010122450050` |
| `propertyType` | Normalised: Residential, Condo / co-op, Multi-family, Commercial / industrial, Vacant land, Exempt / institutional, Other | `Residential` |
| `propertyClass` | Class as published by the source | `Residential (R)` |
| `yearBuilt`, `buildingAreaSqft`, `landAreaSqft`, `units` | Building facts where published | `1942`, `1144` |
| `assessedValue`, `salesRatio` | Assessed value and assessed / sale ratio | `214100`, `0.5547` |
| `deedType`, `documentId` | Deed / conveyance type and recording number | `Private arms-length transfer, Improved (1)` |
| `isArmsLength` | `true` / `false` when the source flags it, `null` if unknown | `true` |
| `latitude`, `longitude` | Coordinates (Maryland, Connecticut) | `39.298072`, `-76.727057` |
| `recordUrl`, `datasetUrl`, `sourceUrl` | Official record page, dataset, API query | `https://opendata.maryland.gov/d/ed4q-f8tm` |
| `raw` | Every non-personal column the source publishes (optional) | |

### Use cases

- **Real-estate investors and wholesalers**: pull every sale in a ZIP or neighbourhood for comps and price-per-sq-ft trends.
- **Appraisers and agents**: arm's-length comparable sales with parcel IDs and links to the official record.
- **Proptech, lenders and analysts**: one schema across NYC, Chicago, Maryland, Connecticut and Norfolk for AVMs, dashboards and sales-ratio studies.
- **Tax-appeal consultants**: compare assessed values with recent sale prices (`salesRatio`).

### How to get property sales records

1. Pick **Sources** (or leave empty for all 5).
2. Set **Sold within last N days** (default 90) or a fixed **Sold on or after** / **Sold on or before** date (YYYY-MM-DD).
3. Optional filters: **ZIP codes** or prefixes (`100`, `21207`), **City / county / neighborhood contains** (`Brooklyn`, `Chicago`, `Hartford`), **Property types**, **Minimum / Maximum sale price (USD)**, **Arm's-length sales only**.
4. Set **Max sales per source** (default 200; 0 = no limit) and click **Start**. For a monthly comps feed, schedule a run with **Sold within last N days** = 31.

### Input example

```json
{
  "sources": ["nyc", "cook-county"],
  "soldWithinDays": 90,
  "zipCodes": ["112", "606"],
  "propertyTypes": ["Residential", "Condo / co-op"],
  "minPrice": 100000,
  "armsLengthOnly": true,
  "maxItemsPerSource": 500
}
```

### Output example

```json
{
  "source": "maryland",
  "sourceName": "Maryland (all 24 counties)",
  "state": "MD",
  "county": "Baltimore",
  "city": "GWYNN OAK",
  "neighborhood": null,
  "saleDate": "2026-11-03",
  "salePrice": 386000,
  "pricePerSqft": 337.41,
  "address": "1303 DORCHESTER AVE",
  "unit": null,
  "zip": "21207",
  "parcelId": "04010122450050",
  "propertyType": "Residential",
  "propertyClass": "Residential (R)",
  "yearBuilt": 1942,
  "buildingAreaSqft": 1144,
  "landAreaSqft": null,
  "units": 1,
  "assessedValue": 214100,
  "salesRatio": 0.5547,
  "deedType": "Private arms-length transfer, Improved (1)",
  "documentId": "000000",
  "isArmsLength": true,
  "latitude": 39.298072,
  "longitude": -76.727057,
  "recordUrl": "https://sdat.dat.maryland.gov/RealProperty/Pages/viewdetails.aspx?County=04&SearchType=ACCT&District=01&AccountNumber=0122450050",
  "datasetUrl": "https://opendata.maryland.gov/d/ed4q-f8tm",
  "sourceUrl": "https://opendata.maryland.gov/resource/ed4q-f8tm.json?...",
  "scrapedAt": "2026-11-03T14:41:21.301Z"
}
```

A per-source summary (saved, scanned, requests, errors) is stored in the key-value store record `RUN_SUMMARY`.

### How much does it cost to get property sales data?

Pay per event: filters run before saving, so you only pay for sales you keep. Compute is included. The price is the same on every Apify plan.

| Event | Charged when | Price |
|---|---|---|
| `sale` | One property sale saved | **$2.00 per 1,000** ($0.002 each) |
| `apify-actor-start` | Once per run (Apify's standard start fee) | $0.00005 |

**Worked examples**

- **1,000 sales** = 1,000 × $0.002 = **$2.00** (+ $0.00005 start fee).
- Comps for one Brooklyn ZIP over 12 months (about 400 sales): **$0.80**.
- A monthly feed of all arm's-length Maryland sales over $100,000 (about 5,000 a month): **$10.00 per month**.

Set **Maximum cost per run** in the run options; the actor stops cleanly at the limit and never charges twice for the same sale.

### How it compares (by approach)

| | This actor (official open data) | Listing-site scrapers (Zillow, Redfin, Realtor) | County-records lookup actors | Commercial property-data APIs |
|---|---|---|---|---|
| Source | Recorder / assessor / finance-department datasets | Listing portals | County assessor sites, one parcel at a time | Aggregated public records |
| Sale price | Recorded consideration | Listed / reported sold price | Varies | Recorded |
| Coverage | 5 jurisdictions, every recorded sale | Nationwide, listed homes only | Per lookup | Nationwide |
| Non-arm's-length flag, deed type, parcel ID | Yes, where published | Rarely | Sometimes | Yes |
| Personal names | Never returned | Agent names | Often owner names | Owner names |
| Blocking risk | None (public APIs) | High (anti-bot, proxies) | Medium | None |
| Cost | $2.00 per 1,000 sales | about $0.90-$3.80 per 1,000 | about $45-$50 per 1,000 records | Subscription, often $100s per month |

### FAQ

#### Where can I find recent home sale prices for comps?

Run this actor with **Sold within last N days** and a **ZIP code** or **Locality** filter. Each row has the recorded sale price, date, address, parcel ID and, where published, building area and price per sq ft.

#### Which areas are covered?

New York City (all 5 boroughs), Cook County IL (Chicago), all 24 Maryland counties, all 169 Connecticut towns and Norfolk VA. More jurisdictions that publish sales on Socrata can be added on request.

#### Does it include buyer, seller or owner names?

No. Buyer, seller, grantor, grantee and owner columns are never output, not even in the optional raw record. The data describes the property and the transaction only.

#### How fresh is the data?

As fresh as each source: Maryland weekly, Cook County weekly with a 1-3 month recording lag, NYC monthly (rolling 12 months), Norfolk for the current fiscal year, and Connecticut annually. If a recent window returns nothing for Connecticut, widen **Sold within last N days** to 400+.

#### How do I keep only real market sales?

Turn on **Arm's-length sales only**. It drops sales the source flags as non-arm's-length (Cook County sale filters, Maryland conveyance codes, Connecticut non-use codes, NYC nominal transfers of $10,000 or less). Sales with no flag (`null`) are kept. A **Minimum sale price** (default $1,000) also removes nominal transfers.

#### Why are some fields empty?

Sources publish different columns: for example NYC does not publish assessed values, Cook County sales carry no building area, and Connecticut has no ZIP codes. Missing values are `null`, never estimated.

#### Can I filter by ZIP code, property type or price?

Yes. **ZIP codes** accepts full ZIPs or prefixes, **Property types** uses the normalised types listed above, and **Minimum / Maximum sale price** are applied server-side so you only pay for matching sales.

#### Is it legal to use this property sales data?

All data comes from official government open-data APIs published for public reuse under each portal's terms. The actor keeps to one request per second per portal and returns no personal names.

#### Can an AI agent use this property sales API?

Yes. Through the Apify MCP server an agent can find it with `search-actors`, read the input schema and run it, for example "arm's-length condo sales in Brooklyn ZIP 11201 in the last 90 days". See **Integrations**.

#### Something wrong?

Open an issue on the Issues tab; we aim to respond within 24 hours.

### Integrations

- **API**: `POST https://api.apify.com/v2/acts/offgrid-labs~property-sales-records/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>` with the input JSON as the body returns the sales directly (runs under 5 minutes; otherwise start a run and read its dataset).
- **Python / JavaScript**: `pip install apify-client` / `npm install apify-client`, then `client.actor("offgrid-labs/property-sales-records").call(run_input={...})`.
- **No-code**: Apify's integrations for **Make**, **Zapier** and **n8n**, plus Google Sheets, Slack, webhooks and Schedules (for example new sales in your ZIPs into a sheet every week).
- **MCP / AI agents**: add `https://mcp.apify.com?tools=offgrid-labs/property-sales-records` to your MCP client to expose this actor as a tool.

### Changelog

- **2026-11-03** · First release: NYC, Cook County (with parcel-address join), Maryland, Connecticut and Norfolk VA via official Socrata APIs; unified schema with price per sq ft, sales ratio and arm's-length flag; ZIP / locality / type / price filters; buyer, seller and owner names never output; `RUN_SUMMARY`.

### Related actors

| Actor | What it does |
|---|---|
| US Building Permits Scraper | New construction, renovation and demolition permits from 12 US cities |
| Local Business Leads Scraper | Contractors, agents and other local businesses by category and city |
| Company Registry Lookup (KYB) | Official company records for LLC buyers and developers |

***

```
// OFFGRID LABS ─────────────────────────────────────────────
// status: monitored 24/7 by a self-hosted agent swarm
// health checks: every 6h · auto-repair + rollback on failure
// sources: official public APIs and open data only
// support: open an issue — human-reviewed response within 24h
// ───────────────────────────────────────────────────────────
```

# Actor input Schema

## `sources` (type: `array`):

Which official open-data sources to query. Empty = all five: New York City, Cook County IL (Chicago), Maryland (statewide), Connecticut (statewide, published annually) and Norfolk VA. Example: \["nyc", "maryland"]

## `localities` (type: `array`):

Keep sales whose city, county or neighborhood contains one of these words (case-insensitive). Examples: \["Brooklyn"], \["Montgomery", "Baltimore City"], \["Greenwich"].

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

Keep sales in these ZIP codes; prefixes work ("112" = all of 112xx). Sales without a ZIP are excluded when set. Connecticut publishes no ZIP. Example: \["11211", "208"]

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

Sales recorded in the last N days (ignored when 'Sold on or after' is set). Sources publish with a lag: NYC monthly, Cook County weekly, Maryland monthly, Connecticut once a year. Default 90. Example: 30

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

Fixed start date instead of 'last N days'. Example: 2026-01-01

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

Optional end date. Example: 2026-06-30

## `minPrice` (type: `integer`):

Drop sales below this price. Default 1000 removes $0 / $10 deed transfers that are not real sales. 0 = keep everything. Example: 250000

## `maxPrice` (type: `integer`):

Drop sales above this price. 0 = no maximum. Example: 1500000

## `propertyTypes` (type: `array`):

Keep only these normalised property types. Empty = all. Example: \["Residential", "Condo / co-op"]

## `armsLengthOnly` (type: `boolean`):

Drop sales the source flags as non-arm's-length (foreclosures, gifts, related-party, multi-sale or non-market deeds) - use for valuation comps. Sources without such a flag are kept.

## `maxItemsPerSource` (type: `integer`):

Maximum sales saved per source, newest first; one sale = one dataset row = one charged result. 0 = no limit. Example: 1000

## `includeRawRecord` (type: `boolean`):

Adds a raw field with the source's original columns (buyer, seller, grantor, grantee and owner name columns are always removed). Default false.

## `socrataAppToken` (type: `string`):

Optional free app token from any Socrata portal (Developer Settings). Raises per-IP request limits for very large pulls. Not needed for normal use.

## Actor input object example

```json
{
  "sources": [
    "nyc",
    "maryland"
  ],
  "localities": [],
  "zipCodes": [],
  "soldWithinDays": 120,
  "soldAfter": "",
  "soldBefore": "",
  "minPrice": 1000,
  "maxPrice": 0,
  "propertyTypes": [],
  "armsLengthOnly": false,
  "maxItemsPerSource": 5,
  "includeRawRecord": false
}
```

# Actor output Schema

## `overview` (type: `string`):

Date, price, address, ZIP, property type, size, price per sq ft, year built.

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

Every field of every sale.

## `runSummary` (type: `string`):

RUN_SUMMARY record: saved/scanned per source, date window, seconds, charged events.

# 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 = {
    "sources": [
        "nyc",
        "maryland"
    ],
    "soldWithinDays": 120,
    "maxItemsPerSource": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("offgrid-labs/property-sales-records").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 = {
    "sources": [
        "nyc",
        "maryland",
    ],
    "soldWithinDays": 120,
    "maxItemsPerSource": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("offgrid-labs/property-sales-records").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 '{
  "sources": [
    "nyc",
    "maryland"
  ],
  "soldWithinDays": 120,
  "maxItemsPerSource": 5
}' |
apify call offgrid-labs/property-sales-records --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,offgrid-labs/property-sales-records"
        }
    }
}
```

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/dBGWXLO631jqmnkul/builds/VqSgM5zA8gWn04S0i/openapi.json
