Skip to main content

The two architectures

The question

Two geocoders answer the same question. You type an address, they give you a point. One of them recommends 128 GB of memory before you start. The other is an npm install and a file on disk. They are not doing the same amount of work, and the difference is not that one is written better. It is that there are two ways to build a geocoder, and they put the work in different places.

This page is about those two ways: what each one is, what each is good at, and what you are buying when you pick one.

The analog

You are at the front desk of a large building and you want to find someone. There are two ways to ask.

The first: hand the receptionist everything you know — a half-remembered name, a floor, the word "accounts" — and have them search every directory in the building for anything resembling any of it, then read you the best matches in order. This works remarkably well. It works even when half of what you said was wrong, even when you gave them a nickname, even when what you wanted was not a person at all but a room. The cost is that the receptionist has to hold every directory in the building open at once, and has to be good at ranking, because everything matches a little.

The second: read what you wrote, work out that "accounts" is the department and the rest is a person's name, walk to the accounts department's own list, and look up the name there. Fewer pages get opened. The answer arrives with its reasoning attached — you know it was the accounts list, so you know what the answer means. The cost is that somebody had to understand your note first, and if they read "accounts" as a surname they will go to the wrong list and come back with nothing at all, rather than with something approximate.

Every geocoder is one of those two receptionists.

Architecture one: search engine with a map

Take every place you know about — countries, regionsregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality., towns, neighborhoods, streetsstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels., address pointssitus dataA dataset of exact address-point coordinates (rooftop-level). Mailwoman's geocoder uses a national situs layer (124.9M US points built from state address-point sources) as the highest-precision tier of the geocode cascade., points of interest — and turn each one into a document in a full-text searchFTS5SQLite's built-in full-text-search module (with BM25 ranking). Mailwoman uses it for prefix and name matching against the gazetteer. index. Give each document its name, its alternate names in every language, its parents, and its coordinate. At query time, throw the user's whole string at the index and rank what comes back.

Nothing in this design labelscomponent tagOne of the 25 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag. your string before the lookup, which is the point. Some engines do run a query parser first — PeliasPeliasAn open-source geocoder, Mailwoman's spiritual predecessor. shapes its queries with libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it., which is where the 4 GB in its row below comes from — but that parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. shapes the query rather than deciding the answer, and the ranking is still against one index over everything. The system never decides that Springfield is a localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighborhood in the hierarchy.; it decides that some documents score well against the tokentokenOne word or subword in the tokenized input. For the neural classifier, tokens come from SentencePiece (subword units); for the rule classifiers, tokens are whitespace- and punctuation-separated words. springfield and ranks them. Ambiguity is not resolved, it is surfaced as a result list. Adding a countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. means adding documents. Adding a language means adding name variants to documents that already exist. No grammar is written for either.

This is the architecture behind Nominatim, Photon and PeliasPeliasAn open-source geocoder, Mailwoman's spiritual predecessor., and behind the tantivy-based Airmail. The engines differ — a relational database with spatial extensions in one case, OpenSearch in another — but the shape is the same: one index over everything, queried as text.

What it is good at, and this is not a consolation prize:

  • Fuzzy recallrecallOf the spans whose gold label is a given tag, the fraction the model found. High recall means few misses. Paired with precision to compute F1. over a huge corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic shards (boundary stress, order variants, negative space). Managed by @mailwoman/corpus.. A misspelled name, half an address, a landmark instead of a streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. — the index still returns something ranked, because scoring is continuous. A design that parsesaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. first has nothing to look up when the parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. fails.
  • Inputs that are not addresses. "The station", "castle museum", a venuevenueA named, non-address place — a business, building, park, or stadium. Mailwoman's free-text point-of-interest component, added as a Tier 2 fine label. name with a toponymtoponymA proper name for a geographic place. inside it. These are documents in the index like any other. They are not addresses and a parser has no labelcomponent tagOne of the 25 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag. for them.
  • Every script, immediately. The index stores text. A Korean place nametoponymA proper name for a geographic place. and a Polish one are the same kind of document, and neither needed a localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for. rule written for it.
  • A ranked list is the right shape for autocomplete. A search box wants five plausible completions, not one confident answer, and this architecture produces five by construction.

What it costs. The ranking happens against the whole corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic shards (boundary stress, order variants, negative space). Managed by @mailwoman/corpus. at query time, so the corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic shards (boundary stress, order variants, negative space). Managed by @mailwoman/corpus. wants to be resident and the ranking function has to be tuned against all of it at once — a change that helps one countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head.'s recallrecallOf the spans whose gold label is a given tag, the fraction the model found. High recall means few misses. Paired with precision to compute F1. can cost another's precisionprecisionOf the spans the model labeled as a given tag, the fraction it got right. High precision means few false positives. Paired with recall to compute F1. without announcing itself, and you find out from the aggregate. And the answer is a document, not a decomposition: you learn which record matched, not which part of your string matched what. If you needed to know that Ave was a street suffixstreet affixA modifier on a street name indicating type or direction — Street, Avenue, rue, Calle, N, East. Mailwoman tags these as street_prefix / street_suffix, recognized via a morphology FST. rather than part of a venuevenueA named, non-address place — a business, building, park, or stadium. Mailwoman's free-text point-of-interest component, added as a Tier 2 fine label. name, this architecture never formed that opinion.

