Skip to main content

Swap in a drop-in for an existing client

You have a service that already calls a geocoder. It builds Photon query strings, reads Nominatim's jsonv2 result array, or posts to libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it.'s /parse. Change that caller to use Mailwoman's own /v1 API would be extra work.

Three packages accept those request formats instead. By the end of this page you will have the Photon-compatible server running against your own 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., a geopy client pointed at it by changing one argument, the Nominatim and libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it. variants running the same way, and a table of which request parametersparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. each drop-in reads. The tutorial takes about thirty minutes, most of it spent downloading.

Prerequisites​

Read the last two items before you start. They determine whether this page can answer your question.

  • Node.js 24.18.0 or later, and about 1.7 GB of free disk.
  • jq for the curl output below. Remove the pipe if you prefer to read the raw JSON.
  • Python with geopy for step 4, in a disposable virtual environment. Skip that step if your client is in another language. In any language, the change is one constructor argument.
  • 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 1,652,916,224-byte download, and it is not on npm. Step 1 fetches it once. Everything after step 1 runs locally.
  • The default ports conflict with the servers you are replacing. The defaults are 2322 for Photon, 8080 for Nominatim and 8081 for libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it.. Those are the same ports your existing servers use. This page moves two of them with --port, because ports 2322 and 8080 were in use on the machine used to verify it. Run the drop-in next to the original on a spare port to test a swap anyway, because you can compare the two answers before you switch.
  • /reverse needs a database this page does not download. Reverse 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. on the Photon and Nominatim drop-ins runs point-in-polygon over a full WOFWOF (Who's On First). An open-source gazetteer of places maintained by Mapzen/whosonfirst. Mailwoman builds a custom SQLite database from WOF GeoJSON repos, extended with postcode data, importance scores, and coincident-role relations. distribution at <data-root>/db/wof/admin-global-priority.db. mailwoman data pull does not provide one, so /reverse returns an empty result until you add it. Step 3 shows that output so that you do not mistake it for a broken install. Most callers use forward 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., and it works from step 1.
  • None of the servers has authentication, and --host defaults to 0.0.0.0. Bind 127.0.0.1 or put the server behind your own ingress before you expose it on a shared network.

1. Pull the gazetteer once​

All three packages read one data root. Set it, and check the plan before you start the transfer. --dry-run prints the URL, the size and the destination without using the network:

export MAILWOMAN_DATA_ROOT=/tmp/mailwoman-data
npx mailwoman data pull candidate --dry-run
✓ candidate: gazetteer/2026-07-07a/candidate.db: [dry-run] 1652.9 MB
https://public.mailwoman.ai/mailwoman/gazetteer/2026-07-07a/candidate.db →
/tmp/mailwoman-data/wof/candidate.db
PASS (1/1 checks)

Then remove the flag and run the download:

npx mailwoman data pull candidate
▸ pull https://public.mailwoman.ai/mailwoman/gazetteer/2026-07-07a/candidate.db (~1652.9 MB) → /tmp/mailwoman-data/wof/candidate.db
[DEBUG] (mailwoman data): HEAD: https://public.mailwoman.ai/mailwoman/gazetteer/2026-07-07a/candidate.db
[DEBUG] (mailwoman data): 200 (uncached) HEAD: https://public.mailwoman.ai/mailwoman/gazetteer/2026-07-07a/candidate.db

export MAILWOMAN_CANDIDATE_DB=/tmp/mailwoman-data/wof/candidate.db
✓ candidate: gazetteer/2026-07-07a/candidate.db: content-length verified (1652.9 MB) →
/tmp/mailwoman-data/wof/candidate.db
PASS (1/1 checks)

The paths in the transcripts on this page use the data root from the verification machine, so yours will differ. The file is saved at <data-root>/db/wof/candidate.db, and every entry point finds it there automatically, including the drop-ins, mailwoman geocode and mailwoman serve. The export line that the pull prints is left over from 8.6.0, when only the drop-ins had that fallback. Run it has no effect. Pass --candidate-db <path> if your copy is stored elsewhere.

Re-running the pull is safe. If the file is already on disk, it reports already present … skipped and downloads no file, so you can leave this command in a provisioning script.

2. Start the Photon-compatible server​

npx @mailwoman/photon serve --port 2323
[resolver] candidate-table backend (demo-parity, population-first): /tmp/mailwoman-data/wof/candidate.db
[@mailwoman/photon] listening on http://0.0.0.0:2323
wof: (none found — set MAILWOMAN_WOF_DB)
resolver: candidate gazetteer (worldwide) — /tmp/mailwoman-data/wof/candidate.db
cors: enabled (Access-Control-Allow-Origin: *)
endpoints: GET / GET /api GET /reverse GET /openapi.json

The four indented lines describe the deployment. Check them in the log when two hosts return different answers. resolver: shows which 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. answers queries. wof: shows the reverse-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. database from the prerequisites. That database is missing here. Only mailwoman serve reads the variable that line suggests. This server looks only in <data-root>/db/wof/. Permissive CORS is on by default because browser geocoder widgets call these endpoints cross-origin. --no-cors turns it off when a reverse proxy already sets the headers.

Leave it running and open a second terminal.

3. Ask it what your client asks​

GET /api is Photon's forward endpoint, and it returns a GeoJSON FeatureCollection.

curl -s "http://127.0.0.1:2323/api?q=1600+pennsylvania+ave+nw+washington+dc+20500&limit=1" | jq '.features[0]'
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-77.016216, 38.904831]
},
"properties": {
"osm_key": "place",
"osm_value": "city",
"type": "city",
"name": "Washington",
"city": "Washington",
"state": "District of Columbia",
"postcode": "20500",
"country": "United States",
"countrycode": "us"
}
}

