# Property Records API - Owner, Sales, AVM, Tax Assessment (`nabeelbaghoor/property-records-data-api`) Actor

Look up US property records by address, parcel number or property id and read the full record: owner and mailing address, beds, baths, size, year built, lot, assessed and market value, tax, full sale history, AVM estimate with confidence, rental value and home equity. Bring your own key.

- **URL**: https://apify.com/nabeelbaghoor/property-records-data-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $14.00 / 1,000 property record returneds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Property Records API - Owner, Sales, AVM, Tax Assessment

Turn a US address into the whole property record: who owns it, what it is made of, what the assessor says it is worth, what it last sold for, and what an automated valuation model thinks it is worth today.

This actor calls a property records API on your behalf and stores one row per property, historical event, school or neighbourhood. Bring your own key: paste your account key into the input and the run uses your own plan and your own contract.

### What you get

- **Twenty-four modes, one question each.** Basic and expanded profiles, owner of record, current mortgage, AVM, rental AVM, home equity, last sale, full sale history, assessment, assessment history, building permits, all events on one record, five kinds of area search, pre-foreclosure, sales trends, nearby schools and neighbourhood statistics.
- **Three ways to name a property.** A full address, the provider's own numeric property id, or an assessor parcel number paired with its county code. An identifier that names none of those is refused with an explanation rather than sent as an address and quietly missing.
- **Ownership.** Owner of record, a second owner where there is one, whether the owner is a company, and the tax mailing address, which is what separates an owner occupier from an absentee landlord.
- **Characteristics.** Property type, class and subtype, year built, bedrooms, bathrooms, total rooms, living area, gross building area, stories, construction type, lot acreage and square footage, and zoning.
- **Money three ways.** The assessor's assessed, market and appraised values with the tax amount and year; the last recorded sale amount, date, transaction type and price per square foot; and the automated valuation with its high, low, confidence score and forecast standard deviation.
- **History as rows, not as a blob.** Sale history, assessment history and building permits each come back one row per event, so they sort and filter like any other table.
- **Area search with real filters.** Postcode, geography id or a radius around a coordinate, narrowed by property type, bedrooms, bathrooms, year built, lot size, building size, sale amount, estimated value, assessor market value and sale date range, sorted by the provider's own sort fields.
- **Place context.** Schools near a coordinate with grade range, district, distance and enrolment; neighbourhood population, households, median age and income, owner occupancy, crime index and average commute; and median and average sale prices over time.

### Pay per result

You are charged per record, never per request, and only for rows that carry real data. An address the provider has no record for is stored with the provider's own wording and costs nothing.

| Event | Price | What it covers |
| --- | --- | --- |
| Property record returned | $0.014 | One property with owner, characteristics, assessment, sale and, where the mode asks for it, valuation, rent or equity |
| Historical event returned | $0.005 | One recorded sale, assessment year or building permit |
| Place record returned | $0.006 | One school, neighbourhood or sales trend period |

### Quick start

1. Get an API key from your provider account.
2. Paste it into **API key**.
3. Leave **What this run does** on basic profile and put one address in **Properties**.
4. Set **Maximum rows** to the most you want to pay for.
5. Run it.

### FAQ

#### What is a property records API?

A property records API returns what public records hold about a piece of real estate: the assessor's record of who owns it and what it is worth, the recorder's record of what it last sold for, permits filed against it, and a model's estimate of its current market value. This actor reads a nationwide US property database assembled from county assessor and recorder filings and returns it as flat rows.

#### How do I identify a property?

Three ways, and you can mix them in one run. A full address with a comma before the city, such as `4529 Winona Court, Denver, CO 80212`. The provider's own numeric property id, which is the most exact match and comes back on every property row so you can store it. Or an assessor parcel number and its five digit county FIPS code joined with a bar, such as `2353-23-001|08031`. A value that is all digits is read as a property id, so a numeric parcel number needs its county code alongside it.

#### What is an AVM and how accurate is it?

An automated valuation model estimates a property's current market value from comparable sales, assessment records and property characteristics. Every AVM row here carries not just the value but the high and low of its range, a confidence score, and the forecast standard deviation, which is the model's own statement of how far off it expects to be. Use those rather than the single number when the decision matters.

#### Can I get the full sale history rather than just the last sale?

Yes. Sale history mode returns one row per recorded sale, each with its own date, amount, transaction type and, where the record carries them, the buyer and seller. Assessment history does the same for tax years, and building permits for filed permits.

