# NYC Property Sales Scraper (`muhammadafzal/nyc-property-sales-scraper`) Actor

Extract official NYC property sales by borough, neighborhood, address, ZIP code, sale date, price, building class, units, and square footage.

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

## Pricing

from $1.60 / 1,000 nyc 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.
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

## NYC Property Sales Scraper

**Free plan limit:** Free-plan users can receive up to **5 result records per run**. Paid-plan and local/unknown runs retain the Actor's existing limits.

Extract recent New York City property sales from the official NYC Department of Finance rolling sales dataset. Filter by borough, neighborhood, address, ZIP code, building class, sale date, and price; receive normalized records ready for real-estate research, comparable-sales analysis, lead generation, and market monitoring.

The Actor reads the public **NYC Citywide Rolling Calendar Sales** dataset (`usep-8jbt`) directly from NYC Open Data. It uses a low-cost HTTPS data path, requires no proxy or login, paginates deterministically, and never fabricates fallback records.

### What it extracts

| Field | Description |
|---|---|
| `saleId` | Deterministic identifier used to deduplicate a sale |
| `borough`, `boroughCode` | Human-readable borough and NYC DOF borough code |
| `neighborhood`, `address`, `apartmentNumber`, `zipCode` | Property location fields reported by NYC DOF |
| `salePrice`, `currency`, `saleDate` | Recorded price in USD and sale date |
| `buildingClassCategory` | DOF building category, such as elevator co-op or one-family home |
| `block`, `lot`, `easement` | Borough tax-lot identifiers |
| `residentialUnits`, `commercialUnits`, `totalUnits` | Reported unit counts |
| `landSquareFeet`, `grossSquareFeet`, `yearBuilt` | Property size and construction year when available |
| tax and building-class fields | Present and sale-time classification values |
| `sourceUrl`, `fetchedAt` | Official dataset URL and extraction timestamp |

Missing source values are returned as `null`, not guessed. Prices of `$0` are excluded by default because the rolling file can contain non-market transfers; enable `includeZeroDollarSales` when those records matter to your analysis.

### When to use it

Use this Actor for:

- recent comparable-sales research;
- borough, neighborhood, or ZIP-level market analysis;
- monitoring recorded sales by price band or building category;
- building structured real-estate datasets for spreadsheets, BI tools, and AI agents;
- finding public transaction records for outreach or further due diligence.

Do not use it for active property listings, owner contact details, live assessments, deeds/documents, geocoded radius searches, or a complete historical archive. The NYC rolling dataset covers approximately the prior 12 months and is updated monthly. A `$0` record is not evidence of a market sale.

### Input

| Input | Type | Default | Purpose |
|---|---:|---:|---|
| `borough` | string | `manhattan` | One borough or `all` |
| `neighborhood` | string | — | Case-insensitive partial DOF neighborhood match |
| `addressContains` | string | — | Case-insensitive partial address match |
| `zipCode` | string | — | Exact five-digit ZIP code |
| `buildingClassCategory` | string | — | Partial category match, such as `CONDO` |
| `saleDateFrom`, `saleDateTo` | date strings | — | Inclusive `YYYY-MM-DD` range |
| `minSalePrice`, `maxSalePrice` | numbers | — | Inclusive USD price range |
| `includeZeroDollarSales` | boolean | `false` | Include transfers reported at `$0` |
| `sortBy` | string | `newest` | `newest`, `oldest`, `price-high`, or `price-low` |
| `maxResults` | integer | `100` | Deliver 1–10,000 records |
| `maxRequestRetries` | integer | `2` | Retry transient network, 429, and 5xx failures 0–5 times |

Example: recent Brooklyn condo sales from `$500,000` to `$2,000,000`:

```json
{
  "borough": "brooklyn",
  "buildingClassCategory": "CONDO",
  "minSalePrice": 500000,
  "maxSalePrice": 2000000,
  "sortBy": "newest",
  "maxResults": 250
}
```

Run through the Apify API:

```bash
curl -X POST "https://api.apify.com/v2/acts/YOUR_USERNAME~nyc-property-sales-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"borough":"queens","zipCode":"11101","maxResults":50}'
```

Keep API tokens in an authorization header or secret manager in production; do not commit them to source control.

### Output example

Each default-dataset item has a stable, schema-validated shape:

```json
{
  "saleId": "1-605-1-2026-07-31-7192000-100-barrow-street-3b",
  "borough": "Manhattan",
  "boroughCode": 1,
  "neighborhood": "GREENWICH VILLAGE-WEST",
  "buildingClassCategory": "10 COOPS - ELEVATOR APARTMENTS",
  "taxClassAtPresent": "2",
  "block": "605",
  "lot": 1,
  "easement": null,
  "buildingClassAtPresent": "D4",
  "address": "100 BARROW STREET, 3B",
  "apartmentNumber": null,
  "zipCode": "10014",
  "residentialUnits": null,
  "commercialUnits": null,
  "totalUnits": null,
  "landSquareFeet": null,
  "grossSquareFeet": null,
  "yearBuilt": 2015,
  "taxClassAtTimeOfSale": 2,
  "buildingClassAtTimeOfSale": "D4",
  "salePrice": 7192000,
  "currency": "USD",
  "saleDate": "2026-07-31",
  "sourceDataset": "NYC Citywide Rolling Calendar Sales",
  "sourceUrl": "https://data.cityofnewyork.us/d/usep-8jbt",
  "fetchedAt": "2026-08-31T12:00:00.000Z"
}
```