osm_key, osm_value and type are always present, because Photon clients read them without checking. They are derived from the resolved place's type instead of being read from 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.. The table in step 7 has details.

The lat/lon parametersparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. apply a proximity bias, and an ambiguous query shows their effect. The same query sent twice produces two orderings:

curl -s "http://127.0.0.1:2323/api?q=paris&limit=3" | jq -c '[.features[] | {name: .properties.name, cc: .properties.countrycode, coords: .geometry.coordinates}]'
curl -s "http://127.0.0.1:2323/api?q=paris&limit=3&lat=33.66&lon=-95.55" | jq -c '[.features[] | {name: .properties.name, cc: .properties.countrycode, coords: .geometry.coordinates}]'
[{"name":"Paris","cc":"fr","coords":[2.342841,48.856599]},{"name":"Paris","cc":"us","coords":[-95.54435,33.668553]},{"name":"Paris","cc":"us","coords":[-88.30661,36.293581]}]
[{"name":"Paris","cc":"us","coords":[-95.54435,33.668553]},{"name":"Paris","cc":"fr","coords":[2.342841,48.856599]},{"name":"Paris","cc":"us","coords":[-88.30661,36.293581]}]

Without a bias, Paris in France ranks first on population. With a bias toward a point in north-east Texas, the Texan Paris ranks first and France drops to second. The bias re-ranks results and never filters them, so both alternatives stay in the collection. If you pass your map center, a type-ahead orders results the way the person typing expects.

Now try the /reverse endpoint from the prerequisites:

curl -s "http://127.0.0.1:2323/reverse?lat=52.52&lon=13.405" | jq -c
{ "type": "FeatureCollection", "features": [] }

