# NYC Property Profile - Owner, Value & Violations (`j0401/nyc-property-profile`) Actor

One NYC property by BBL, joined across nine city datasets on the exact key: PLUTO owner and land use, DOF valuation, HPD violations and complaints, DOB permits, OATH hearings, ACRIS documents, tax abatements and tax-lien notices. Profile returns a summary; events returns the records.

- **URL**: https://apify.com/j0401/nyc-property-profile.md
- **Developed by:** [Wenhao Yang](https://apify.com/j0401) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 nyc property profiles

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 Profile

One New York City property, one call. Give a 10-digit BBL and get back the owner and land-use record, the DOF valuation and assessment, the housing violations and complaints, the building permits and building-code violations, the ECB enforcement records, the administrative-hearing cases, the recorded legal documents, the tax abatements - current detail and history - and the tax-lien notices, joined on the exact key the city uses.

### Low cost

Property profiles: **$0.005 per profile, down to $0.003 at Gold.**

Underlying event records: **from $0.00005 per record, down to $0.00003 at Gold.**

Change monitoring (from 2026-11-15) is charged at $0.0001 per changed field.

### What you get

`mode=profile` (default) returns **one row per BBL**:

- **Identity** - `bbl`, `bin`, `borough`, `boroughName`, `block`, `lot`, `condoNumber`
- **PLUTO land use** - `address`, `zip`, `ownerName`, `ownerType`, `yearBuilt`, `unitsResidential`, `unitsTotal`, `numBuildings`, `numFloors`, `buildingClass`, `buildingArea`, `lotArea`, `zoning`, `plutoAssessedTotal`, `plutoExemptTotal`, `latitude`, `longitude`, `plutoMatched`
- **DOF valuation** - `valuationYear`, `valuationPeriod`, `marketValue`, `assessedValue`, `taxableValue`, `taxClass`, `valuationOwner`, `valuationBuildingClass`, `valuationYearBuilt`, `valuationUnits`, `valuationBuildings`, `valuationLandArea`, `valuationZoning`, `valuationAddress`, `valuationZip`
- **HPD** - `hpdViolationsTotal` / `hpdViolationsOpen` / `hpdViolationsLatest`, `hpdComplaintsTotal` / `hpdComplaintsOpen` / `hpdComplaintsLatest`
- **DOB** - `dobPermitsTotal` / `dobPermitsLatest`, `dobViolationsTotal` / `dobViolationsOpen` / `dobViolationsHazardous` / `dobViolationsEcbReferred` / `dobViolationsLatest`, `dobEcbTotal` / `dobEcbOpen` / `dobEcbBalanceDue` / `dobEcbPenalty` / `dobEcbLatest`
- **Hearings and legal** - `oathCasesTotal` / `oathCasesLatest`, `acrisDocumentsTotal`
- **Abatements** - `abatementsTotal` / `abatementAppliedTotal` / `abatementLatestYear` / `abatementPrograms` (current city snapshot) plus `abatementSnapshots` / `abatementFirstYear` / `abatementYears` / `abatementHistoryRecords` / `abatementHistoryApplied` / `abatementHistoryTruncated` (history across the snapshots the city publishes, de-duplicated)
- **Tax lien** - `taxLienRecords` / `taxLienLatestCycle`
- **Status** - `registersMissing` (a register that could not be queried, or answered but holds nothing for this lot), `found`, `complete`, `sourceErrors`, `asOf`

`mode=events` returns the underlying records, newest first, with `eventSource`, `eventDate`, `eventId`, `eventStatus`, `eventDescription`, `eventAmount` and `eventDetail` (the source-specific fields, kept as text). Event sources: HPD violations, HPD complaints, DOB permits, DOB building violations, ECB violations, OATH hearings and abatement applications.

`mode=changes` (from 2026-11-15) compares the profile with the baseline this monitor stored for the property and returns **one row per field that moved**: `changeField`, `oldValue`, `newValue`, `changedAt`. The first run stores the baseline and returns nothing to charge; later runs return the delta.

### Examples

The default property (BBL 2028820229 - 16 Richman Plaza, Bronx):

```json
{ "bbl": "2028820229" }
```

Any property (240 1 Avenue, Manhattan):

```json
{ "bbl": "1009720001", "mode": "profile" }
```

The underlying HPD violations, complaints, DOB permits and violations, ECB violations, OATH hearings and abatements for one property:

```json
{ "bbl": "2028820229", "mode": "events", "maxEvents": 50 }
```

Monitor what moved since the last baseline:

```json
{ "bbl": "1009720001", "mode": "changes" }
```

A portfolio: up to 12 properties in one run, one profile row each:

```json
{
  "bbls": ["2028820229", "1009720001", "4087860042", "4087980002"],
  "mode": "profile"
}
```

### Coverage and honest limits

- **Eleven city corpora, joined on the exact key** - PLUTO, DOF valuation, HPD violations, HPD complaints, DOB permits, DOB building violations, DOB ECB violations, OATH/ECB hearings, ACRIS legal documents, DOF abatement detail (current snapshot and full history) and the tax-lien sale list.
- 🔴 **Every corpus keys differently.** PLUTO keys on the 10-digit BBL as a number; HPD violations stores it as text; HPD complaints and DOB permits store it as a number; OATH uses a borough **name** plus zero-padded block/lot text (lots appear at both four and five digits, and both are matched); ACRIS and the tax-lien list use a numeric borough plus block/lot. The two DOB violation corpora key on the **7-digit BIN**, not the BBL: the BINs carried by the lot's HPD violations and DOB NOW permits are collected first, so a multi-building or condo lot joins its buildings. When no BIN is published the building-violations corpus is still reachable by borough plus zero-padded block/lot; ECB is BIN-only and is then reported in `registersMissing`.
- 🔴 **A profile is a summary of what the city publishes for that BBL, not a title search.** It does not replace a title report, a survey or a lien search. ACRIS counts are legal-document records for the block/lot; the tax-lien list is a notice list with no amount or owner.
- **The counts are record counts, not necessarily distinct events.** HPD complaints are published one row per **complaint problem**; DOB permits are one row per **work permit** (one job can hold several); abatements are one row per application/tax-year record; OATH is one row per ticket; DOB violations and ECB violations are one row per violation.
- **Abatement history is de-duplicated, not stacked.** The city appends a whole snapshot each cycle, so the same application appears once per snapshot in the raw table. History keeps each record's newest copy; `abatementHistoryApplied` is therefore a sum of distinct records, not a sum multiplied by the number of snapshots. `abatementSnapshots` reports how many snapshots the records came from. A lot with more than 2,000 raw snapshot rows reports `abatementHistoryTruncated=true` rather than claiming a complete history (measured worst case is 1,000).
- **A source that fails does not kill the profile.** Its error is reported in `sourceErrors` and `complete` becomes `false`; every other corpus is still delivered. If both anchor corpora (PLUTO and the DOF valuation roll) fail outright, the run fails instead of returning a half-empty property.
- **PLUTO lists tax lots, not every BBL.** Condo billing lots and special parcels can come back `plutoMatched=false`; the other corpora still answer.
- `ownerName` and `valuationOwner` are the **published owner of record** from city open data - not a mailing list and not a person-level profile.
- Each corpus refreshes on its own schedule; `asOf` is the date the profile was assembled, and the per-corpus dates in the summary fields are the city's own.
- **One BBL or a batch of up to 12.** `bbls` returns one profile row per BBL in a single run (each metered as one profile); larger lists need several runs or the dedicated bulk actors listed below. If the run's time budget cannot finish the batch, the run fails rather than delivering a short batch - split the list and re-run.
- **A BBL with no records still costs one profile charge** - the run still queries all eleven corpora. `found` is true when any property-level corpus (PLUTO, the valuation roll, HPD, DOB, ECB, OATH, abatements or the lien list) answers; ACRIS-only block/lot records do not make a property "found", so a block-level legal document alone will not.
- **`maxEvents` is a per-source cap**: `maxEvents=50` returns up to 50 records from each of the seven event corpora (at most 700 rows per run). A corpus with no records for this property contributes none.
- **Change monitoring is per caller.** The baseline is stored per API user (falling back to a shared baseline where no user identity is available), so two customers never share a diff window. A baseline is kept when you run `mode=profile`; `mode=changes` refreshes it after each diff. Only published fields are compared - there is no scoring, ranking or inference.
- For bulk event history, the dedicated **NYC HPD Violations**, **NYC DOB Permits** and **NYC Property Abatements** actors cost **from $0.00005 per row, $0.00003 at Gold** - the same ladder as this actor's event mode.

### Related

Also available in this account: **NYC Property Valuation & Assessment** (tax-lot rolls), **NYC HPD Violations** (housing-code violations by building), **NYC DOB Permits** (DOB NOW job filings) and **NYC Property Abatements** (full abatement detail).

# Actor input Schema

## `bbl` (type: `string`):

10-digit Borough-Block-Lot, first digit 1-5, e.g. "2028820229" (16 Richman Plaza, Bronx) or "1009720001" (240 1 Avenue, Manhattan). Ignored when bbls is set.

## `bbls` (type: `array`):

Portfolios: up to 12 BBLs in one run, one profile row each (mode=profile only). Each BBL is metered as one profile. The run fails loudly rather than delivering a partial batch if the time budget runs out.

## `mode` (type: `string`):

profile = one summary row for the property (default). events = the underlying HPD violations / complaints, DOB permits / violations, ECB violations, OATH hearings and abatements, newest first. changes = what moved since this monitor last stored a baseline for the property (one row per changed field; available from 2026-11-15).

## `maxEvents` (type: `integer`):

In events mode, the newest N records taken from EACH event corpus (HPD violations, HPD complaints, DOB permits, DOB violations, ECB violations, OATH hearings and abatements; 1 to 100 per source, so at most 700 rows per run). A corpus with no records for this property contributes none. Ignored in profile and changes mode.

## Actor input object example

```json
{
  "bbl": "2028820229",
  "bbls": [],
  "mode": "",
  "maxEvents": 100
}
```

# Actor output Schema

## `recordsUrl` (type: `string`):

NYC property profile - as JSON

## `datasetUrl` (type: `string`):

No description

## `runUrl` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("j0401/nyc-property-profile").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 = {}

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,j0401/nyc-property-profile"
        }
    }
}
```

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/x7Bd50Kfd3n5PAhGR/builds/eXZhV9qePFEdb6uJ1/openapi.json