Architecture two: understand, then look up

Labelcomponent tagOne of the 25 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag. the string first. Decide that this spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. is a house numberhouse numberThe numeric or alphanumeric identifier of a building on a street. Mailwoman's house_number component; its position relative to the street name flips between locales., this one a streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels., this one a localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighborhood in the hierarchy., this one a postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. — without consulting any place database, because that decision is about how the words are being used, not about which places exist. Then run one constrained lookup per labeled spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. against a gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture.: not "what matches this string" but "what localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighborhood in the hierarchy. carries this name, inside this regionregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality.".

The lookups are cheap because they are constrained. A localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighborhood in the hierarchy. probe scoped to a resolved regionregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. never considers the rest of the planet, so the index it hits is a small indexed read rather than a ranked scan. That is the property that lets the data stay on disk: the query touches a handful of pages instead of the corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic shards (boundary stress, order variants, negative space). Managed by @mailwoman/corpus..

Mailwoman is this architecture. So, in part, is any pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. built on libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it.libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it. is the labeling half without the lookup half, which is why it is a parser rather than a geocoder.

What it is good at:

  • A decomposition, not a match. You get back which piece of the input was the streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. and which was the town, with offsets into your original string. Anything downstream that needs fields rather than a point — deduplicationrecord matchingThe process of determining whether two database records refer to the same real-world entity. Mailwoman's matcher uses a geocode-first approach (match the resolved place, not the address string) with Fellegi-Sunter probabilistic scoring., validation, formatting, record matchingrecord matchingThe process of determining whether two database records refer to the same real-world entity. Mailwoman's matcher uses a geocode-first approach (match the resolved place, not the address string) with Fellegi-Sunter probabilistic scoring. — needs exactly this.
  • Answers that carry their own provenance. Each resolved spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. names the gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. record it landed on, so a wrong answer can be traced to the spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. that went wrong.
  • A small resident footprint. The modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' is a file; the gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. is a sealed read-only database read from disk.
  • The scope of the data is yours to choose. CoveragecoverageThe fraction of a population or region for which a data source has real, non-placeholder entries — e.g. 47% rooftop coverage on Texas addresses. Distinct from accuracy on the rows that are present. is a set of downloads rather than a property of the install, so a US-only deployment carries US-sized artifacts.

What it costs. Someone has to train the labeler, on a labeled corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic shards (boundary stress, order variants, negative space). Managed by @mailwoman/corpus., per localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for. — and where that corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic shards (boundary stress, order variants, negative space). Managed by @mailwoman/corpus. is thin, the labelscomponent tagOne of the 25 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag. are wrong in a way no amount of gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. data repairs. When the labeling goes wrong the failure is quiet and total: a spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. labeled street that was a localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighborhood in the hierarchy. gets looked up in the streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. index, finds nothing, and the result comes back empty rather than approximately right. The search architecture would have returned something. And a query that is not an address at all has no useful labeling, so it has no useful lookup.

What the hardware numbers measure

Each project publishes what it wants you to provision. The figures below are quoted from each project's own documentation and were read on 2026-08-04; where a project publishes no figure, the row says so rather than estimating one.

EngineIndexPublished requirementSource
Nominatim 5.3.2PostgreSQL, over the OpenStreetMapOpenStreetMap (OSM). A community-curated global map database (ODbL-licensed) with addr:* tagged features and place hierarchies. A secondary corpus source and a source of street names. planet"A minimum of 2GB of RAM is required or installation will fail. For a full planet import 128GB of RAM or more are strongly recommended." "For a full planet install you will need at least 1TB of hard disk space."Nominatim installation
PhotonOpenSearch, embedded by default"A planet-wide database requires about 95GB disk space (as of 2026, grows by about 10% a year)." "At least 64GB RAM are recommended for smooth operations…"Photon README
AddokRedisNo hardware figure is published. The deployment behind France's national address registerinput modeThe Decision-A register switch: 'fragmented' (human-typed fragments — feeds the evidence channels) vs 'formatted' (complete records — runs the trained absence identity). Explicit on CLI/API; per-endpoint defaults (batch→formatted, autocomplete→fragmented); kind-derived otherwise. is described as "26+ million addresses indexed", "~2000 searches/second", "~15 minutes full import time".Addok README
PeliasPeliasAn open-source geocoder, Mailwoman's spiritual predecessor.ElasticsearchThe published requirements document covers software dependencies rather than hardware. Its one size figure is that "LibpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it. requires about 4GB of disk space to download all the required data."Pelias requirements
Mailwoman 8.6.0Sealed SQLite files, read from disk707 MB installed, of which 523 MB is the ONNX runtimeONNX (Open Neural Network Exchange). An open format for machine learning models that enables interoperability between training frameworks and inference runtimes. Mailwoman ships its trained model as an ONNX file so it can run in Node.js and the browser via onnxruntime.; the modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' itself is 39.4 MB. The global candidate gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. is a 1.65 GB download. US rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few meters — the highest tier of the geocode cascade. Sourced from address-point and situs data. and interpolationinterpolationA geocoding technique that estimates a coordinate along a street segment based on the house number range. Used as the middle tier of Mailwoman's geocode cascade when exact address-point data is unavailable. coveragecoverageThe fraction of a population or region for which a data source has real, non-placeholder entries — e.g. 47% rooftop coverage on Texas addresses. Distinct from accuracy on the rows that are present. is 41.3 GB across 103 artifacts, pulled per state.Footprints

