Pelias-compatible geocoding on Postgres

Search the whole planet in milliseconds

Geocoding, autocomplete and reverse geocoding over 555 million Overture places. One Rust binary and one Postgres. Every query in our planet benchmark returns in under 200 ms.

Places indexed
555M
Every benchmark query
<200 ms
Pelias endpoints
4
Open source
MIT

Real responses

The Pelias API you already know

Recorded from a local planet build. Same parameters and GeoJSON shape as Pelias, plus the TINQL query and the ranking that answered it.

GET /v1/search?text=eiffel tower&focus.point.lat=48.8566&focus.point.lon=2.3522&size=3

{
  "bbox": [
    2.3410981,
    48.8384429,
    2.3807976,
    48.8609288
  ],
  "features": [
    {
      "geometry": {
        "coordinates": [
          2.3473009,
          48.8489712
        ],
        "type": "Point"
      },
      "properties": {
        "confidence": 1,
        "country": "France",
        "country_code": "FR",
        "county": "Paris",
        "distance": 0.921,
        "gid": "geolith:venue:7437554776554892439",
        "id": "7437554776554892439",
        "importance": 0.8188999891281128,
        "label": "Eiffel Tower, Paris, Île-de-France, France",
        "layer": "venue",
        "locality": "Paris",
        "name": "Eiffel Tower",
        "region": "Île-de-France",
        "source": "geolith"
      },
      "type": "Feature"
    },
    {
      "geometry": {
        "coordinates": [
          2.3410981,
          48.8609288
        ],
        "type": "Point"
      },
      "properties": {
        "confidence": 1,
        "country": "France",
        "country_code": "FR",
        "county": "Paris",
        "distance": 0.944,
        "gid": "geolith:venue:8142640563823528902",
        "id": "8142640563823528902",
        "importance": 0.7164000272750854,
        "label": "Eiffel Tower, Paris, Île-de-France, France",
        "layer": "venue",
        "locality": "Paris",
        "name": "Eiffel Tower",
        "region": "Île-de-France",
        "source": "geolith"
      },
      "type": "Feature"
    },
    {
      "geometry": {
        "coordinates": [
          2.3807976,
          48.8384429
        ],
        "type": "Point"
      },
      "properties": {
        "confidence": 1,
        "country": "France",
        "country_code": "FR",
        "county": "Paris",
        "distance": 2.908,
        "gid": "geolith:venue:6133583665147722819",
        "id": "6133583665147722819",
        "importance": 0.9271000027656555,
        "label": "Eiffel Tower, Paris France, Paris, Île-de-France, France",
        "layer": "venue",
        "locality": "Paris",
        "name": "Eiffel Tower, Paris France",
        "region": "Île-de-France",
        "source": "geolith"
      },
      "type": "Feature"
    }
  ],
  "geocoding": {
    "attribution": "https://search.geolith.app/attribution",
    "engine": {
      "name": "geolith-search",
      "version": "0.1.0"
    },
    "query": {
      "ranking": "importance+distance",
      "size": 3,
      "text": "eiffel tower",
      "tinql": "\"eiffel\" AND \"tower\""
    },
    "version": "0.2"
  },
  "type": "FeatureCollection"
}

Benchmarks

Every query under 200 ms

Planet table, 555.6 million rows. Warm, best of 3, on one Mac with Postgres 18 in Docker. Local numbers, not a hosted SLA.

Global search

  • eiffel tower6 ms
  • cafe17 ms
  • main street78 ms
  • new york92 ms

Search near a point

  • ramen · Tokyo3 ms
  • pharmacy · London6 ms
  • cafe · New York10 ms
  • boulangerie · Paris20 ms

Autocomplete

  • starb4 ms
  • par50 ms
  • new yo110 ms
  • pa116 ms

How it works

A geocoder that is just Postgres

Pelias-compatible

Same endpoints, parameters and GeoJSON response shape as Pelias, so existing Pelias clients work unchanged.

Postgres, not a cluster

One Postgres with PostGIS and the TIN BM25 index. No Elasticsearch, no separate search service to run.

Ranked inside the index

BM25 text score plus place importance is ranked in the index itself, so a top 10 never sorts millions of rows.

Search, autocomplete, reverse

Free text, structured addresses, search-as-you-type with a focus point, and geodesic reverse geocoding.

Safe by construction

User text is tokenized before it reaches the query, so it cannot inject operators. Search text is never logged.

One Rust binary

An axum service with a warmed connection pool and a time budget on every query. MIT licensed.

Self-host

Your data stays on your servers

Build the geocoding table with geolith, load it with one COPY, and run the binary. A hosted API is coming soon.

# 1. Load geolith's geocoding output into Postgres (PostGIS + TIN)
# 2. Run the API
DATABASE_URL=postgres://user:pass@localhost:5432/postgres \
  cargo run --release

curl 'localhost:4000/v1/autocomplete?text=new%20yo'

Needs Postgres with PostGIS and the TIN extension, and the geocoding table from geolith's pg-copy output.

Self-hosting guide →

The geolith stack

Build, serve, search

Open-source tools for the whole map stack, from Overture data to tiles and places.