#### Can I search a whole postcode or neighbourhood?

Yes. The search modes take a postcode, one of the provider's geography ids, or a coordinate and a radius, and narrow the result with filters on property type, bedrooms, bathrooms, year built, lot size, building size, sale amount, estimated value, assessor market value and sale date. The provider caps a single search at 10000 results however it is paged, so use tighter filters or smaller areas for a large sweep.

#### How do I find distressed or off-market properties?

Pre-foreclosure mode returns properties in an area with a recorded pre-foreclosure filing. Combining a sale search with an old sale date range and an owner mailing address that differs from the property address is the usual way to build an absentee owner list.

#### What happens when the provider has no record for an address?

It answers with HTTP 200 and its own status message rather than a not-found status, which is a trap worth knowing about: a client that read only the status line would report an empty result and charge nothing while explaining nothing. This actor reads the provider's status envelope on every response, stores the miss with the provider's own wording in the `reason` field, and does not charge for it.

#### What happens if my key is missing or rejected?

The run ends cleanly with the reason as its status message rather than failing with a stack trace. A missing key, an empty input, a key the provider will not accept and an exhausted allowance are all treated as answers, not faults.

#### Is my API key stored anywhere?

No. It is read from the run input or from the `DATA_API_KEY` environment secret, sent as the provider's own request header, and never written to the dataset or the log.

#### Which parts need which licence?

The provider licenses its property, valuation, transaction and neighbourhood packages separately. A mode your plan does not cover comes back as a clear message naming the plan rather than as an empty result, so you find out immediately rather than after a full run.

### Example output

```json
{
  "recordType": "expandedProfile",
  "requested": "4529 Winona Court, Denver, CO 80212",
  "found": true,
  "propertyId": 145423726,
  "parcelNumber": "2353-23-001",
  "fipsCode": "08031",
  "address": "4529 Winona Court, Denver, CO 80212",
  "city": "Denver",
  "state": "CO",
  "postalCode": "80212",
  "latitude": 39.771,
  "longitude": -105.0436,
  "propertyType": "SFR",
  "propertyClass": "Single Family Residence / Townhouse",
  "yearBuilt": 1953,
  "occupancy": "OWNER OCCUPIED",
  "bedrooms": 3,
  "bathrooms": 2,
  "totalRooms": 6,
  "livingAreaSqFt": 1832,
  "lotAcres": 0.17,
  "lotSqFt": 7405,
  "zoningType": "Residential",
  "assessedValue": 41250,
  "marketValue": 576000,
  "taxAmount": 3184.22,
  "taxYear": 2025,
  "lastSaleAmount": 545000,
  "lastSaleDate": "2021-06-18",
  "avmValue": 612300,
  "avmHigh": 660000,
  "avmLow": 564600,
  "avmConfidenceScore": 88,
  "lastUpdated": "2026-04-30"
}
```

### Keyword map

property records API, property data API, real estate data API, AVM API, automated valuation model API, home value estimate API, property owner lookup, owner of record API, assessor data API, tax assessment API, property tax API, sale history API, deed and transaction data, comparable sales API, rental estimate API, home equity API, loan to value API, pre-foreclosure data, building permit data API, property search by postcode, radius property search, school data by address, neighbourhood demographics API, skip tracing property data, absentee owner list, real estate lead generation data

# Actor input Schema

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

A run answers one question. The profile, owner, mortgage, valuation, equity and event modes read properties you already have an identifier for. The search modes return everything in a postcode, geography or radius that matches the filters. The history modes return one row per recorded sale, assessment or permit. Schools and neighbourhood describe the place rather than the property. Fields belonging to another mode are ignored rather than causing an error.

## `apiKey` (type: `string`):

Your own account's API key, from your provider account. Bring your own key: nothing is shared between runs and the key is never written to the dataset or the log.

## `properties` (type: `array`):

The properties to read, one per line, in any of three forms. A full address with a comma before the city, such as 4529 Winona Court, Denver, CO 80212. The provider's own numeric property id. An assessor parcel number and its five digit county code joined with a bar, such as 2353-23-001|08031. An all digit value is read as a property id, so pair a numeric parcel number with its county code rather than sending it alone.

## `postalCode` (type: `string`):

Search every property in this postcode. Used by the search, sale search, assessment search, AVM search and pre-foreclosure modes. For example 80212.

