Search docs

Engines

Geolith Search measured against Pelias, Photon and Nominatim on an India extract.

By · Updated

Conditions

  • One machine: Apple M2 Max, 96 GB RAM, Docker Desktop with 12 CPUs and 36 GB.
  • All four engines run in Docker on the same machine, each from its own image and data volume. One engine runs at a time; the others are stopped. Compose file and scripts: packages/bench/docker.
  • geolith-search answers from one .glx file on a Docker volume. There is no database.
  • Accuracy: 200 queries per class, one request each, after a warm-up pass. A top-1 result counts as correct when it lies within the gold radius of the gold point: 10 km for a town and 25 km for a city in the OSM places class, 0.5 km for reverse, and 1 km for every other class. Top-5 counts a result within the same radius in the first five.
  • Load: autocannon from the host, 10 seconds, 100 connections.
  • Load and latency measured on 19 Oct 2026 with the 530 MB India file. Accuracy for geolith re-measured on 20 Oct 2026 with the current 543 MB file (name initials, synonyms and the town-reading fixes since); the other engines' rows are unchanged.
  • Single run on a local machine: treat the ratios as the result, not the absolute numbers.

Test data

  • India as the area. OpenStreetMap extract india-latest.osm.pbf (1.7 GB, 5 Oct 2026) and Overture release 2026-09-23.1.
  • geolith indexes OSM and Overture together: settlements, venues and addresses from OSM, deduplicated against Overture, on top of the Overture places. Pelias indexes OSM with Who's on First admin data, plus street polylines and address interpolation. Photon and Nominatim index OSM.
  • The Overture download box reaches past the Indian border, so the .glx file also holds places in neighbouring countries. Every geolith request sends boundary.country=IN, so all four engines answer from India only.
  • The gold sets are sampled from both sources. The Overture POI class is sampled from Overture, which only geolith indexes, so read that row as coverage, not ranking.

Summary

EngineTop-1 winsSearch loadPeak memory under loadIndex on disk
geolith5 of 7 classes1,518 req/s0.74 GB543 MB, one file
Photon 1.3.01 of 7 (OSM POIs)489 req/s3.2 GB810 MB
Nominatim 5.31 of 7 (reverse)774 req/s3.1 GB45 GB
Pelias (master)0 of 7371 req/s11.6 GB across six services1.0 GB + supporting stores

Accuracy

Top-1 / top-5 within the gold radius of each class (see Conditions), 200 queries per class.

Query classgeolithPeliasPhotonNominatim
OSM places (cities, towns)95.0 / 97.577.0 / 86.080.5 / 96.585.0 / 96.0
OSM addresses95.0 / 98.561.5 / 63.088.5 / 91.050.5 / 52.5
OSM POIs84.5 / 91.561.0 / 72.585.5 / 89.568.5 / 72.5
Overture POIs93.0 / 97.57.0 / 14.012.5 / 16.56.0 / 6.5
Autocomplete82.5 / 88.532.5 / 36.542.5 / 48.5not supported
Search near a point96.0 / 97.039.0 / 42.552.0 / 53.544.5 / 47.0
Reverse99.5 / 99.598.0 / 98.098.5 / 98.5100.0 / 100.0

Percentages are of all queries in the class, so a query that returns nothing counts as a miss.

Feature probes

25 single queries on the same India data, each testing one capability the three other engines document (neighbourhood and postcode on a result, addr:place addresses, house number with an area, bounding boxes, de-duplication, category filter and output, localized output, accents, apostrophes, interpolation). Script: ml/parity_probe.py.

geolithNominatimPhotonPelias
24 / 2521 / 2519 / 2516 / 25

The probe geolith fails, an interpolated house number at the end of a range, fails on all four engines.

Address parser

With no comma, the reader finds the town at the end of a query by trying readings against the file, as Nominatim does. See Reading a town without a comma.

Beyond parity: name initials

People write many places by their initials, and Google Maps finds them: "SIMS, Seawoods" for the Sterling Institute of Management Studies, "SBI Nerul" for the State Bank of India, "JNU" for Jawaharlal Nehru University. Nominatim, Photon and Pelias find a place by its initials only when OpenStreetMap tags them as short_name. geolith indexes the initials of every venue and area named in three to six words, so all three queries above return the right place first. The initials add 12 MB to the India file and change no gold-set answer.

Latency, one request

Median / p95 in ms, one request per query, warm engines.

