Skip to main content

When to use this

You usually do not need this endpoint. /v1/fetch and /v1/ask both accept an address directly and echo the same quality information back in a geocode block. Reach for /v1/geocode when you want the coordinate on its own — to inspect it before spending a fetch, to cache it yourself, or to reuse it across many calls.
Every other endpoint takes lat/lng. This one turns a street address into that coordinate, and tells you how it was derived. Geocoding is lossy — a coordinate can land on the wrong parcel — so wherever you use it, the quality signal travels with the answer. As a standalone call you see accuracy_type before doing anything else; via address on /v1/fetch you get the same fields in the response’s geocode block.

Request shape

The parser is Geocodio’s, so ordinary formatting variation is fine — Pkwy/Parkway, N/North, NY/N.Y., extra whitespace, mixed case. What it cannot do is guess a locality: a bare "706 Ocean Pkwy" with no city, state, or ZIP is ambiguous nationwide and comes back 404 address_too_coarse.

Example: a rooftop match

Read accuracy_type before you trust the coordinate

This is the field that matters. It says how the coordinate was derived, and the difference is the difference between the right property and a neighbour’s. range_interpolation is the one to watch. It assumes house numbers are evenly spaced along the block, which holds in a dense grid and breaks in the countryside. Measured against authoritative county parcel polygons in North Carolina: every rooftop result fell inside its own parcel with a median error of 0 m, while interpolated results fell outside, one of them by 1.1 km. In rural areas the 95th-percentile error is about 2,872 m — several properties over. Here is what that looks like. Note that the request said Kyle, TX and the answer is in San Marcos:
The result is honest — accuracy_type and normalized_address both tell you what happened — but a caller that reads only lat/lng would silently underwrite the wrong site. A reasonable rule:
  • rooftop / nearest_rooftop_match — safe for parcel-level work.
  • anything else — treat as approximate. Show the user normalized_address and let them confirm before you act on it.

Vague addresses are rejected, not guessed

An address the geocoder can only place at a ZIP, city, county, or state centroid returns 404 address_too_coarse instead of a coordinate. This is worth stating plainly because the upstream behaves differently: given a nonexistent address, Geocodio does not report “no match” — it answers 200 with the nearest city centroid. Passing that through would hand you a confident-looking coordinate for an arbitrary property near a town centre, and every parcel-clipped field downstream would describe the wrong land, with citations.
Retrying will not help — the upstream’s answer is stable. Ask the user for a street number.

Two more ways a request is refused

Beyond the centroid floor above, a result is rejected when it is precise but about the wrong place. These are different failures and need different gates — accuracy_type says how precisely a match was placed, accuracy says whether the right thing was matched at all. Low confidence. Below 0.8 similarity (the provider’s own caution threshold) the match is a guess, so it is refused rather than returned. "1 Rue de Rivoli, Paris, France" used to come back as a Virginia coordinate at 0.65. Override with MIREYE_GEOCODE_MIN_SIMILARITY if you have a reason to. Outside the US. We do not ask the provider to force a US match. Doing so does not filter to US addresses — it asks for the best US thing available, which is how "10 Downing Street, London, UK" became a rooftop-grade coordinate in Charleston, West Virginia. Unforced, that query correctly returns nothing, and a result that does come back non-US is refused. Both surface as the same 404 codes documented under Errors.

Known limitation: US territories

Coverage in Puerto Rico, Guam, the US Virgin Islands, American Samoa and the Northern Marianas is thin — and unlike everywhere else, the provider’s confidence score does not reflect that. Measured 2026-07-26:
Different street, different ZIP, different town — returned at near-perfect confidence. Every guard on this page passes these. They sit inside the US envelope, above the similarity floor, and the country component says US. We have not added a heuristic for it, deliberately. Every rule that catches these also rejects legitimate mainland addresses: a real rural Texas address that correctly resolves to a neighbouring town shares fewer tokens with its match than the wrong Puerto Rico one does with its. Trading real addresses for territory ones is the worse deal. So this check is yours, and it is the ordinary one: compare normalized_address against what your user typed. For territory addresses, treat that as required rather than advisory. It is not uniform. A San Juan address on a well-covered street resolved correctly at 0.91, and the US Virgin Islands, American Samoa and the Northern Marianas refuse cleanly rather than guessing. Puerto Rico and Guam are where a wrong answer can look right.