Four things are worth saying about that table before anyone reads a verdict into it.

The rows are not measuring the same thing. Nominatim's and Photon's figures are for a planet-scale install and are recommendations for smooth operation under load. Mailwoman's are measured install and download sizes, on a page that says plainly that runtime memory is measured per workload elsewhere and is not re-measured there. A missing number is not a small number.

Addok is the row that breaks the dichotomy, and it earns its place for that. It is a search-shaped index — Redis, ranked matching, no parser — and it has no memory problem, because it indexes one countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head.'s address registerinput modeThe Decision-A register switch: 'fragmented' (human-typed fragments — feeds the evidence channels) vs 'formatted' (complete records — runs the trained absence identity). Explicit on CLI/API; per-endpoint defaults (batch→formatted, autocomplete→fragmented); kind-derived otherwise. rather than a planet. Scope the corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic shards (boundary stress, order variants, negative space). Managed by @mailwoman/corpus. down and the architectural cost of the search design mostly disappears. If your addresses are all in one countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. and that countrycountryThe top-level address component (an ISO country). Closed-vocabulary, so it is best handled by a deterministic matcher feeding a proposal rather than a retrained model head. publishes a registerinput modeThe Decision-A register switch: 'fragmented' (human-typed fragments — feeds the evidence channels) vs 'formatted' (complete records — runs the trained absence identity). Explicit on CLI/API; per-endpoint defaults (batch→formatted, autocomplete→fragmented); kind-derived otherwise., that is a serious answer and the rest of this page does not argue against it.

Disk is not free either. Mailwoman's US rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few meters — the highest tier of the geocode cascade. Sourced from address-point and situs data. tier is 41.3 GB if you take all of it. The trade is not "small versus large", it is that the large part is optional, per-state, and idle until queried.

Both designs are paying for the same thing, in different currencies. GeocodinggeocodingThe process of converting an address into geographic coordinates (latitude and longitude). Mailwoman geocodes in a multi-tier cascade: exact address-point match → street interpolation → locality centroid. Each tier is progressively coarser but more widely available. needs the world's places somewhere it can reach them. The search design reaches them by ranking, which wants them resident. The labelcomponent tagOne of the 25 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag.-then-look-up design reaches them by constrained probe, which lets them stay on disk — and pays for that constraint with a modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' and a labeled corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic shards (boundary stress, order variants, negative space). Managed by @mailwoman/corpus. per localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for., which is a cost that arrives before you ever run a query and shows up on no hardware page at all.

Where the verdict lands

Not on a hardware table. It lands on what your input looks like.

If your queries are exploratory — search boxes, partial strings, landmarks, "the thing near the station" — the search architecture is doing the job it was designed for, and a parser is the wrong tool because there is frequently nothing to parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates.. If your queries are addresses that arrived from a form, a CRM export or a delivery manifest, and what you need back is fields as much as a point, the labeling architecture is doing the job it was designed for, and a ranked document list is an answer you will have to post-process into fields anyway.

The two also compose. A pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. can labelcomponent tagOne of the 25 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag. first, take the constrained lookup when it succeeds, and fall back to a ranked full-text searchFTS5SQLite's built-in full-text-search module (with BM25 ranking). Mailwoman uses it for prefix and name matching against the gazetteer. over the raw string when the labeling produces nothing usable. That is a real design and it inherits both sets of costs.

  • What geocoding is — the job both architectures are doing.
  • The landscape — hosted APIs, self-hosted engines, and in-process libraries.
  • Gazetteers — the place database underneath both designs.
  • Footprints — Mailwoman's measured sizes, with the commands that produced them.
  • Swap in for Nominatim — running the labelcomponent tagOne of the 25 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag.-then-look-up design behind a Nominatim-compatible endpoint.