# Alternatives to the Nominatim public API for production

> The public Nominatim API allows at most 1 request per second and forbids autocomplete. Here are the production options (self-host Nominatim, Photon or Pelias, or use a hosted API) and the code to move a Nominatim call to geolith.

---
title: Alternatives to the Nominatim public API for production
description: The public Nominatim API allows at most 1 request per second and forbids autocomplete. Here are the production options (self-host Nominatim, Photon or Pelias, or use a hosted API) and the code to move a Nominatim call to geolith.
date: '2026-10-22'
tags: [nominatim, openstreetmap, migration]
minutes: 10
steps:
  - name: Check what the public API allows
    text: Read the OSMF Nominatim usage policy. At most 1 request per second, a valid User-Agent, no client-side autocomplete, cached results.
  - name: Pick a production option
    text: Self-host Nominatim, Photon or Pelias, or use a hosted API such as geolith.
  - name: Map the request
    text: Nominatim /search?q=...&format=json becomes geolith /v1/search?text=...
  - name: Map the response
    text: Nominatim returns lat and lon as strings; geolith returns GeoJSON with geometry.coordinates as [lon, lat] numbers.
faqs:
  - q: Can I use nominatim.openstreetmap.org in my app?
    a: Only within the OSMF usage policy, read on 22 Oct 2026, which sets an absolute maximum of 1 request per second, requires a valid User-Agent or Referer, forbids client-side autocomplete and discourages bulk geocoding. It may change without notice.
  - q: Is geolith more accurate than Nominatim?
    a: On the same India data (20 Oct 2026, 200 queries per set) geolith was ahead on places (95.0 vs 85.0), addresses (95.0 vs 50.5), OSM POIs (84.5 vs 68.5), Overture POIs (93.0 vs 6.0) and searches near a point (96.0 vs 44.5). Nominatim was ahead on reverse (100 vs 99.5).
  - q: What about storing results?
    a: The Nominatim policy asks you to cache results. geolith results may be stored permanently on every plan.
---

The public Nominatim API at nominatim.openstreetmap.org is run by the OpenStreetMap Foundation on donated servers, and its usage policy (read 22 Oct 2026) sets an **absolute maximum of 1 request per second**, requires an identifying User-Agent, **forbids client-side autocomplete**, discourages bulk geocoding and may change without notice. For a product with real users you need your own instance or a hosted geocoder. This guide lists the options and shows how to move a Nominatim call to geolith.

## What the public policy says

> "No heavy uses (an absolute maximum of 1 request per second)."
> — OpenStreetMap Foundation, Nominatim Usage Policy

From the OSMF Nominatim usage policy, read on 22 Oct 2026:

- No heavy use: an absolute maximum of 1 request per second.
- A valid HTTP Referer or User-Agent that identifies the application; stock library User-Agents are not enough.
- Clear attribution, and the data is under the ODbL.
- Auto-complete search is not supported, and implementing it on the client side with the API is forbidden.
- Bulk geocoding of larger amounts of data is not encouraged; small one-time jobs must be single-threaded on one machine, and scripts that run at regular intervals are limited to 4 requests per minute.
- Results must be cached on your side.
- The policy may change without notice, and access can be withdrawn.

None of that is unreasonable for a free community service. It just is not a production API.

## The options

| Option | What you run | Notes |
| --- | --- | --- |
| Self-host Nominatim | PostgreSQL/PostGIS and a full OSM import | Mature, best-known OSM geocoder; no autocomplete |
| Self-host Photon | A Java service and a search index | Search-as-you-type on OSM data |
| Self-host Pelias | Elasticsearch plus importers and services | Flexible, many data sources |
| Self-host geolith | One `.glx` file and a small server | No database; answered with range reads |
| Hosted geolith | Nothing | 100,000 requests a month free; results may be stored |

On the same India data in Docker (20 Oct 2026, 200 queries per set), top-1 accuracy was:

| Set | geolith | Nominatim | Photon | Pelias |
| --- | --- | --- | --- | --- |
| OSM places | 95.0 | 85.0 | 80.5 | 77.0 |
| OSM addresses | 95.0 | 50.5 | 88.5 | 61.5 |
| OSM POIs | 84.5 | 68.5 | 85.5 | 61.0 |
| Overture POIs | 93.0 | 6.0 | 12.5 | 7.0 |
| Autocomplete | 82.5 | n/a | 42.5 | 32.5 |
| Near a point | 96.0 | 44.5 | 52.0 | 39.0 |
| Reverse | 99.5 | 100 | 98.5 | 98.0 |

## Moving a Nominatim call to geolith

A typical Nominatim call:

```js
// Before: public Nominatim
const res = await fetch(
  'https://nominatim.openstreetmap.org/search?' +
    new URLSearchParams({ q: 'Connaught Place, New Delhi', format: 'json', limit: '1' }),
  { headers: { 'User-Agent': 'my-app/1.0 (me@example.com)' } },
);
const [hit] = await res.json();
const lat = Number(hit.lat); // strings in Nominatim
const lon = Number(hit.lon);
const label = hit.display_name;
```

The same with geolith:

```js
// After: geolith
const res = await fetch(
  'https://api.geolith.app/v1/search?' +
    new URLSearchParams({ text: 'Connaught Place, New Delhi', size: '1' }),
  { headers: { Authorization: 'Bearer YOUR_API_KEY' } },
);
const { features } = await res.json();
const [lon, lat] = features[0].geometry.coordinates; // numbers, GeoJSON order
const label = features[0].properties.label;
```

Parameter mapping:

| Nominatim | geolith |
| --- | --- |
| `/search?q=` | `/v1/search?text=` |
| `limit` | `size` (1 to 40) |
| `countrycodes=in,np` | `boundary.country=IN,NP` |
| `accept-language=hi` | `lang=hi` |
| `/reverse?lat=&lon=` | `/v1/reverse?point.lat=&point.lon=` |
| `/lookup?osm_ids=` | `/v1/place?ids=` (geolith gids from earlier results) |
| `display_name` | `properties.label` |
| `lat`, `lon` (strings) | `geometry.coordinates` = `[lon, lat]` (numbers) |
| no autocomplete | `/v1/autocomplete?text=` |

A reverse call moves the same way:

```bash
# Before
curl -A "my-app/1.0 (me@example.com)" \
  "https://nominatim.openstreetmap.org/reverse?lat=18.922&lon=72.8346&format=json"

# After
curl "https://api.geolith.app/v1/reverse?point.lat=18.922&point.lon=72.8346&size=1" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

## Before you switch

- **Coverage.** The hosted geolith API is worldwide: one planet index of 678,043,458 OpenStreetMap and Overture places.
- **Attribution.** Keep it: `© OpenStreetMap contributors (ODbL), Overture Maps Foundation`.
- **Storage.** You may store geolith results permanently, so a one-time migration of your cached Nominatim data is not required; re-geocode as rows change.

Get a [free key](https://console.geolith.app/signup) (100,000 requests a month) or read the [full comparison](/compare/nominatim).

## Sources

1. [Nominatim Usage Policy](https://operations.osmfoundation.org/policies/nominatim/), OpenStreetMap Foundation (read 2026-10-22)
2. [geolith search benchmarks: engines](https://docs.geolith.app/search/benchmarks/engines), geolith (read 2026-10-22)


Get a free API key: https://console.geolith.app/signup