The route returns an empty collection instead of an error. This is the effect of the wof: (none found) line from step 2. Without a full WOFWOF (Who's On First). An open-source gazetteer of places maintained by Mapzen/whosonfirst. Mailwoman builds a custom SQLite database from WOF GeoJSON repos, extended with postcode data, importance scores, and coincident-role relations. distribution on disk, the server never constructs the reverse geocoder, and the route returns the empty shape instead of a 500. To get real answers, put admin-global-priority.db in <data-root>/db/wof/ and restart.

4. Point your existing client at it​

The steps above did not use a client library. This step performs the swap. In geopy the swap is one argument. Install geopy in a disposable environment:

python3 -m venv /tmp/geopy-venv && /tmp/geopy-venv/bin/pip install geopy

Then pass the Photon geocoder a domain and a scheme so that it does not use the hosted service by default:

from geopy.geocoders import Photon

photon = Photon(domain="localhost:2323", scheme="http")
loc = photon.geocode("1600 Pennsylvania Ave NW, Washington DC 20500")
print(loc.address)
print((loc.latitude, loc.longitude))
Washington, 20500, Washington, District of Columbia, United States
(38.904831, -77.016216)

Your code receives the same object it already handles, from a server you run. Every Photon client has an equivalent setting under some name, such as a base URL, a host or an endpoint override, because the hosted instance is a default and not a hard-coded constant.

5. The Nominatim shape​

The Nominatim drop-in uses the same data root and the same download, with a different package and a different result format. Start it next to the Photon server:

npx @mailwoman/nominatim serve --port 8088
[resolver] candidate-table backend (demo-parity, population-first): /tmp/mailwoman-data/wof/candidate.db
[@mailwoman/nominatim] listening on http://0.0.0.0:8088
wof: (none found — set MAILWOMAN_WOF_DB)
resolver: candidate gazetteer (worldwide) — /tmp/mailwoman-data/wof/candidate.db
cors: enabled (Access-Control-Allow-Origin: *)
endpoints: GET / GET /search GET /reverse GET /lookup GET /status GET /openapi.json

A health check calls /status, which returns Nominatim's two-key object:

curl -s http://127.0.0.1:8088/status
{ "status": 0, "message": "OK" }

/search takes free text or the structured parametersparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values., and addressdetails=1 adds the address block:

curl -s "http://127.0.0.1:8088/search?q=10+Downing+Street,+London+SW1A+2AA&addressdetails=1" | jq '.[0]'
{
"place_id": 1178923677,
"licence": "Data © Who's On First, Overture Maps, OpenAddresses, US Census TIGER",
"lat": "51.500525578898",
"lon": "-0.109400835283853",
"display_name": "10, Downing Street, London, SW1A 2AA",
"address": {
"city": "London",
"postcode": "SW1A 2AA",
"house_number": "10",
"road": "Downing Street"
},
"annotations": {
"DMS": {
"lat": "51° 30′ 1.89″ N",
"lng": "0° 6′ 33.84″ W"
},
"MGRS": "30UYC0062309449",
"Maidenhead": "IO91wm",
"Mercator": {
"x": -12178.445276157312,
"y": 6710313.068983022
},
"geohash": "gcpuvx2k6",
"qibla": 119.00112658398905,
"sun": {
"rise": {
"apparent": 1785731284
},
"set": {
"apparent": 1785786464
}
}
}
}

The annotations block is an addition in OpenCage's format. Fields derived from only the coordinate are always present: the coordinate formats, the qibla direction and the sun times. Three more fields, flag, currency and callingcode, need 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. in the resolved hierarchy. This result has none, but ?q=Berlin,+Germany does. The IANA timezone, UN/LOCODE and EU NUTS codes are added when those lookup databases are in the data root. A Nominatim client that ignores unknown keys is unaffected by any of these fields.

countrycodes is applied as a hard restriction, so you can use it to override the 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. when a bare place nametoponymA proper name for a geographic place. resolves to the wrong one:

curl -s "http://127.0.0.1:8088/search?q=springfield&countrycodes=de" | jq -c
[]

The empty array means the restriction was applied and no place in Germany matched. A hard filter that returns no result is working correctly. Step 3's soft bias never removes results this way.

geopy's Nominatim geocoder takes the same two arguments as its Photon one, plus the user_agent it requires:

from geopy.geocoders import Nominatim

nom = Nominatim(domain="localhost:8088", scheme="http", user_agent="swap-tutorial")
loc = nom.geocode("10 Downing Street, London SW1A 2AA", addressdetails=True)
print(loc.address)
print((loc.latitude, loc.longitude))
print(loc.raw["address"])
10, Downing Street, London, SW1A 2AA
(51.500525578898, -0.109400835283853)
{'city': 'London', 'postcode': 'SW1A 2AA', 'house_number': '10', 'road': 'Downing Street'}

6. The libpostal shape​

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.-only drop-in needs no 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., because the model weightsmodel weightsThe learned parameters of the neural classifier, shipped as ONNX files in the @mailwoman/neural-weights-* packages. Weights are locale-specific bundles that include the model, tokenizer, and a model-card.json metadata file. are installed as a package dependency. It starts even on a machine that never ran step 1:

npx @mailwoman/libpostal serve --port 8081
[@mailwoman/libpostal] listening on http://0.0.0.0:8081
cors: enabled (Access-Control-Allow-Origin: *)
endpoints: GET / POST/GET /parse POST/GET /expand GET /openapi.json

/parse returns libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it.'s ordered [{label, value}] array, over GET or POST:

curl -s "http://127.0.0.1:8081/parse?query=1600%20Pennsylvania%20Ave%20NW%2C%20Washington%20DC%2020500"
curl -s -X POST http://127.0.0.1:8081/parse -H 'content-type: application/json' -d '{"query":"30 Rue de Rivoli, 75004 Paris"}'
[{"label":"house_number","value":"1600"},{"label":"road","value":"Pennsylvania Ave NW"},{"label":"city","value":"Washington"},{"label":"state","value":"DC"},{"label":"postcode","value":"20500"}]
[{"label":"house_number","value":"30"},{"label":"road","value":"Rue de Rivoli"},{"label":"postcode","value":"75004"},{"label":"city","value":"Paris"}]

/expand returns a set. The set is deliberately small:

curl -s "http://127.0.0.1:8081/expand?address=1600%20Pennsylvania%20Ave%20NW"
{ "expansions": ["1600 Pennsylvania Ave NW", "1600 Pennsylvania Avenue Northwest"] }

The route builds three strings (the input, its normalized form, and its abbreviation-expanded form) and drops duplicates. That is why this address yields two. Mailwoman's normalization stagestageOne of the dataflow stages in the runtime pipeline (normalize, locale check, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). is deterministic, so the result is one canonical alternative instead of the multi-variant hypothesis set that libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it.'s probabilistic expander produces. A caller that indexes every expansion gets fewer keys per address here. A caller that takes expansions[0] or the last element sees no difference.

A missing parameterparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. returns a status code and a one-key object instead of a stack trace:

curl -s -i "http://127.0.0.1:8081/parse" | head -1
curl -s "http://127.0.0.1:8081/parse"
HTTP/1.1 400 Bad Request
{"error":"query is required"}

7. What each drop-in honors​

The tables below list compatibility as of 8.7.0. The entries were read from the route handlers and the engines behind them, then checked against a running server. Each request parameterparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. has one of three verdicts:

  • Honored — the parameterparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. changes the answer.
  • Accepted, ignored — the request is valid and the parameterparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. is parsed, but no downstream stagestageOne of the dataflow stages in the runtime pipeline (normalize, locale check, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). reads it. Your client will not break, and it will get the unfiltered answer.
  • Not read — the parameterparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. never reaches a handler.

Rows for response fields and whole routes have their own verdicts, and the third column explains each one.

Photon: /api and /reverse​

ParameterparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. or fieldVerdictWhat that means here
qHonoredParsed and resolved. Empty or over 512 characters answers an empty collection
limitHonoredCaps the featuresfeatureAn input signal a model conditions on. Beyond the raw tokens, Mailwoman feeds soft features — gazetteer-membership channels and the postcode anchor — that inform predictions without overriding them. returned. /reverse returns at most one regardless
lat + lon on /apiHonoredSoft proximity bias, both required. Re-ranks candidates, never filters
format=jsonldHonored, additionRe-serializes the same collection as schema.org Place[]. Upstream Photon has no such format
langAccepted, ignoredNames come back as 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. stores them. ?q=cologne&lang=en still answers Köln
osm_tagAccepted, ignoredThe server applies no category filter. A request for amenity:cafe returns the same places as one without it
layerAccepted, ignoredThe server applies no layerlayerOne transformer block — attention plus a feed-forward network, with normalization and residual connections — applied to every position. Stacking layers lets the model build up richer representations; Mailwoman's encoder has 6. filter
radius on /reverseAccepted, ignoredThe reverse geocoder returns the containing place instead of running a radius search
bboxNot readThe handler 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. no bounding box. Use lat/lon for viewport influence
osm_key/osm_value/typeAlways presentDerived from the resolved place's type so clients can dereference them. Not 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. tag values
osm_id / osm_typeNever setNo part of this is keyed to an 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. object.A client that stores them stores undefined
/reverse without a full WOFWOF (Who's On First). An open-source gazetteer of places maintained by Mapzen/whosonfirst. Mailwoman builds a custom SQLite database from WOF GeoJSON repos, extended with postcode data, importance scores, and coincident-role relations. distributionEmpty collectionSee the prerequisites

Nominatim: /search, /reverse, /lookup, /status​

ParameterparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. or fieldVerdictWhat that means here
qHonoredFree-text forward 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.
street, city, county, state, country, postalcodeHonoredJoined into one query string in that order, then parsed. ?city=Berlin&country=Germany resolves
countrycodesHonoredHard 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. restriction, first code only. Can legitimately answer []
addressdetailsHonoredAdds the address block
formatHonoredgeojson and jsonld change the envelope; jsonv2 (the default) and json both answer the result array. /lookup ignores jsonld — a preserved quirk
limitAccepted, capped/search returns at most one result today, so limit=5 still answers one
boundedAccepted, ignoredThe server applies no viewbox restriction
accept-languageAccepted, ignoredNames behave the same way as with Photon's lang
zoom on /reverseAccepted, ignoredThe detail level is whatever the containing hierarchy provides
viewbox, extratags, namedetails, polygon_*, dedupeNot readThe handlers 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. none of them
place_idSubstitutedForward results return a deterministic hash of coordinate plus display name rather than a Nominatim id. Reverse results return 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.'s own place id
osm_id / osm_typeNever setAs with Photon
licenceSubstitutedData © Who's On First, Overture Maps, OpenAddresses, US Census TIGER — the sources these answers come from
annotationsAdditionThe OpenCage-style block from step 5
/lookup501Not implemented
/reverse without a full WOFWOF (Who's On First). An open-source gazetteer of places maintained by Mapzen/whosonfirst. Mailwoman builds a custom SQLite database from WOF GeoJSON repos, extended with postcode data, importance scores, and coincident-role relations. distributionnullSee the prerequisites

libpostal: /parse and /expand​

ParameterparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. or fieldVerdictWhat that means here
query, addressHonoredOn /parse, either name works, over GET or POST. /expand takes address
Component 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.MappedMailwoman's tags project onto libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it.'s names — street to road, locality to city, region to state, dependent_locality and neighbourhood both to suburb, and so on through twenty entries. A tag with no entry passes through under its own name
/expand outputDeduplicated setThe input, its normalized form and its abbreviation-expanded form, with duplicates dropped — one to three strings rather than a hypothesis set. See step 6
Language optionsNot readOne weights bundlemodel weightsThe learned parameters of the neural classifier, shipped as ONNX files in the @mailwoman/neural-weights-* packages. Weights are locale-specific bundles that include the model, tokenizer, and a model-card.json metadata file. (en-us) is loaded at startup, and there is no per-request language parameterparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values.

8. Stop them​

Press Ctrl-C in each server's terminal. The servers write no files to the data root, so a restart continues from the same state.

What you have now​

You have three compatible endpoints on your own hardware. They read one 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. and accept the request formats your existing callers already build. You also have a record of which parametersparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. each one reads, so if a swap changes an answer, you can find the reason in a table.

Next​

  • Run the API server — Mailwoman's own /v1 API, with the fuller result object and a generated OpenAPI document. As a rule, point an existing integration at a drop-in and a new one at /v1.
  • Improve geocode precision — the per-state download that moves these answers from a city centroidlocality centroidThe representative center point of a city or locality, used as a coarse coordinate when no exact address point is available — the coarsest tier of the geocode cascade. to a 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..
  • Parse in the browser — the same 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.' with no server at all.