Skip to main content

The two architectures

The question​

Two geocoders answer the same question: you type an address, and they return 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 do different amounts of work, and code quality does not explain the difference. There are two ways to build a geocoder, and they do the work in different places.

This page describes those two designs: how each works, what each is good at, and what you get when you pick one.

The analog​

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

In the first, you give the receptionist everything you know, such as a half-remembered name, a floor and the word "accounts". The receptionist searches every directory in the building for anything resembling any of it and reads you the best matches in order. This works well even when half of what you said was wrong, when you gave a nickname, or when you wanted a room instead of a person. The cost is that the receptionist must keep every directory in the building open at once and must rank well, because everything matches a little.

In the second, the receptionist reads your note, works out that "accounts" is the department and the rest is a person's name, goes to the accounts department's own list, and looks up the name there. Fewer pages get opened. The answer comes with its reasoning: you know it came from the accounts list, so you know what it means. The cost is that someone must understand your note first. If they read "accounts" as a surname, they go to the wrong list and come back with no entry at all. You do not even get an approximate answer.

Every geocoder works like one of those two receptionists.

Architecture one: search engine with a map​

This design takes every known place (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 turns 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. Each document gets its name, its alternate names in every language, its parents, and its coordinate. At query time, the engine sends the user's whole string to the index and ranks the results.

This design deliberately does not 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. your string before the lookup. 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.. libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it. accounts for the 4 GB in its row below. 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 without deciding the answer, and the ranking still runs 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 finds documents that 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. The system does not resolve ambiguity. It returns the candidates as a result list. To add 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., the index needs more documents. To add a language, it needs name variants in existing documents. Neither change requires a grammar.

Nominatim, Photon and PeliasPeliasAn open-source geocoder, Mailwoman's spiritual predecessor. use this architecture, and so does the tantivy-based Airmail. The storage engines differ (a relational database with spatial extensions in one case, OpenSearch in another), but the design is the same: one index over everything, queried as text.

What it is good at. These strengths are real advantages:

  • 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 extracts (boundary stress, order variants, negative space). Managed by @mailwoman/corpus.. For a misspelled name, half an address, or 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 a ranked result, 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 no 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. 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", or 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 containing a place nametoponymA proper name for a geographic place. 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 needs a localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for. rule.
  • A ranked list suits autocomplete. A search box needs five plausible completions instead of one confident answer, and this architecture always produces a ranked list.

What it costs. The system ranks 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 extracts (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 extracts (boundary stress, order variants, negative space). Managed by @mailwoman/corpus. needs to be in memory, and the ranking function must be tuned against all of it at once. A change that improves 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 reduce 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 any warning, and you find out only from the aggregate scores. The answer is also a matched document without a breakdown of your input. You learn which record matched, but you do not learn which part of your string matched what. If you need to know whether 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. or 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 determines that.

Architecture two: parse, then look up​

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. the string first. It decides that one 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., another 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., another a localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighborhood in the hierarchy. and another a postcodepostcodeThe country-specific postcode (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 depends on how the words are used instead of on which places exist. It then runs 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.. Instead of asking "what matches this string", each lookup asks "which localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighborhood in the hierarchy. has 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. lookup 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 it performs a small indexed read instead of a ranked scan. This is why the data can stay on disk: the query reads a handful of pages instead of the whole corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic extracts (boundary stress, order variants, negative space). Managed by @mailwoman/corpus..

Mailwoman uses this architecture. Any pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-hint → 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. uses part of it. libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it. provides the labeling half without the lookup half. That is why it is a parser and not a geocoder.

What it is good at:

  • A breakdown of the input as well as a match. The result reports 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. Any downstream task that needs fields instead of 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 record their source. 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. identifies 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 matched, so you can trace a wrong answer 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 caused it.
  • A small memory 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, and 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.
  • You choose the scope of the data. 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 that you choose separately from the install, so a US-only deployment stores only US-sized files.

What it costs. Someone must 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 extracts (boundary stress, order variants, negative space). Managed by @mailwoman/corpus. for each localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for.. Where that corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic extracts (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, and 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 them. When labeling goes wrong, the result is empty and no field flags the error. 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 really a localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighborhood in the hierarchy. is 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, matches no record, and returns an empty result instead of an approximate one. The search architecture would have returned something. 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 recommends you 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 states the absence instead of 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 2.88 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

Keep four points in mind before you draw a conclusion from that table.

The rows measure different things. Nominatim's and Photon's figures are for a planet-scale install and are recommendations for smooth operation under load. Mailwoman's figures are measured install and download sizes. The Footprints page states that runtime memory is measured per workload on other pages and is not re-measured there. A missing number is not a small number.

Addok combines traits of both categories. That is why it is in the table. It uses a search-style index (Redis and ranked matching, without a 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. instead of the whole planet. When the corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic extracts (boundary stress, order variants, negative space). Managed by @mailwoman/corpus. is scoped down, the architectural cost of the search design mostly disappears. If all your addresses are 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., Addok is a strong option, and this page does not argue against it.

Disk space also costs something. 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 download all of it. The difference is that the large part is optional, split by state, and unused until queried.

Both designs pay for the same thing in different ways. A geocoder needs the world's places stored somewhere it can reach them. The search design reaches them by ranking, and ranking needs them in memory. 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 lookups, and those lookups let them stay on disk. It pays for that 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 extracts (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.. That cost is paid before you run a single query, and no hardware page lists it.

Where the verdict lands​

The choice depends on what your input looks like. The hardware table does not decide it.

If your queries are exploratory (search boxes, partial strings, landmarks, "the thing near the station"), the search architecture fits the job, and a parser is the wrong tool because such a query can contain no address 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 from a form, a CRM export or a delivery manifest, and you need fields as well as a point, the labeling architecture fits the job. A ranked document list would have to be post-processed into fields anyway.

The two designs can also be combined. A pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-hint → 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, use 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 no usable spansspanA 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 design is workable, and it pays 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.