Plotted API

The Plotted real estate API and property API. REST + JSON, bearer-token auth, 150M+ U.S. property records with owners, contacts, dwelling attributes, and spatial queries. Base URL: https://api.plotted.to

Quickstart

Three steps. Should take under five minutes.

1. Get a key

Sign up at plotted.to/pricing. The free plan gives you 100 credits — enough to try every endpoint.

2. Hit the API

curlshell
curl "https://api.plotted.to/v1/parcels?state=FL&mail_zip=34102&limit=5" \
  -H "Authorization: Bearer plt_live_***"

3. Parse the response

200 OK · application/jsonjson
{
  "data": [
    {
      "row_id": 8421947823,
      "state": "FL",
      "county": "Collier",
      "owner_name": "NAPLES BAY HOLDINGS LLC",
      "is_trust": false,
      "situs_address": "4421 GULF SHORE BLVD N",
      "mail_zip": "34102",
      "year_built": 2003,
      "bedrooms": 4,
      "lat": 26.142441,
      "lon": -81.795822
      // ...35 more columns
    }
  ],
  "page": { "limit": 5, "next_cursor": "eyJyb3dfaWQiOjg0MjE5NDc4MjN9" },
  "meta": { "credits_used": 1, "credits_remaining": 99999, "latency_ms": 31 }
}

Authentication

