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
curl "https://api.plotted.to/v1/parcels?state=FL&mail_zip=34102&limit=5" \ -H "Authorization: Bearer plt_live_***"
3. Parse the response
{
"data": [
{
"row_id": 8421947823,
"state": "FL",
"county": "Collier",
"owner_name": "NAPLES BAY HOLDINGS LLC",
"is_trust": false,
"is_homeowner": true,
"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).
Authorization: Bearer plt_live_*** Content-Type: application/json // for POST endpoints
Credits & rate limits
Every API call costs 1 credit, except:
POST /v1/matchandPOST /v1/match_and_expand— 1 credit per record submitted- Sandbox keys (
plt_test_) — free, but limited to 100 req/min and return synthetic data GET /v1/healthand 4xx responses — free
Rate limits by plan:
| Plan | Per minute | Per second burst | Concurrent |
|---|---|---|---|
| Free | 60 | 5 | 2 |
| Standard | 600 | 20 | 10 |
| Pro | 6,000 | 200 | 50 |
| Enterprise | unlimited | negotiated | negotiated |
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..."
}
}
| Status | code | When |
|---|---|---|
| 400 | invalid_parameter | A query/body parameter failed validation |
| 401 | missing_credentials | No Authorization header |
| 401 | invalid_credentials | Key invalid, revoked, or wrong environment |
| 402 | insufficient_credits | Out of credits — top up or upgrade |
| 404 | not_found | Record (e.g., row_id) doesn't exist |
| 429 | rate_limited | Plan rate limit exceeded |
| 500 | server_error | Our 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
Filter and list records. All filters AND-combined. Returns paginated.
| Param | Type | Description |
|---|---|---|
| state | string | 2-letter state code, e.g. FL |
| mail_zip | string | 5-digit owner mailing zip |
| situs_zip | string | 5-digit property zip |
| owner | string | Owner-name substring (case-insensitive) |
| is_homeowner | bool | Filter homeowners only |
| is_trust | bool | Filter trust-held parcels |
| has_contact | bool | Records with email or phone attached |
| year_built_min / year_built_max | int | Build year range |
| limit / cursor | int / string | Pagination |
curl "https://api.plotted.to/v1/parcels?state=FL&is_trust=true&has_contact=true&limit=10" \ -H "Authorization: Bearer $KEY"
Fetch one record by its stable row_id. Use this for cached deep-links.
Fuzzy trigram search across owner_name and owner1..owner5.
| Param | Type | Description |
|---|---|---|
| namerequired | string | Search query (≥3 chars) |
| state | string | Restrict to one state |
| min_similarity | float | 0.0–1.0, default 0.4 |
Spatial endpoints
Address → lat/lon, using our own parcel data (no third-party geocoder). Returns the best-confidence match.
| Param | Type | Description |
|---|---|---|
| addressrequired | string | Street + number, e.g. 4421 Gulf Shore Blvd N |
| city / state / zip | string | At least one disambiguator recommended |
Lat/lon → closest parcel within max_dist_m (default 200m).
Radius search around a point. Combines with every /v1/parcels filter.
| Param | Type | Description |
|---|---|---|
| latrequired | float | Latitude |
| lonrequired | float | Longitude |
| radius_mirequired | float | 0.01 – 50.0 |
| state, is_trust, is_homeowner, has_contact, owner, year_built_min/max | (see /parcels) | All filters stack |
| limit / cursor | int / string | Pagination |
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.
Same as /nearby, but takes an address string instead of lat/lon — we geocode it for you in the same call.
All neighbors on the same normalized street as the given parcel. Suffix-tolerant (DR ≡ DRIVE ≡ DRV).
All parcels inside a GeoJSON Polygon or MultiPolygon. For drawing arbitrary regions on a map.
{
"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_homeowner": true },
"limit": 500
}
Rectangle / map-viewport queries.
Curated views
Pre-filtered to mail_zip ≠ situs_zip. Accepts state, min_distance_mi, and all standard filters.
Pre-filtered to is_trust = true. Accepts state + standard filters.
Pre-filtered to is_homeowner = true — every owner-occupied record across the dataset, in one call.
Bulk endpoints
Send a list of up to 10,000 addresses. Get back the enriched parcel record for each.
{
"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 }.
Match each input, then return all parcels within radius_mi of each match. Includes filters.
{
"items": [/* up to 10,000 addresses */],
"radius_mi": 0.5,
"filters": { "is_trust": true }
}
Record schema
Every list response contains records with these columns. Nullable unless marked.
| Field | Type | Notes |
|---|---|---|
| row_id | int64 | PK — stable across rebuilds |
| source | string | Origin layer for the record (county parcel layer or homeowner index) |
| state | string | 2-letter |
| county / parcel_id | string | Original county parcel identifier |
| owner_name | string | Raw owner string, preserved as published |
| owner1 ... owner5 | string | Split individual owners |
| owner1_type ... owner5_type | enum | person · entity · trust · gov |
| owner_count | int | Count of non-null owner1..5 |
| is_trust / is_homeowner | bool | Flag columns |
| fiduciary_name / fiduciary_code | string | Trustee / fiduciary fields where the county publishes them (e.g. Florida) |
| mail_address / mail_city / mail_state | string | Owner mailing block |
| mail_zip / mail_zip4 | string | Always 5-digit / +4 separated |
| situs_address / situs_city / situs_zip | string | Property location block |
| land_use_code / land_use_desc | string | County land-use classification |
| year_built | string | Original construction year |
| bldg_sqft / res_units / bedrooms / baths_full / baths_half | float | Dwelling attributes |
| assessed_value / sale_price / sale_date / acres | numeric | Value attributes |
| owner_email / owner_email_2 | string | Up to 2 emails when matched |
| owner_phone / owner_phone_2 | string | Up to 2 phones |
| contact_match_level | enum | owner · occupant |
| contact_confidence | float | 0.0 – 1.0 |
| lat / lon | float | Parcel centroid (WGS84) |
Changelog
- v1.0 — 2026-06-11. Public launch. 150,000,000+ records. 51 states.