## `geoIdV4` (type: `string`):

Search inside one of the provider's geographies: a census block or tract, a neighbourhood, a school district, a flood zone or a place. This is also how the neighbourhood and sales trend modes name the area they describe. For example 9df3a2df15d1ba7de0f4f1c4dfa42ba5.

## `coordinate` (type: `string`):

A latitude and longitude pair written latitude first, for example 39.7710, -105.0436. Used by radius and schools modes, and usable by the search modes with the radius below.

## `radiusMiles` (type: `number`):

How far around the coordinate to search, in miles, up to 50. Defaults to one mile when a coordinate is set without it.

## `propertyType` (type: `string`):

Keep only properties of this type, written the way the provider classifies them, for example SFR for a single family residence, CONDOMINIUM, APARTMENT or COMMERCIAL (GENERAL).

## `minBeds` (type: `integer`):

Keep only properties with at least this many bedrooms.

## `maxBeds` (type: `integer`):

Keep only properties with at most this many bedrooms.

## `minBaths` (type: `number`):

Keep only properties with at least this many bathrooms in total, counting half baths as a half.

## `maxBaths` (type: `number`):

Keep only properties with at most this many bathrooms in total.

## `minYearBuilt` (type: `integer`):

Keep only properties built in this year or later.

## `maxYearBuilt` (type: `integer`):

Keep only properties built in this year or earlier.

## `minLotAcres` (type: `number`):

Keep only properties on a lot of at least this many acres.

## `maxLotAcres` (type: `number`):

Keep only properties on a lot of at most this many acres.

## `minBuildingSize` (type: `integer`):

Keep only properties with at least this much floor space, measured on the provider's comparable size basis.

## `maxBuildingSize` (type: `integer`):

Keep only properties with at most this much floor space.

## `minSaleAmount` (type: `integer`):

Keep only properties whose recorded sale price is at least this much, in dollars.

## `maxSaleAmount` (type: `integer`):

Keep only properties whose recorded sale price is at most this much, in dollars.

## `minAvmValue` (type: `integer`):

Keep only properties the automated valuation model puts at or above this figure, in dollars.

## `maxAvmValue` (type: `integer`):

Keep only properties the automated valuation model puts at or below this figure, in dollars.

## `minMarketValue` (type: `integer`):

Keep only properties whose total market value on the assessment roll is at least this much. This is the assessor's figure rather than the model's.

## `maxMarketValue` (type: `integer`):

Keep only properties whose total market value on the assessment roll is at most this much.

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

Keep only sales recorded on or after this date, written as YYYY/MM/DD, which is the format the provider uses. For example 2024/01/01.

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

Keep only sales recorded on or before this date, written as YYYY/MM/DD. For example 2024/12/31.

## `orderBy` (type: `string`):

How to order a search, using the provider's own sort fields. Add a direction after the field, for example saleamt+desc, avmvalue+desc, beds+asc, calendardate+desc or distance+asc when searching a radius.

## `pageSize` (type: `integer`):

How many records to ask for per request. A larger page means fewer round trips; the provider caps a single search at 10000 results however it is paged.

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

The most rows this run may store, and so the most it can charge for. A postcode search can match tens of thousands of properties, so this is the cap that keeps a run predictable.

## `requestsPerMinute` (type: `integer`):

How fast this run calls the provider. Lower it if your plan is rate limited more tightly than the default.

## `baseUrl` (type: `string`):

Overrides the API host. Only needed if the provider has given your account a different endpoint.

## Actor input object example

```json
{
  "mode": "basicProfile",
  "properties": [
    "4529 Winona Court, Denver, CO 80212"
  ],
  "pageSize": 100,
  "maxResults": 100,
  "requestsPerMinute": 60
}
```

# Actor output Schema

## `records` (type: `string`):

One row per record, alongside the address, identifier or search that produced it.

# 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 = {
    "properties": [
        "4529 Winona Court, Denver, CO 80212"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/property-records-data-api").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 = { "properties": ["4529 Winona Court, Denver, CO 80212"] }

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/property-records-data-api").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 '{
  "properties": [
    "4529 Winona Court, Denver, CO 80212"
  ]
}' |
apify call nabeelbaghoor/property-records-data-api --silent --output-dataset

```

## MCP server setup

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

```

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/agdoscBhei5zePgUH/builds/k9iBZXjeefHVBnUy6/openapi.json