The `SUMMARY` key-value-store record reports the outcome (`data`, `partial`, `empty`, `rejected`, or `failed`), matched and delivered counts, request count, warnings, charge-budget state, and estimated result-event cost. This keeps diagnostics out of the homogeneous sales dataset.

### Pricing

This Actor uses **pay-per-event** pricing. These are the current Apify Store event prices:

| Event | Price (USD) | When it is charged |
|---|---:|---|
| `apify-actor-start` | $0.00005 | Actor Start — Charged when the Actor starts running. Number of events charged depends on Actor memory (one event per GB, minimum one event). — Charged once per run. |
| `apify-default-dataset-item` | $0.0005 | NYC property sale record — One normalized NYC Department of Finance property-sale record written to the default dataset. |

Apify platform usage for this Actor run is included alongside the event prices above; no separate per-run platform-usage fee is passed to users.

This covers Apify platform usage for this Actor run. Other Apify products or usage outside this Actor run may still follow your account plan.

### Reliability and limitations

- Data comes directly from the official public NYC Open Data endpoint.
- Filters execute server-side using generated, escaped SoQL; arbitrary user-supplied SoQL is not accepted.
- Results are ordered deterministically and fetched in bounded pages.
- Transient `429`, `5xx`, timeout, and network errors use bounded exponential backoff.
- Invalid cross-field ranges return an actionable rejected summary before result work.
- A valid query with no matches returns an empty dataset and an explicit `SUMMARY` record.
- Malformed source rows are skipped with warnings rather than replaced with invented values.
- The source is a rolling, monthly dataset, not a complete historical sales archive. NYC may revise records and field coverage.
- Property descriptions, sizes, unit counts, and classifications reflect NYC DOF source data and can be blank or administrative rather than market-ready.

### Responsible use

The source records are public government data. Follow NYC Open Data terms and applicable laws. Verify records against authoritative documents before making legal, lending, valuation, investment, or outreach decisions. Do not use the Actor to discriminate, harass, or make high-impact decisions without appropriate review.

For support, include the Apify run ID, redacted input, expected result, and the relevant `SUMMARY` record. Never include your API token.

# Actor input Schema

## `borough` (type: `string`):

Use this to select one NYC borough or all boroughs. Defaults to Manhattan; this is not a neighborhood filter.

## `neighborhood` (type: `string`):

Use this for a case-insensitive partial NYC DOF neighborhood match, for example `UPPER WEST SIDE`. Leave empty for every neighborhood.

## `addressContains` (type: `string`):

Use this for a case-insensitive partial street-address match, for example `PARK AVENUE`. This is not a geocoded radius search.

## `zipCode` (type: `string`):

Use this for one exact five-digit NYC ZIP code, for example `11201`. Leave empty for every ZIP code.

## `buildingClassCategory` (type: `string`):

Use this for a partial DOF category match, for example `CONDO` or `ONE FAMILY`. This is not the two-character building-class code.

## `saleDateFrom` (type: `string`):

Use this lower sale-date bound in YYYY-MM-DD format, for example `2026-01-01`. The rolling dataset covers roughly the prior 12 months.

## `saleDateTo` (type: `string`):

Use this upper sale-date bound in YYYY-MM-DD format, for example `2026-06-30`. It must not precede the lower bound.

## `minSalePrice` (type: `number`):

Use this inclusive minimum sale price in US dollars, for example `500000`. Leave empty to use the zero-dollar-sale setting.

## `maxSalePrice` (type: `number`):

Use this inclusive maximum sale price in US dollars, for example `2000000`. It must not be below the minimum.

## `includeZeroDollarSales` (type: `boolean`):

Use this to include records reported at $0, which often represent non-market transfers. Defaults to false; a minimum price overrides this behavior.

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

Use this to order matching records by sale date or price. Defaults to newest sales first.

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

Use this to cap delivered records and PPE result events. Default is 100; accepted range is 1–10,000.

## `maxRequestRetries` (type: `integer`):

Use this to retry NYC Open Data 429, 5xx, timeout, and network failures. Default is 2; accepted range is 0–5.

## Actor input object example

```json
{
  "borough": "manhattan",
  "includeZeroDollarSales": false,
  "sortBy": "newest",
  "maxResults": 100,
  "maxRequestRetries": 2
}
```

# Actor output Schema

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

Schema-validated public property-sale records from NYC Open Data.

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

Outcome, delivered and matched counts, requests, warnings, budget state, and estimated result-event cost.

# 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 = {
    "borough": "manhattan"
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/nyc-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 = { "borough": "manhattan" }

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/nyc-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 '{
  "borough": "manhattan"
}' |
apify call muhammadafzal/nyc-property-sales-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,muhammadafzal/nyc-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/4gkA47mfHH8MXml0I/builds/iTbTvcYNpmwuXXqb9/openapi.json
