Resolve an address, coordinate, or APN to canonical join keys
curl --request POST \
--url https://api.example.com/v1/lookup \
--header 'Content-Type: application/json' \
--data '
{
"input": "<string>",
"include_parcel": true
}
'import requests
url = "https://api.example.com/v1/lookup"
payload = {
"input": "<string>",
"include_parcel": True
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({input: '<string>', include_parcel: true})
};
fetch('https://api.example.com/v1/lookup', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v1/lookup",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'input' => '<string>',
'include_parcel' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/v1/lookup"
payload := strings.NewReader("{\n \"input\": \"<string>\",\n \"include_parcel\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/v1/lookup")
.header("Content-Type", "application/json")
.body("{\n \"input\": \"<string>\",\n \"include_parcel\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/lookup")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"input\": \"<string>\",\n \"include_parcel\": true\n}"
response = http.request(request)
puts response.read_body{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Resolve
POST /v1/lookup
Resolve an address, coordinate, or APN to canonical join keys: a coordinate, resolved address, and — when it’s safe to — a parcel.
POST
/
v1
/
lookup
Resolve an address, coordinate, or APN to canonical join keys
curl --request POST \
--url https://api.example.com/v1/lookup \
--header 'Content-Type: application/json' \
--data '
{
"input": "<string>",
"include_parcel": true
}
'import requests
url = "https://api.example.com/v1/lookup"
payload = {
"input": "<string>",
"include_parcel": True
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({input: '<string>', include_parcel: true})
};
fetch('https://api.example.com/v1/lookup', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v1/lookup",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'input' => '<string>',
'include_parcel' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/v1/lookup"
payload := strings.NewReader("{\n \"input\": \"<string>\",\n \"include_parcel\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/v1/lookup")
.header("Content-Type", "application/json")
.body("{\n \"input\": \"<string>\",\n \"include_parcel\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/lookup")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"input\": \"<string>\",\n \"include_parcel\": true\n}"
response = http.request(request)
puts response.read_body{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}When to use this
/v1/geocode answers “where is this address.” This
endpoint answers a different question: “what are the canonical join keys for
this place, and how confident should I be?” Use /v1/lookup when the input
might be ambiguous, when you want a parcel attached, or when the input could
be a coordinate or an APN instead of a clean address.disposition is
one of:
resolved— a coordinate, plus a parcel when the geocode cleared parcel-quality accuracy.clarify— the input is genuinely ambiguous: multiple plausible matches at comparable confidence. You getcandidates, never a silent pick.no_match— an honest failure, with areason.
Request shape
| Field | Type | Required | Notes |
|---|---|---|---|
input | string | yes | 1–256 characters. An address, a "lat,lng" pair, or an APN. |
include_parcel | boolean | no | Default true. Set false to skip the parcel lookup entirely — you still get the coordinate and disposition, just no parcel attempt. |
kind | string | no | Explicit override: "address", "coord", or "apn". Omit to let the input’s shape decide. |
Example: a clean, parcel-quality match
curl -s https://api.mireye.com/v1/lookup \
-H "Authorization: Bearer $MIREYE_API_TOKEN" \
-H 'content-type: application/json' \
-d '{"input": "480 Berdoll Ln, Cedar Creek TX"}' | jq
{
"disposition": "resolved",
"lat": 30.199699,
"lng": -97.496411,
"resolved_address": "480 BERDOLL LN, CEDAR CREEK, TX 78612",
"resolved_location": { "lat": 30.199699, "lng": -97.496411, "source": "address" },
"county_fips": "48021",
"county": "Bastrop County",
"tract_geoid": "48021950400",
"state_fips": "48",
"state": "Texas",
"block_group_geoid": "480219504001",
"block_geoid": "480219504001000",
"congressional_district": "TX-27",
"cbsa_name": "Austin-Round Rock-San Marcos, TX Metro Area",
"cbsa_code": "12420",
"elevation_m": 152.3,
"fema_flood_zone": "X",
"within_floodplain": false,
"coastal_high_hazard": false,
"county_market": {
"population": 106822,
"population_growth_1yr_pct": 4.1,
"net_domestic_migration": 3120,
"building_permits_total_annual": 1450,
"building_permits_sf_annual": 1200,
"building_permits_yoy_pct": 6.2,
"hpi_yoy_pct": 3.8,
"employment_total": 47210,
"employment_yoy_pct": 2.9,
"median_household_income_usd": 78500
},
"in_opportunity_zone": false,
"opportunity_zone_tract_geoid": null,
"timezone": "America/Chicago",
"parcel": {
"parcel_id": "R123456",
"apn": "...",
"address": "480 BERDOLL LN, CEDAR CREEK, TX 78612",
"area_m2": 20234.5,
"geometry": "...",
"geometry_wkt": "...",
"owner": "PECAN GROVE FARMS #1 LLC",
"zoning": "AG",
"land_use": "Agricultural",
"assessed_value_usd": 185000,
"last_sale_date": "2019-03-14",
"last_sale_price_usd": 210000,
"transaction_count": 3,
"match_type": "exact",
"match_distance_m": 4.2,
"match_radius_m": null,
"source": "parcel_vendor_paid",
"interior_point_lat": 30.199712,
"interior_point_lng": -97.496388
},
"match_method": "geocode_rooftop+point_in_parcel",
"confidence": 0.95,
"parcel_grade": true,
"precision_note": null
}
Area, hazard, and market context
Everyresolved response carries free context beyond the coordinate and
parcel, gathered concurrently with the parcel lookup (not after it — a
slow enrichment call never adds to the parcel leg’s latency):
- Jurisdiction codes, from the same Census Geocoder call as
county_fips/county/tract_geoid:state_fips,state,block_group_geoid,block_geoid,congressional_district(e.g."TX-27"),cbsa_name/cbsa_code(the metro or micro market, e.g."Austin-Round Rock-San Marcos, TX Metro Area"). - Elevation and flood risk:
elevation_m,fema_flood_zone,within_floodplain,coastal_high_hazard— the same catalog sources/v1/fetchuses (3DEP/EPQS elevation, FEMA NFHL). - County market context:
county_marketbundles ten metrics from five federal sources (Census PEP/BPS/ACS, FHFA, BLS QCEW) — population and its 1-year growth, net domestic migration, building permits (total, single-family, YoY %), home-price YoY %, employment (total, YoY %), and median household income. - Opportunity Zone status:
in_opportunity_zone(a federal tax-incentive designation) andopportunity_zone_tract_geoidwhen applicable. - Timezone:
timezone, the IANA zone name (e.g."America/Chicago").
null on its own failure —
none of them can turn an otherwise-successful resolved into anything else.
Same guarantee parcel_unavailable already makes for the parcel leg, applied
to enrichment too.
Example: genuine ambiguity — never a silent pick
curl -s https://api.mireye.com/v1/lookup \
-H "Authorization: Bearer $MIREYE_API_TOKEN" \
-H 'content-type: application/json' \
-d '{"input": "1100 King St W, Toronto"}' | jq
{
"disposition": "clarify",
"candidates": [
{ "resolved_address": "1100 King St W, Toronto, ON, Canada", "lat": 43.6394, "lng": -79.4223, "confidence": 0.82 },
{ "resolved_address": "1100 King St W, Toronto, OH 45871", "lat": 41.0284, "lng": -84.3247, "confidence": 0.80 }
]
}
Example: a lone winner in the wrong town is a no_match, never silent
Some inputs name a city the geocoder only has ONE plausible match for — but the wrong one, in a different town that happens to share the same street name and number. There’s no second candidate to offer, so this isn’tclarify;
it’s an honest no_match, same governing rule as everywhere else on this
page:
curl -s https://api.mireye.com/v1/lookup \
-H "Authorization: Bearer $MIREYE_API_TOKEN" \
-H 'content-type: application/json' \
-d '{"input": "100 Main St, Columbus"}' | jq
{
"disposition": "no_match",
"reason": "unaddressed_or_no_match",
"hint": "the input named a different city than the best match found ('100 Main St, Fair Bluff, NC 28439') -- treated as a non-match rather than silently resolving to a place you didn't ask for."
}
Saint/St.,
Mount/Mt., Fort/Ft. — so a legitimately-abbreviated city is never a
false positive). It only fires when the input actually names a city; a
street-only input has nothing to contradict.
Example: a parcel-lookup failure never demotes a good geocode
If the parcel vendor is unavailable — including a plan/quota issue on our end — the geocode still comes back honestly, just without a parcel:{
"disposition": "resolved",
"lat": 38.8977,
"lng": -77.0365,
"resolved_address": "1600 PENNSYLVANIA AVE NW, WASHINGTON, DC",
"parcel_unavailable": true,
"parcel_unavailable_reason": "parcel_vendor_quota_exhausted",
"match_method": "geocode_rooftop",
"confidence": 0.95
}
disposition away from
resolved. The geocode succeeded; only the parcel attempt didn’t, and the
response says exactly why.
Check parcel_grade before re-using the coordinate
parcel_grade: false means the coordinate is an estimate. The geocoder
could only interpolate the point along the street rather than match it to a
rooftop, and a range_interpolation-grade match can be off by up to ~2,872 m
in rural areas — easily a different parcel. precision_note says the same
thing in prose, ready to show a user.
The parcel in that response is still the right one. A street-tier lookup
matches by the situs address, never by the estimated pin, precisely
because a mislocated pin can bill the wrong parcel. It is only the top-level
lat/lng that is approximate — so passing it to /v1/fetch or /v1/ask
can return the neighbouring property’s data while every field in that
response looks perfectly confident.
Use parcel.interior_point_lat / interior_point_lng instead. They are
derived from the matched parcel’s own geometry and are guaranteed to fall
inside it, which makes them the coordinate to key downstream point queries on.
They are null only when no geometry came back.
{
"disposition": "resolved",
"lat": 34.033012, "lng": -118.372694, // interpolated — may be the neighbour
"resolved_address": "5861 WASHINGTON BLVD, CULVER CITY, CA 90232",
"parcel_grade": false,
"precision_note": "Note: this location was estimated from the street address rather than matched to a rooftop, ...",
"parcel": {
"parcel_id": "5065-016-005", // correct — matched by address, not by the pin
"interior_point_lat": 34.033104, // use THIS for /v1/fetch and /v1/ask
"interior_point_lng": -118.372611
},
"match_method": "geocode_range_interpolation+parcel_address_search",
"confidence": 0.75
}
false when no geocode ran: a
coordinate you supplied is not an estimate of ours, and clarify / no_match
have no single coordinate to grade. /v1/fetch and /v1/ask carry the same
pair inside their geocode block, graded by the same rule.
Below centroid quality (a city or county middle), the input is rejected
outright rather than graded — see address_too_coarse under Errors.
Swapped coordinates are a hard error, never a nearest-match
curl -s -i https://api.mireye.com/v1/lookup \
-H "Authorization: Bearer $MIREYE_API_TOKEN" \
-H 'content-type: application/json' \
-d '{"input": "-73.9,40.7"}'
HTTP/1.1 422 Unprocessable Entity
{
"detail": {
"error": "resolve_coord_bounds",
"message": "'-73.9,40.7': lat/lng appear swapped",
"retryable": false
}
}
APN-only lookup is not supported yet
The only wired parcel integration here is a point (lat/lng) lookup — there is no attribute-search-by-APN path. An APN-shaped input returns a cleanno_match:
{
"disposition": "no_match",
"reason": "apn_not_supported_in_v1",
"hint": "APN-only lookup isn't supported yet -- supply an address or coordinate."
}
Response shape
| Field | Type | Notes |
|---|---|---|
disposition | string | resolved, clarify, or no_match. |
lat / lng | number | Present on resolved. |
resolved_address | string or null | The address the geocoder actually matched, when input was an address. |
resolved_location | object | Present on resolved. The uniform {lat, lng, source} echo every location endpoint carries — source is "address" or "coordinate" depending on the input kind. |
county_fips | string or null | 5-digit county FIPS. Best-effort — a free Census Geocoder lookup that never fails the request; null if it’s unreachable. |
county | string or null | Census-reported county name (e.g. "Bastrop County"), not formatted as “County, ST”. |
tract_geoid | string or null | 11-digit Census tract GEOID, 2020 vintage. |
state_fips / state | string or null | 2-digit state FIPS and full state name — same Census call as county/tract. |
block_group_geoid / block_geoid | string or null | Finer-grained Census geoids than tract, same call. |
congressional_district | string or null | e.g. "TX-27" — same Census call, one more layers token. |
cbsa_name / cbsa_code | string or null | The metro/micro market (Core-Based Statistical Area) this point falls in, same Census call. |
elevation_m | number or null | Meters above sea level (3DEP/EPQS). |
fema_flood_zone / within_floodplain / coastal_high_hazard | — | FEMA NFHL flood-risk fields — same catalog source /v1/fetch uses. |
county_market | object or null | Ten county-level federal metrics — see “Area, hazard, and market context” above. |
in_opportunity_zone / opportunity_zone_tract_geoid | boolean/string or null | Federal Qualified Opportunity Zone status. |
timezone | string or null | IANA timezone name, e.g. "America/Chicago". |
parcel | object or null | Present only when a parcel-quality geocode found a trustworthy parcel. All 17 parcel_* fields the endpoint’s one paid call already returns. |
parcel.parcel_id / apn / address / area_m2 / geometry / geometry_wkt / owner / zoning / land_use / assessed_value_usd / last_sale_date / last_sale_price_usd / transaction_count / match_type / match_distance_m / match_radius_m / source | — | Parcel detail from the upstream parcel provider. source reports the data TIER (parcel_vendor_paid / parcel_vendor_limited), not the vendor. Premium fields (owner/zoning/land_use/assessed_value_usd/last_sale_*/transaction_count) are null in free-tier mode. |
parcel.interior_point_lat / interior_point_lng | number or null | A point guaranteed to fall inside the matched parcel, derived from its own geometry. Prefer these over the top-level lat/lng for downstream point queries when parcel_grade is false. null when no geometry was returned. |
parcel_unavailable | boolean | true when a parcel was eligible but the lookup didn’t produce one — check parcel_unavailable_reason. |
parcel_unavailable_reason | string or null | Why no parcel is attached. No parcel here: no_parcel_at_point, no_parcel_for_address, parcel_match_too_far. Our own guards (nothing was charged; retry later): parcel_budget_exhausted, parcel_circuit_open. The upstream parcel provider refused or failed: parcel_vendor_quota_exhausted, parcel_vendor_unauthorized, parcel_vendor_forbidden, parcel_vendor_rate_limited, parcel_vendor_upstream_error, parcel_vendor_http_error. Transport/parse: parcel_lookup_transient_error, parcel_lookup_malformed_response, parcel_lookup_failed (a cause with no more specific code). |
match_method | string or null | e.g. geocode_rooftop+point_in_parcel, geocode_range_interpolation+parcel_address_search, map_click. Present on resolved. |
confidence | number or null | One confidence scale throughout — geocode-tier confidence, penalized by match distance when a parcel is attached. Present on resolved. |
parcel_grade | boolean or null | false when the coordinate was interpolated from the street rather than matched to a rooftop — see “Check parcel_grade before re-using the coordinate”. Absent when no geocode ran (coordinate input, clarify, no_match). |
precision_note | string or null | The prose form of a false parcel_grade, safe to show a user. Absent when the coordinate is parcel-grade. |
candidates | array or null | Present only on clarify — up to 3, each with resolved_address, lat, lng, confidence. |
reason | string or null | Present only on no_match — e.g. unaddressed_or_no_match, address_too_coarse, apn_not_supported_in_v1. |
hint | string or null | A next-step suggestion, when there is one. |
Errors
Input-validation failures specific to this endpoint:| Error code | HTTP | Retryable | Meaning |
|---|---|---|---|
resolve_coord_bounds | 422 | no | Coordinate is outside any plausible bound — including the swapped-lat/lng case above. |
resolve_invalid_input | 422 | no | Input didn’t parse as an address, coordinate, or APN-shaped token. |
resolve_busy | 429 | yes | Per-worker overload gate (resolve has its own — separate from /v1/geocode’s). Retry-After: 3. |
resolve_timeout | 504 | yes | Did not complete within the endpoint’s deadline. Retry-After: 5. |
/v1/geocode’s error
taxonomy exactly: address_form_unsupported,
geocode_unconfigured, geocode_forbidden, geocode_timeout,
geocode_upstream_error. A parcel-leg failure is never one of these — see
above, it degrades the response instead.
Limits
- One input per request. No batching in V1; loop client-side.
- 256 characters maximum, rejected rather than truncated.
- Address-leg caching follows
/v1/geocode’s rules: successful geocodes cache 30 days, negatives 24 hours, form-only normalization (case/whitespace/ punctuation) — see/v1/geocode’s Limits for the full detail, since the same cache backs both. - US only, same envelope as the rest of the API.
- Parcel data comes from a commercial upstream provider. There is no free
parcel tier — a parcel is attached only when that lookup succeeds and the
match is close enough to trust; every other case is an honest
parcel_unavailable, not an error.
What we keep
Same retention as/v1/geocode: the
input you send (when it’s an address) is recorded alongside the coordinate it
resolved to, for 30 days, so a result can be audited later. If your use case
cannot accommodate this, resolve client-side and call /v1/fetch or
/v1/ask with lat/lng instead.