Response shape

The fallback, and why provider matters

If the primary provider exceeds its 3 s deadline, we fall back to the free US Census geocoder rather than failing the request. That fallback fires on timeout only — never on a missing key, an auth failure, or a no-match, because those must surface rather than be papered over with a lower-precision answer. The fallback is a genuine degradation, not an equivalent. In the same North Carolina measurement it landed inside the correct parcel zero times out of nine and missed 6 of 15 addresses entirely. It always reports accuracy_type: "range_interpolation", and provider: "census" is your signal that you are on the degraded path.

Errors

Every retryable failure carries a Retry-After header; non-retryable ones deliberately do not. A 1–256 character violation is a FastAPI 422, as elsewhere in the API — see Errors.

Limits

  • One address per request. No batching in V1; loop client-side.
  • 256 characters maximum, rejected rather than truncated.
  • Successful results are cached for 30 days; “no such address” for 24 hours. A repeat lookup of the same address within the window is free and instant, and the cache expires on the same 30-day clock as everything else we keep — this page used to say “a year” here while promising 30 days two sections down, and both could not be true. The cache key is normalized for form only — case, spacing, accents and punctuation, except -, # and /, which carry meaning (12-34 Queens house numbers, # 4 units, 123 1/2 half-addresses) — so 706 Ocean Pkwy and 706 OCEAN PKWY are one entry. It deliberately does not treat Parkway and Pkwy as the same word: doing that safely requires knowing which token is the street suffix, and guessing merges real streets whose name is a suffix word (Northern Blvd would collapse into N Blvd). Two spellings of one address therefore cost two lookups. That is the intended trade — a duplicate lookup is cheap, a merged cache entry would hand you a different property’s coordinate. A negative is deliberately short-lived: an address the provider has not ingested yet starts working the moment it does, and a month-long 404 would outlast the truth by months.
  • Worst case 5.5 s (3 s primary + 2.5 s fallback). Set your client timeout above that.
  • US only. Feed the coordinate to /v1/fetch or /v1/ask, both of which enforce the US envelope.

What we keep

The address you send is recorded, alongside the coordinate it resolved to and the accuracy grade that produced it. We keep it so a result can be checked later. If you tell us a lookup returned the wrong data for one of your addresses, the record of what you actually sent is the only thing that can settle it — with just a coordinate and a result, there is no way to tell a bad match from a typo. Concretely, per request we store the address as you typed it, the resolved lat/lng, and the geocode block including accuracy_type and provider. Retention is 30 days, everywhere the address lives. Two stores hold it and both expire on the same clock:
  • The audit record (address as typed, coordinate, geocode block) sits in monthly partitions; a scheduled job drops each one once its whole month is past the cutoff, so a record lives 30 to about 60 days depending on when in the month it was created.
  • The result cache (the matched address and coordinate, keyed by a digest of your input, kept so a repeat lookup does not re-bill) expires at 30 days via the store’s own TTL — no job involved, so it cannot quietly stop.
Nothing is kept beyond that. The two are linked, and the API enforces both ends itself. It removes expired address fields from its own records on a rolling check — the removal does not depend on any scheduled job staying configured — and if it ever cannot remove them, it stops recording addresses until it can. We keep the address because it expires; the component that accepts it is the component that deletes it. Two things we deliberately do not do. We do not store a hash of your address next to it — over an address space this small a hash is a lookup key, not anonymity, so it would add exposure while implying the opposite. And the address is held in one structured field rather than copied into logs or stored upstream URLs. If your use case cannot accommodate this, geocode client-side and call /v1/fetch or /v1/ask with lat/lng instead — a coordinate request stores no address, because none was sent.