# Property Owner Lookup by Address: County Records (`firsthand/property-owner-lookup`) Actor

Property owner lookup by address from official state and county parcel records. Paste a list of US addresses and get the owner name, plus mailing address, value and last sale where the state publishes them. Covers MA, CT, NC, NY, AR and WV.

- **URL**: https://apify.com/firsthand/property-owner-lookup.md
- **Developed by:** [Firsthand](https://apify.com/firsthand) (community)
- **Categories:** Real estate, Lead generation
- **Security:** Limited permissions
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 owner 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 Owner Lookup by Address: County Records

Paste a list of US addresses and get the owner of each property from the official state or county parcel record: owner name, mailing address, assessed value, last sale, parcel number and a link to the source.

This Actor is a property owner search by address for Massachusetts, Connecticut, North Carolina, New York, Arkansas and West Virginia. It reads the parcel services published by those states, not a resold database.

### What you get

Each address returns one row:

- `ownerName`, `ownerName2`: owner as published
- `mailingAddress`, `mailingCity`, `mailingState`, `mailingZip`: where the tax bill goes
- `absenteeOwner`: true when the mailing address is not the property address: another house, or the same house number and street in another city or state
- `totalValue`, `landValue`, `buildingValue`: value as published
- `yearBuilt`, `lotSizeAcres`, `landUse`
- `lastSaleDate`, `lastSalePrice`
- `parcelId`, `recordId`, `propertyAddress`, `unit`, `propertyCity`, `propertyZip`, `county`; `recordId` is the assessor's record number in Massachusetts, where one parcel outline can carry several records
- `taxYear`, `sourceDate`, `sourceNote`: how fresh the source is and what it leaves out
- `sourceName`, `sourceUrl`: the official source of the row
- `status`, `reason`, `matchMethod`: what happened with the address

### Who owns this property: how to use it

1. Put your addresses into **Addresses**, one per line, with city and state: `165 Capitol Ave, Hartford, CT 06106`. Keep the direction (`N Main St`), the house number as written (`12 1/2`, `154A`) and the unit if the address has one. An address without a city or ZIP returns `ambiguous_address`.
2. Run the Actor.
3. Download the results as Excel, CSV or JSON.

Rows come back in the order the lookups finish, so match them by `inputAddress`. Every line you send gets a row, also when there is no record, when the line cannot be read, and when it repeats an earlier line. Two lines are the same address only if the house number with its letter, the street, direction, city, ZIP and unit all match.

An address is charged once, also when it returns several rows. Several rows come only when the records belong to one property and one owner (`matchedRecords` shows how many); the same record is charged once per run, so `Apt 1` and `Apt 2` of a building that has one record cost one record. If the records at an address have different owners, you get a free `ambiguous_address` with the units to add, or a note that the source does not tell those records apart. If you add the unit, you get that unit's record. When the source lists only the whole building, you get the building and `reason` says so. When the source lists other units but not yours, the row is `unit_not_found` and free. A house number 0 is not read.

### Parcel lookup by address: how matching works

1. The address goes to the free US Census geocoder. Its answer is used only if it agrees with your street, direction, city and ZIP.
2. The Actor reads the parcels around that point in the state service and keeps a record only if the house number, the street and the direction match. A record for the exact number comes before a parcel that covers a range of numbers.
3. If that fails, it asks the state service for the address directly, inside your city. When the record has a ZIP, it must be yours; when the geocoder found the address, the record must stand within about 1 km of it.
4. Several matching records must be one property: the same house number, street and town, within 200 m of each other. Otherwise the row is a free `ambiguous_address` that lists them, for example the same `597 Main St` in two villages of Barnstable, MA. Add the village or ZIP.

The Actor does not guess a nearby parcel. `12 N Main St` and `12 S Main St` are different houses, `12 Oak St` is not `12 Oak Ave`, `12 Main St` is not `12 Main St Ext` in any spelling (EXT, EXTN, EX), and `12`, `12 1/2`, `12A` and `12 Rear` are different records, also with a unit. A house number with a decimal point (`1.6`) is not read. When something does not line up, you get a free row with the reason instead of somebody else's name:

- `ambiguous_address`: the address fits several places, for example `101 Main St, Durham` is both East and West Main. Add the direction, city or ZIP.
- `address_mismatch`: the closest known address differs in street type, direction, house number, city or ZIP. The reason shows it, so you can correct the line.

When you leave out the direction, the geocoder's direction is used only if it finds a single such address; two or more give `ambiguous_address`.

Two exceptions. When only one side has a direction (`89 S Tunnel Rd` and `89 TUNNEL RD`), a record is accepted only if the parcel touches the geocoded point, within about 40 m. A parcel that covers a range (`35-37 Kingston St`) is accepted when no record for the exact number stands within 300 m; check `propertyAddress`.

### Coverage and match rate

| State | Records in the source | Owner | Mailing address | Value | Last sale |
|---|---|---|---|---|---|
| Massachusetts | 2.6 million | yes | yes | yes | most |
| Connecticut | 1.3 million | yes | yes, no ZIP | total and land, no building | most |
| North Carolina | 5.9 million | yes | yes | yes | date only, some counties |
| New York | 3.8 million, 38 of 62 counties | yes | not in New York City | yes | no |
| Arkansas | 2.1 million | yes | no | yes | no |
| West Virginia | 1.4 million | yes | yes | no | no |

Match rate, measured in November 2026 on 840 independent addresses from OpenStreetMap, 40 per city in 21 cities: 57% found. Each found row was compared with the input address: house number, letter, street and direction. None differed. 10 rows were parcels that cover a range of numbers including the requested one, 3 had a direction on one side only.

| State | Found |
|---|---|
| Connecticut | 78% |
| North Carolina | 61% |
| Massachusetts | 54% |
| West Virginia | 50% |
| New York | 48% |
| Arkansas | 48% |

Results vary by city, from 30% in Fayetteville, AR to 95% in Torrington, CT. Boston found 42%, Manhattan 40%, Queens 43%, Brooklyn 53%, Charlotte 80%, Durham 45%. The test used cities and their suburbs; rural addresses were not measured. Most misses are addresses the state record does not list under that house number: one parcel with several buildings, a New York City lot filed under one number of the building, or a house letter the source does not list (`401 B Canal St` when the record is `401 CANAL ST`). An address taken from a county record or an MLS listing should match more often than an address from a map, but that was not measured.

A run handles about 1 to 1.5 addresses per second; New York addresses are the slowest. A Massachusetts address that the geocoder misses goes to a direct query that can take up to a minute.

### Massachusetts property records by address

The source is the MassGIS statewide property tax parcels service. Fields come from local assessors, and `taxYear` shows which fiscal year each town last delivered. Everett publishes owner names on about a third of its parcels. Boston often lists a range of house numbers on one parcel.

### North Carolina property records by address

The source is NC OneMap. What is filled depends on the county: sale dates and year built are missing in many counties. Eight counties publish no numbered property addresses, so they cannot be searched by address: Alleghany, Bladen, Cabarrus, Franklin, Hoke, Orange, Perquimans and Richmond.

### Connecticut property records by address

The source is the Connecticut CAMA and Parcel Layer, published under CC0.

### New York property records

The source is the NYS Tax Parcels Public service. Only counties that allow redistribution are published: Albany, Bronx, Broome, Cayuga, Chautauqua, Cortland, Erie, Genesee, Greene, Hamilton, Kings, Lewis, Livingston, Montgomery, New York, Oneida, Onondaga, Ontario, Orange, Oswego, Otsego, Putnam, Queens, Rensselaer, Richmond, Rockland, Schuyler, St. Lawrence, Steuben, Suffolk, Sullivan, Tioga, Tompkins, Ulster, Warren, Wayne, Westchester and Wyoming. An address in another county returns `not_found`.

New York City records have an owner name but no mailing address. In Queens the hyphen is part of the house number: `71-20 Austin St` is one number. `landUse` is the state property class code.

Do not use New York records for solicitation or fund-raising (N.Y. Public Officers Law 89(2)(b)(iii)).

### Arkansas and West Virginia property records

Arkansas publishes no owner mailing address, and about one parcel in five has no street address in the source, so it cannot be found by address. West Virginia publishes no values.

### Example output

```json
{
  "inputAddress": "16 W Jones St, Raleigh, NC 27601",
  "status": "found",
  "state": "NC",
  "county": "Wake",
  "propertyAddress": "16 W JONES ST",
  "parcelId": "1704701109",
  "ownerName": "NORTH CAROLINA STATE OF",
  "mailingAddress": "116 W JONES ST",
  "absenteeOwner": true,
  "totalValue": 111173937,
  "landValue": 83141730,
  "buildingValue": 28032207,
  "yearBuilt": 1962,
  "lotSizeAcres": 4.9867,
  "matchMethod": "geocoder_40m",
  "matchedRecords": 1,
  "sourceName": "NC OneMap Statewide Parcels",
  "sourceUrl": "https://www.nconemap.gov/"
}
```

### Status values

| status | Meaning | Charged |
|---|---|---|
| `found` | Record found | yes, once per address and once per record in a run |
| `not_found` | No parcel with this number and street | no |
| `address_mismatch` | A close address exists, but the street type, direction, house number, city or ZIP differs | no |
| `ambiguous_address` | The address fits several places or properties, has owners that differ by unit, or has no city or ZIP | no |
| `unit_not_found` | The building has records, none for your unit or house letter | no |
| `owner_missing` | Record found, but the source has no owner name or only a placeholder like UNKNOWN | no |
| `owner_withheld` | Record found, owner hidden, see below | no |
| `state_excluded` | State left out on purpose, see below | no |
| `state_not_covered` | State not covered yet | no |
| `invalid_address` | No house number and street could be read from the line | no |
| `duplicate` | Same address as an earlier line | no |
| `source_error` | A source did not answer; try again | no |
| `not_processed` | The run stopped first (timeout, spending limit or `maxItems`) | no |

### Pricing

$0.003 per address with an owner record ($3 per 1,000). There is no start fee.

You pay nothing for any other status, nothing for the second and later rows of one address, and nothing for a record already charged earlier in the same run. At the 57% match rate above, a list of 1,000 addresses costs about $1.70.

Set a spending limit on the run and the Actor stops before going over it.

### API for developers

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("firsthand/property-owner-lookup").call(run_input={
    "addresses": ["165 Capitol Ave, Hartford, CT 06106"],
})
items = client.dataset(run.default_dataset_id).list_items().items
for row in items:
    print(row["inputAddress"], row["status"], row.get("ownerName"))
```

Field names from other Actors work here too: `address`, `lookups`, `stateHint`, `countyHint`, `maxRecords`, `maxResults`.

### Good to know

- **Excluded states.** South Carolina, Kansas, Arizona, Washington, New Jersey, Vermont and Colorado are left out on purpose because of state law or the terms of the state service. Addresses there return `state_excluded` with the reason.
- **Withheld owners.** When the mailing address is in Vermont or New Jersey by state or by ZIP, the row has no name or mailing address and is not charged. In New York the second mailing address of the record is checked too. The same applies when the source marks the owner as protected (`SUPPRESSED OWNER`, `Name Withheld`, `REDACTED`). A typo in a ZIP can hide an owner who lives elsewhere. This works only where the source publishes a mailing address, so not in Arkansas and New York City; Connecticut publishes no mailing ZIP, so there it is checked by state only, and a mailing address with neither state nor ZIP cannot be checked.
- **Placeholders are not names.** Owner names such as `RESIDENT`, `UNKNOWN OWNER`, `VACANT` or `RIGHT OF WAY` give a free `owner_missing`. Mailing addresses such as `MAIL RETURNED` or `UNKNOWN ADDRESS` are returned empty.
- **Legal use.** These are public records, but they contain personal names and addresses. You are responsible for how you use them. Do not use this Actor for tenant screening, employment, credit or insurance decisions (FCRA). Follow TCPA, CAN-SPAM and state rules when you contact owners. Nothing here is legal advice.
- **Removing a record.** To ask for a record to be hidden, open an issue on this Actor's Issues tab with the state, town and the property address or parcel number.
- **Freshness.** Sources update at their own pace. Massachusetts, Connecticut and New York give `taxYear`, North Carolina and Arkansas give `sourceDate`. West Virginia's service is updated for tax year 2023, see `sourceNote`.
- **Values differ by state.** Connecticut assesses at 70% of market value, New York uses local assessment ratios. Compare values only inside one state.
- **A sale price of 0 or 1** usually means a transfer without a market price. Placeholder sale dates such as 1/1/1900 are returned as empty.

# Actor input Schema

## `addresses` (type: `array`):

US street addresses, one per line, with city and state: 165 Capitol Ave, Hartford, CT 06106. Add the direction (N, E) and the unit when the address has one. Covered states: MA, CT, NC, NY, AR, WV. Every line gets a row; a line in another state returns a free row that explains why.

## `state` (type: `string`):

State used when a line has no state: MA or Massachusetts.

## `county` (type: `string`):

Optional county name. Helps in Arkansas and New York when the US Census geocoder does not know a rural address.

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

Stop after this many addresses with a charged record. Leave empty to process the whole list.

## `address` (type: `string`):

One address, same as one line of Addresses.

## `lookups` (type: `array`):

Same as Addresses.

## `stateHint` (type: `string`):

Same as State.

## `countyHint` (type: `string`):

Same as County.

## `maxRecords` (type: `integer`):

Same as Maximum addresses with a record.

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

Same as Maximum addresses with a record.

## Actor input object example

```json
{
  "addresses": [
    "165 Capitol Ave, Hartford, CT 06106",
    "1 Ashburton Pl, Boston, MA 02108",
    "16 W Jones St, Raleigh, NC 27601"
  ]
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "addresses": [
        "165 Capitol Ave, Hartford, CT 06106",
        "1 Ashburton Pl, Boston, MA 02108",
        "16 W Jones St, Raleigh, NC 27601"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("firsthand/property-owner-lookup").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 = { "addresses": [
        "165 Capitol Ave, Hartford, CT 06106",
        "1 Ashburton Pl, Boston, MA 02108",
        "16 W Jones St, Raleigh, NC 27601",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("firsthand/property-owner-lookup").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 '{
  "addresses": [
    "165 Capitol Ave, Hartford, CT 06106",
    "1 Ashburton Pl, Boston, MA 02108",
    "16 W Jones St, Raleigh, NC 27601"
  ]
}' |
apify call firsthand/property-owner-lookup --silent --output-dataset

```

## MCP server setup

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

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/Ma4Es1wlyys9unOAw/builds/ZO1k4Vce202HVLC6z/openapi.json