Query classgeolithPeliasPhotonNominatim
Autocomplete5.8 / 54.74.9 / 9.84.0 / 14.8—
Search near a point7.1 / 22.29.0 / 18.83.1 / 14.56.5 / 18.1
OSM addresses7.2 / 17.57.5 / 17.92.2 / 17.611.4 / 32.6
OSM places2.5 / 5.74.0 / 35.43.7 / 5.05.4 / 10.7
OSM POIs5.7 / 18.78.9 / 18.72.2 / 17.16.8 / 14.2
Overture POIs5.9 / 11.47.7 / 12.710.0 / 18.86.5 / 13.0
Reverse0.7 / 1.24.3 / 6.71.9 / 2.46.2 / 8.8

Load

10 seconds, 100 connections: requests per second, p50 ms, p99 ms, errors.

Query classgeolithPeliasPhotonNominatim
Search1,518 · 68 · 109 · 0371 · 242 · 539 · 0489 · 151 · 484 · 0774 · 80 · 662 · 0
Autocomplete668 · 142 · 306 · 0980 · 92 · 217 · 0593 · 167 · 313 · 0—
Search near a point994 · 98 · 145 · 0414 · 226 · 1152 · 0592 · 161 · 485 · 0894 · 38 · 540 · 0
Reverse21,860 · 4 · 8 · 01,842 · 52 · 88 · 03,132 · 30 · 43 · 01,118 · 41 · 420 · 0

Every run was clean. geolith runs one index read per core and queues the rest, which keeps its memory at 0.74 GB under load.

Storage and setup

EngineData storeIndex on diskSetup
geolithone .glx file543 MB, 6,382,829 placesone geolith run from Overture + OSM: 15 min
PeliasOpenSearch 7.17.27, 2.2M documents1.01 GB indexWOF, OSM, polylines and interpolation; the final pass ran ≈ 10 min after downloads
PhotonOpenSearch inside the Photon process810 MBpublished dump, no build step here
NominatimPostgreSQL 1645 GBosm2pgsql import

Pelias also needs libpostal, the placeholder service, the point-in-polygon service and the interpolation SQLite store; those services are running and are counted in its memory, not in the 1.01 GB.

The .glx file

What the file format itself costs per query, read with glx-query from local disk with no server and no network. The file is 530 MB (19 Oct 2026).

Query classTop-1Top-5Read rounds p50 / p95Bytes p50 / p95CPU p50 / p95
OSM places93.096.52 / 20.5 / 1.9 MB1 / 2 ms
OSM addresses95.598.02 / 30.6 / 1.9 MB7 / 18 ms
OSM POIs80.588.53 / 30.5 / 3.2 MB5 / 17 ms
Overture POIs90.596.02 / 30.5 / 9.4 MB4 / 26 ms
Autocomplete82.589.03 / 50.5 / 13.7 MB5 / 53 ms

glx-query sends no country filter, so these top-1 numbers can differ from the API table by a point or two.

Analysis

  • geolith takes top-1 on five of seven classes and leads search, point-focused search and reverse throughput, from one 543 MB file with no database, using under 1 GB of memory under load.
  • OSM POIs are the one class Photon leads, by 1.0 point (85.5 vs 84.5); geolith leads its top-5. Built from OSM alone, the data Photon indexes, geolith scores 86.5 on this class. In the full build, Overture adds more same-name branches in the same town (several shops called Burberry or Amul), and the gold set expects one of them.
  • Nominatim owns reverse geocoding (100%), with a 45 GB database.
  • Pelias wins no class on this dataset. It has the fastest autocomplete under load and the lowest autocomplete p95.
  • geolith's slowest single requests are long autocomplete prefixes (p95 54.7 ms), within its 100 ms autocomplete target but well above Photon and Pelias.
  • The data asymmetry cuts both ways. geolith is the only engine here that indexes both OSM and Overture, which is why it takes the Overture POI class outright.

Reproduce

# From a checkout of the geolith search repository:
cd packages/bench
pnpm install

# Fill the Docker volumes, import Pelias and Nominatim, build the image:
docker/setup.sh
# Measure each engine alone (accuracy, load, memory):
docker/run.sh
# glx-query is the geolith-glx reader's command-line tool (feature `build`).
node src/glx.ts queries/india ~/india.glx ~/.cargo/bin/glx-query results/glx.json

The query sets are test fixtures sampled from OpenStreetMap (ODbL) and Overture Maps places on the dates above; the gold points are the sampled feature locations.