Bearer token in the Authorization header. Keys are scoped to your account — never embed them in client-side code. Keys start with plt_live_ (production) or plt_test_ (sandbox, doesn't count against credits).

Headers
Authorization: Bearer plt_live_***
Content-Type:  application/json     // for POST endpoints
Tip: Rotate keys from your dashboard. Revocation is instant. We log every key's last-used timestamp and IP so you can spot anomalies.

Credits & rate limits

Every API call costs 1 credit, except:

Rate limits by plan:

PlanPer minutePer second burstConcurrent
Free6052
Standard6002010
Pro6,00020050
Enterpriseunlimitednegotiatednegotiated

When you hit a rate limit you get 429 Too Many Requests with a Retry-After header.

Errors

Errors return a JSON envelope with a stable code string and a human-readable message.

{
  "error": {
    "code": "invalid_parameter",
    "message": "radius_mi must be between 0.01 and 50",
    "param": "radius_mi",
    "request_id": "req_01H8Z3X4..."
  }
}
StatuscodeWhen
400invalid_parameterA query/body parameter failed validation
401missing_credentialsNo Authorization header
401invalid_credentialsKey invalid, revoked, or wrong environment
402insufficient_creditsOut of credits — top up or upgrade
404not_foundRecord (e.g., row_id) doesn't exist
429rate_limitedPlan rate limit exceeded
500server_errorOur fault. Retry; request_id if you contact support

Pagination

List endpoints accept limit (max 1,000, default 50) and return a page.next_cursor. Pass it back as cursor to get the next page. Cursors are opaque and expire after 60 minutes.

Core endpoints

GET /v1/parcels cost: 1 credit

Filter and list records. All filters AND-combined. Returns paginated.

ParamTypeDescription
statestring2-letter state code, e.g. FL
mail_zipstring5-digit owner mailing zip
situs_zipstring5-digit property zip
ownerstringOwner-name substring (case-insensitive)
is_trustboolFilter trust-held parcels
has_contactboolRecords with email or phone attached
year_built_min / year_built_maxintBuild year range
value_min / value_maxnumberMarket/assessed value range (USD)
beds_min / baths_minnumberMin bedrooms / full baths
sqft_min / sqft_maxnumberBuilding sqft range
countystringCounty name (exact)
limit / cursorint / stringPagination
Examplecurl
curl "https://api.plotted.to/v1/parcels?state=FL&is_trust=true&has_contact=true&limit=10" \
  -H "Authorization: Bearer $KEY"
GET /v1/parcels/{row_id} cost: 1 credit

Fetch one record by its stable row_id. Use this for cached deep-links.

GET /v1/owners/search cost: 1 credit

Fuzzy trigram search across owner_name and owner1..owner5.

ParamTypeDescription
namerequiredstringSearch query (≥3 chars)
statestringRestrict to one state

Spatial endpoints

GET /v1/geocode cost: 1 credit

Address → lat/lon, using our own parcel data (no third-party geocoder). Returns the best-confidence match.

ParamTypeDescription
addressrequiredstringStreet + number, e.g. 4421 Gulf Shore Blvd N, Naples FL 34103
state / zipstringParsed from a comma-formatted address tail, or pass explicitly to disambiguate
GET /v1/reverse cost: 1 credit

Lat/lon → closest parcel within max_dist_m (default 200m).

GET /v1/parcels/nearby cost: 1 credit

Radius search around a point. Combines with every /v1/parcels filter.

ParamTypeDescription
latrequiredfloatLatitude
lonrequiredfloatLongitude
radius_mirequiredfloat0.01 – 50.0
state, is_trust, has_contact, owner, year_built_min/max(see /parcels)All filters stack
limit / cursorint / stringPagination
Example: trust holdings within 1mi, with phonescurl
curl "https://api.plotted.to/v1/parcels/nearby" -G \
  -d "lat=26.142&lon=-81.795&radius_mi=1" \
  -d "is_trust=true&has_contact=true" \
  -H "Authorization: Bearer $KEY"

Response adds distance_mi to each record, sorted ascending.

GET /v1/parcels/nearby_address cost: 2 credits

Same as /nearby, but takes an address string instead of lat/lon — we geocode it for you in the same call.

GET /v1/parcels/same_street/{row_id} cost: 1 credit

All neighbors on the same normalized street as the given parcel. Suffix-tolerant (DR ≡ DRIVE ≡ DRV).

POST /v1/parcels/within cost: 2 credits

All parcels inside a GeoJSON Polygon or MultiPolygon. For drawing arbitrary regions on a map.

Bodyjson
{
  "geometry": {
    "type": "Polygon",
    "coordinates": [[[-81.80,26.13],[-81.78,26.13],
                     [-81.78,26.16],[-81.80,26.16],[-81.80,26.13]]]
  },
  "filters": { "is_trust": true },
  "limit": 500
}
POST /v1/parcels/bbox cost: 1 credit

Rectangle / map-viewport queries.

Curated views

GET /v1/views/absentee cost: 1 credit

Pre-filtered to mail_zip ≠ situs_zip. Accepts state, min_distance_mi, and all standard filters.

GET /v1/views/trust cost: 1 credit

Pre-filtered to is_trust = true. Accepts state + standard filters.

GET /v1/views/homeowners cost: 1 credit

Pre-filtered to owner-occupied homes — every homeowner record across the dataset, in one call.

Bulk endpoints

POST /v1/match cost: 1 credit per record

Send a list of up to 10,000 addresses. Get back the enriched parcel record for each.

Bodyjson
{
  "items": [
    { "address": "4421 Gulf Shore Blvd N", "city": "Naples", "state": "FL" },
    { "address": "100 Bayfront Pl",        "city": "Naples", "state": "FL" }
  ]
}

Response: array of { input, match: parcel|null, confidence: 0–1 }.

POST /v1/match_and_expand cost: 1 credit per input

Match each input, then return all parcels within radius_mi of each match. Includes filters.

Bodyjson
{
  "items": [/* up to 10,000 addresses */],
  "radius_mi": 0.5,
  "filters": { "is_trust": true }
}

Enrichment & company data

Attach people, contacts, professional/LinkedIn profiles, business firmographics, and property overlays to an address, a person, or a company — backed by nationwide professional profiles plus national business and property data.

GET /v1/enrich/address cost: 1 credit

Everything known at an address: the property, the people who live/own there, and distress & public-record overlays.

ParamTypeDescription
address_idint64Canonical address id from a prior call
addressstringStreet address — resolved together with zip
zipstring5-digit ZIP (required with address)

One of address_id or address+zip is required.

GET /v1/person/enrich cost: 1 credit

Resolve a person → identity, addresses, entities controlled, and political giving, plus a LinkedIn professional layer (title, company, industry) when matched by email, phone, or LinkedIn URL.

ParamTypeDescription
person_idint64Canonical person id
emailstringExact email
phonestring10-digit phone
last or namestringSurname — pair with zip or state
firststringFirst name (optional refinement)
linkedinstringLinkedIn URL → professional profile

Provide one key. Name lookups require zip or state.

Search professional profiles nationwide. Provide one anchorlinkedin_url, company, or last — and refine with job title, role, industry, region, or first name.

ParamTypeDescription
linkedin_urlanchorstringExact LinkedIn profile
companyanchorstringCompany name
lastanchorstringSurname
titlestringJob title contains…
rolestringJob role (e.g. engineering)
industrystringIndustry contains…
regionstringUS state (2-letter or full name)
firststringFirst name
limitintMax results (default 25, max 100)

Example: /v1/person/search?company=Google&title=engineer&limit=25

GET /v1/company/enrich cost: 1 credit

Business registry (officers, registered agent, addresses) blended with LinkedIn firmographics (website, industry, size, LinkedIn URL, headcount).

ParamTypeDescription
einstringEmployer ID Number
namestringCompany name (≥3 chars)
statestringRestrict registry to one state

One of ein or name is required.

Filter-first prospecting (Apollo / ZoomInfo style) over profiles nationwide. Combine any filters below; multi-value is comma-separated. Contacts are gated — reveal email & phone with /v1/person/reveal.

ParamTypeDescription
qstringFree text across name, title, company
titlestringJob title contains…
namestringPerson name contains…
companystringCompany name contains…
industrystringIndustry contains…
senioritystringSeniority (e.g. director, vp, cxo)
rolestringJob role (e.g. engineering, sales)
region / countrystringUS state (2-letter or full) / country
localitystringCity / locality
company_sizestringHeadcount band (e.g. 51-200)
domainstringCompany website / domain
schoolstringSchool / university attended
genderstringGender
has_email / has_phonestringSet to 1 to only return profiles with that contact on file
limitintMax results (default 25, max 100)

Example: /v1/people/search?title=vp%20engineering&industry=software&region=CA&has_email=1

Filter-first company search over 49M firmographic profiles.

ParamTypeDescription
qstringFree text across name + industry
namestringCompany name contains…
industrystringIndustry contains…
locationstringHQ location contains…
domainstringCompany website / domain
sizestringHeadcount band (e.g. 51-200)
min_headcountintMin profiles we hold at the company
founded_min / founded_maxintFounded-year range
limitintMax results (default 25, max 100)
GET /v1/person/reveal cost: 1 credit (refunded if none on file)

Spend 1 credit to unlock a profile's email(s) and phone(s) by LinkedIn URL. If no contact is on file, the call returns 404 and the credit is automatically refunded.

ParamTypeDescription
linkedin_urlrequiredstringLinkedIn URL of the profile to reveal

Record schema

Every list response contains records with these columns. Nullable unless marked.

FieldTypeNotes
row_idint64PK — stable across rebuilds
sourcestringOrigin layer for the record (county parcel layer or homeowner index)
statestring2-letter
county / parcel_idstringOriginal county parcel identifier
owner_namestringRaw owner string, preserved as published
owner1 ... owner5stringSplit individual owners
owner1_type ... owner5_typeenumperson · entity · trust · gov
owner_countintCount of non-null owner1..5
is_trustboolTrust-owned flag
fiduciary_name / fiduciary_codestringTrustee / fiduciary fields where the county publishes them (e.g. Florida)
mail_address / mail_city / mail_statestringOwner mailing block
mail_zip / mail_zip4stringAlways 5-digit / +4 separated
situs_address / situs_city / situs_zipstringProperty location block
land_use_code / land_use_descstringCounty land-use classification
year_builtstringOriginal construction year
bldg_sqft / res_units / bedrooms / baths_full / baths_halffloatDwelling attributes
assessed_value / sale_price / sale_date / acresnumericValue attributes
owner_email / owner_email_2stringUp to 2 emails when matched
owner_phone / owner_phone_2stringUp to 2 phones
contact_match_levelenumowner · occupant
contact_confidencefloat0.0 – 1.0
lat / lonfloatParcel centroid (WGS84)
Privacy: Contact fields are returned redacted in sandbox environments and per regional regulation (e.g., California consumer requests). Real values only on production keys with verified business use. Consumers can suppress their data via Do Not Sell or Share My Info.

Changelog