Nominatim Geocoding Pipelines Jump to heading

A geocoder that returns an answer for every input is not the same as a geocoder that returns the right answer, and Nominatim is honest about the difference in a way that trips up pipelines built on the assumption that geocoding is a lookup. It is a search engine over OpenStreetMap places, it ranks candidates, and every ranking decision it makes is one your pipeline has to either constrain in advance or evaluate afterwards.

How one address string becomes a ranked list of candidate places An input address string enters a normalisation step that tokenises it and expands abbreviations. The tokens are matched against the place index, producing many candidate places. Each candidate is scored on how completely the address matched, on the place rank of the feature type, and on an importance value derived from the underlying data. The candidates are then ordered and returned, with the caller responsible for deciding whether the top one is good enough. Geocoding is search, and search returns a ranking address string free text or structured normalise tokenise, expand match index many candidates score rank and importance ranked list you pick, not it Nothing in this chain decides that a result is correct — it decides that one candidate outranked the others, which is a different claim.
Treating the first result as the answer is what turns an ambiguous input into a confidently wrong coordinate.

The Problem This Topic Solves Jump to heading

You have a column of addresses, place names or coordinates and you need geometry attached to each one. Nominatim answers both directions — forward, from text to a place, and reverse, from a coordinate to the place containing it — over the same OpenStreetMap data your other pipeline stages already use, which makes it uniquely consistent with the rest of your stack.

The failure scenario is worth stating plainly because it is so common. A pipeline geocodes fifty thousand addresses as free text against the public instance, at whatever rate the client manages, with no country constraint. Three things then happen: the source is blocked within the hour for exceeding the usage policy; the results that did arrive include a number of confident matches in the wrong country, because several place names are not unique globally; and none of it is reproducible, because a rerun a month later returns slightly different coordinates as the underlying map improves. Every one of those three is preventable, and all three prevention measures are cheap.

Prerequisites Jump to heading

Read the parent Querying OSM: Overpass, Nominatim & APIs overview for the quota context. Understand OSM address tagging as covered in Tag Taxonomy & Key-Value Standards, because Nominatim’s structured fields map onto the addr:* namespace and its gaps are the same gaps. And be clear about identity — a geocode returns an OSM object reference, and what that reference guarantees over time is the subject of OSM Feature Identity & ID Stability.

The Ranking Model Jump to heading

Three numbers travel with every Nominatim result, and reading them correctly is most of the skill.

Place rank describes how specific the matched feature is, on a scale that runs from a continent at the coarse end to an individual building or address point at the fine end. A result with a place rank corresponding to a city, returned for a query that supplied a house number, tells you the house number was not matched — the geocoder fell back to the containing settlement rather than failing.

Importance is a normalised score used to order otherwise comparable candidates. It derives from the underlying data’s prominence. It is a tie-breaker between places of similar specificity, not a confidence score, and treating it as a probability that the answer is right is a category error.

The address detail object — returned when you ask for it — decomposes the match into house number, road, suburb, city, state, postcode and country. This is the single most useful part of the response for a pipeline, because it lets you assert that the component you cared about actually matched rather than inferring it from a display string.

Reading a Nominatim result: what each field tells you and what it does not A grid of four response fields against what each one means and the mistake commonly made with it. Place rank indicates how specific the matched feature is and is often mistaken for a quality score. Importance orders comparable candidates and is often mistaken for a confidence probability. The address detail object decomposes the match into components and is often ignored in favour of the display name. The OSM identifier names the matched object and is often assumed to be stable across re-runs. Four fields, four common misreadings What it means Common misreading Use it for place_rank match specificity a quality score detecting fallback importance ordering tie-break a confidence value nothing on its own address object matched components ignored for display_name asserting the match osm_type and id the matched object a stable key re-resolution later The third row is the one that matters: asserting on components is the only way to know which part of the query actually matched.
Every one of these misreadings produces a pipeline that looks like it is validating results while checking nothing.

Structured Queries Beat Free Text Jump to heading

Nominatim accepts a free-text query and does a creditable job of parsing it, but a pipeline almost always knows more about its own data than the parser can infer. Supplying street, city, state, postalcode and country as separate fields removes the guesswork about which token is which, and it makes the failure mode much better: a structured query that cannot match a house number falls back to the street, and you can see that in the returned place rank.

Constraining is equally important. A countrycodes parameter eliminates the entire class of wrong-country matches in one step. A viewbox with bounded mode restricts results to a rectangle. Both cost nothing and both convert a silent correctness problem into an empty result you can handle.

The remaining lever is deduplication before the call. Real address lists repeat heavily, and normalising case, whitespace and punctuation before lookup routinely removes a third of the requests without changing a single result. That is covered in practice in Batch Geocoding with Nominatim Without Getting Blocked, alongside the rate discipline the public instance requires.

Reverse Geocoding and the Zoom Parameter Jump to heading

Reverse geocoding takes a coordinate and returns the place containing it, and its behaviour is governed by a zoom parameter that most integrations leave at the default and then find confusing. Zoom selects the level of the hierarchy you want back: a low value returns a country or state, a middle value returns a city or suburb, and a high value returns a building or address point.

The practical rule is to set zoom from the question, not from the data. “Which country is this point in” and “what is the nearest address to this point” are different questions that happen to share an endpoint, and asking one while expecting the other’s answer is the usual cause of a reverse geocoder that seems to return the wrong level of detail at random.

Validation and Error Handling Jump to heading

