Skip to content

Geocoding Accuracy: What Rooftop, Interpolated and ZIP-Level Actually Mean

9 min read · updated August 11, 2026

Two geocoders can return the same address at points 400 metres apart and both be behaving as documented. The difference is the match level, and the match level is a statement about where the coordinate was manufactured — not an error bar.

What the levels name

Google’s Geocoding API returns a geometry.location_type on every result, and its geocoding request documentation defines four values. ROOFTOP means the result is “a precise geocode for which we have location information accurate down to street address precision”. RANGE_INTERPOLATED means the result “reflects an approximation (usually on a road) interpolated between two precise points”. GEOMETRIC_CENTER is “the geometric center of a result such as a polyline (for example, a street) or polygon (region)”. APPROXIMATE is everything else.

Other geocoders name the same idea differently. Esri returns Addr_type with values such as PointAddress, StreetAddress, StreetName and Postal. The US Census geocoder distinguishes a TIGER line-segment match from a match against its address-point file. The vocabulary differs; the underlying taxonomy does not, because there are only a few places a coordinate can come from: a surveyed point for that specific address, a position computed along a road segment, or the centre of some area the address falls inside.

The critical thing about all of them is what they are not. None of these levels is a published error radius. A vendor telling you a match is interpolated is telling you the derivation, and you have to do the rest of the work yourself. That work is arithmetic, and it is worth doing once for your own data.

One address, interpolated

Take 150 Fourth Street, on a block whose even side runs 100 to 198. The road centreline for that block is a polyline 180 metres long. An interpolating geocoder does exactly what the name says: it places the house number linearly along that line.

fraction  = (150 - 100) / (198 - 100) = 0.5102
along     = 0.5102 x 180 m               = 91.8 m from the block start
offset    = 10 m perpendicular, right side (to get off the centreline)

Now the rooftop answer for the same address. The parcel’s structure centroid sits 108 metres along the block and 22 metres back from the centreline. The two answers differ by

dx = 108 - 91.8 = 16.2 m
dy = 22 - 10    = 12.0 m
d  = sqrt(16.2^2 + 12.0^2) = sqrt(262 + 144) = sqrt(406) = 20.1 m

Twenty metres, on a well-behaved urban block. That is fine for a choropleth and useless for anything that has to know which building the driver stops at.

The interpolation makes one assumption, and it is the assumption that breaks: that house numbers are evenly spaced along the segment. Suppose the first 40 metres of that block is a terrace numbered 100 to 130 and the remaining 140 metres holds four large lots numbered 132 to 198. Number 130 truly sits at 40 m but interpolates to (130 − 100)/98 × 180 = 55 m, a 15 m error; number 140 truly sits near 75 m and interpolates to 73 m. Every number on the block is displaced, in a pattern that depends on land use rather than on anything the geocoder can see.

The error is bounded by the segment length, which is why interpolation degrades so badly outside towns. A rural road segment carrying numbers 1 to 999 over 4 kilometres puts every address somewhere on a 4 km line. Half a kilometre of error is routine there, and no field in the response distinguishes that case from the 20 m urban one — both come back as an interpolated match.

What a postcode centroid costs you

“ZIP-level” is the level people underestimate most, because it sounds like a small area and its accuracy depends entirely on which country you are in.

A US ZIP code is not a polygon at all. It is a set of mail delivery routes; the polygon you actually use is a Census ZCTA, an approximation built by assigning census blocks to the ZIP that dominates them. Treat a ZCTA of area A as a disc of the same area and the geometry gives you the expected error directly. For a uniform disc of radius r, the mean distance from the centre is 2r/3:

Urban ZCTA, A = 30 km^2
  r    = sqrt(30 / pi)  = 3.09 km
  mean = 2 x 3.09 / 3   = 2.06 km
  max  =     3.09 km

Rural ZCTA, A = 500 km^2
  r    = sqrt(500 / pi) = 12.6 km
  mean = 8.4 km

Those are assumptions made visible, not measurements: real ZCTAs are not discs and population is not uniform inside them, which usually makes the population-weighted error smaller than the geometric one. The order of magnitude is the point. A ZIP centroid is a kilometres- scale answer, and it is the same kilometres-scale answer for every address in the ZCTA, which is worse than it sounds — the errors are perfectly correlated, so averaging over many addresses does not cancel them.

Elsewhere the same word means something else entirely. A UK postcode unit such as SW1A 1AA covers on the order of fifteen delivery points, so a unit-postcode centroid is a rooftop-grade answer in everything but name. A Dutch six-character postcode typically covers one side of one street. “Postcode accuracy” is therefore not a tier at all; it is a tier per country, and a pipeline that treats them alike will be two orders of magnitude wrong in one country and right in the next.

The failures that have no level

A geocoder almost always returns something, and the dangerous results are the ones with a confident-looking level attached.

  • Partial matches. Google sets partial_match when only part of the request matched — typically a street that does not exist in the stated locality, or a misspelling the service resolved to a different street. The result still carries a location_type, and that level describes the thing that was matched, not the thing you asked for.
  • Dropped subpremise. Apartment 12B geocodes to the building. That is correct and it is often what you want, but the level reads as rooftop and the coordinate is shared by two hundred households.
  • New construction. An address that does not exist in the reference data yet falls back a level silently — to the street, then to the locality. Nothing in the response says “this address is unknown”; you get a legitimate coordinate for a larger thing.
  • PO boxes and care-of addresses. These geocode to the sorting facility. It is a real building, so a rooftop-grade level is defensible and completely misleading if you are computing distance to a customer.

Carrying the level through the pipeline

The single most useful habit is to store the level in the same row as the coordinate and never let them separate. A latitude and longitude with no provenance column is indistinguishable from a good one three joins later.

From there, set the threshold per use rather than globally. Last-mile dispatch needs a point good to roughly the width of a building, so rooftop or nothing, with a fallback to a human. A demand model aggregated to a census tract tolerates interpolated matches, because 20 m of error rarely crosses a tract boundary — but it does not tolerate ZIP centroids, which pile every address in the ZCTA onto one tract. Choose the tier by asking what boundary the error has to stay inside, and compare that boundary to the arithmetic above.

When you need an error estimate rather than a level, the only one you can actually compute is disagreement: geocode a sample through a second, independent provider and measure the distance between the two answers. Agreement to a few metres is strong evidence of a real address point in both reference datasets; disagreement of hundreds of metres tells you at least one of them fell back to an area. That sampled distribution is worth more than any vendor accuracy claim, and it is the input you want before deciding how much to spend geocoding at volume. Where the same building arrives from two sources under two spellings, the matching problem comes first — see address matching and normalisation.

The location_type values and the partial_match semantics quoted here are Google’s documented behaviour at the time of writing. Match-level vocabularies are vendor-defined and do change; check the provider’s current reference before hard-coding a string comparison against them.