Condition Root cause Detection Remediation
Result in the wrong country Unconstrained free-text query address.country_code differs from expectation Pass countrycodes, or a bounded viewbox
House number missing from the answer No matching address point exists place_rank coarser than an address Accept the street-level fallback explicitly, or reject
Empty result for a valid address Over-constrained structured query Zero candidates with all fields supplied Relax one field at a time, most specific first
Different coordinates on a rerun The underlying map improved Coordinate drift beyond a few metres Store the result plus its date; re-resolve on a schedule
HTTP 403 or persistent 429 Missing User-Agent or over one request per second Blocked responses from every request Identify the client, throttle, or self-host
Same place returned for many inputs Inputs normalised too aggressively Many distinct addresses share one result Loosen normalisation; keep house numbers distinct
Plausible but wrong suburb Ambiguous name matched a different feature osm_type is a node where a boundary was expected Assert on the returned address components

Performance, Scale and the Self-Hosting Threshold Jump to heading

The public Nominatim instance permits roughly one request per second from a single source. That is a hard ceiling on throughput, and it means a hundred thousand addresses is somewhere over a day of continuous, polite requesting — which is both slow for you and a poor use of a shared resource.

Three things extend the useful range. Deduplicate before calling, as described above. Cache every result keyed on the normalised query, so reruns are free and your test suite is deterministic. Batch the work into stages so that a failed run resumes from where it stopped rather than starting over.

Past roughly the ten-thousand-lookup mark, the right answer stops being a better client. Importing your own instance from a regional extract removes the rate limit entirely, makes results reproducible until you choose to re-import, and lets you tune the address indexing to your region. It is a real operational commitment — the import is long and the database is large — and Importing Nominatim from an OSM Extract sizes it honestly.

How a geocoding workload's right answer changes with volume A progression across four volume bands. Up to a few hundred lookups the public instance with a polite client is entirely appropriate. Into the low thousands, deduplication and caching are what keep it viable. Approaching ten thousand, the one request per second ceiling makes the run take hours and a private instance starts to pay for itself. Beyond that, a self-hosted import is the only approach that is both fast and reproducible. The right answer changes twice as volume grows hundreds public instance a polite client is enough thousands dedupe and cache still public, still fine ten thousand hours per run self-hosting starts to pay beyond private import fast and reproducible The threshold is not about politeness alone: past a few hours per run, reproducibility and restartability matter more than the rate limit.
Most teams cross the second threshold long before they notice, because a slow nightly job does not feel like a problem until it fails halfway.

Failure Modes and Gotchas Jump to heading

  • The first result is not the answer. It is the highest-ranked candidate. Assert on the returned address components before accepting it.
  • Place rank reveals fallback. A city-level rank for a query with a house number means the house number did not match. That is information, and discarding it means storing a city centroid as if it were a building.
  • Importance is not confidence. Two results with importance 0.4 and 0.3 are not “57% likely” and “43% likely”; they are simply ordered.
  • Accents and abbreviations matter. Normalisation helps, but stripping accents can merge genuinely distinct places. Normalise conservatively and keep the original string.
  • Coordinates move. A building’s centroid changes when a mapper traces it more accurately. Store the date alongside every geocode.
  • Reverse geocoding without zoom is ambiguous. Set it from the question you are asking, not from the default.
  • Postcodes are not universally mapped. In some countries the postcode in a Nominatim result is inferred from a containing area rather than from the feature itself.

Integration Points Jump to heading

Geocoded output feeds two very different consumers. When the result is a coordinate to be stored, it belongs in the same normalized feature table as everything else, and the composite osm_type/osm_id key is what links it back to the map — the same key discussed in Converting Overpass JSON to a GeoDataFrame. When the result is a match between your record and an OSM feature, it is the first step of a conflation workflow, and it should be scored and audited the way OSM Conflation & Data Enrichment describes rather than accepted wholesale.

Upstream, address strings should be cleaned before they reach the geocoder, using the same value-standardisation discipline as any other OSM-adjacent field; Value Standardization & Regex Cleaning covers the techniques.

Guides in This Topic Jump to heading

Frequently Asked Questions Jump to heading

Why does Nominatim return a city when I asked for a street address?

Because no matching address point or road existed, so the geocoder fell back to the most specific containing feature it could match. The returned place rank tells you exactly what level it settled on, which is why it should be read on every result. Storing a city-level fallback as though it were a building coordinate is one of the most damaging silent errors in a geocoding pipeline, and a single assertion on place rank prevents it.

Is the importance score a confidence value?

No. It orders candidates that are otherwise comparable and derives from how prominent a place is in the underlying data, not from how well it matched your query. A high importance on a wrong match is entirely possible and entirely normal — a famous city will outrank an obscure village of the same name regardless of which one you meant. Confidence has to come from asserting that the address components you supplied appear in the result.

How do I stop getting results from the wrong country?

Constrain the query. Passing a country code restriction eliminates the whole class of problem in one parameter, and a bounded view box does the same for a smaller area. Free-text queries without any constraint are asking the geocoder to guess which of several real places sharing a name you meant, and it will guess based on prominence, which has no connection to your intent.

At what volume should I run my own instance?

Somewhere around ten thousand distinct lookups, though the honest trigger is time rather than count. The public instance’s roughly one request per second ceiling means ten thousand lookups is several hours of continuous requesting, at which point a failure halfway through is expensive and reproducibility is impossible. A private import removes the rate limit, makes results stable between re-imports, and turns a slow nightly job into a fast one.

Why do my geocodes drift between runs?

Because the underlying map changed. A building traced more accurately moves its centroid; a point replaced by a polygon changes both the coordinate and the object identifier. That drift is the map improving, not the geocoder being unreliable, but a pipeline that assumes stability will see it as churn. Store the coordinate, the object reference and the resolution date together, and re-resolve deliberately on a schedule you control.

Up one level: Querying OSM: Overpass, Nominatim & APIs.