ExNominatim is a full-featured client for the OpenStreetMap Nominatim API V1, with extensive request validation, robust error-handling and reporting, and user guidance with helpful validation messages.
Goals
- Prevent unnecessary calls to the Nominatim API server by validating intended requests and preventing them if the request parameters are invalid.
- Solid error-handling for robustness in production.
- Provide helpful validation messages to the user when a request is deemed invalid.
Features
- Covers the
/search,/reverse,/lookup,/statusand/detailsendpoints. search_one/1convenience for single-result lookups.- Utilizes request parameter structs with the appropriate fields (except for
json_callback) for each endpoint. - Configurable for your application with overridable defaults using Elixir's
Configmodule to set any default values, including the:base_urloption for use with self-hosted Nominatim API instances. - Validates parameter values prior to the request (possible to override this with the
force: trueoption). - Provides helpful return tuples
{:ok, ...},{:error, reason}and{:error, {specific_error, other_info}}across the board. - Collects all detected field validation errors in an
:errorsfield, and provides a:valid?field in each request params struct. - Automatically sets the
User-Agentheader to "ExNominatim/{version}" to comply with the Nominatim Usage Policy. - Optional caching via Cachex (
:cacheoption). - Built-in rate limiting with
:automode for the public server. - Telemetry events for production observability.
- Optional geohash computation on results with lat/lon.
Installation
The package can be installed from Hex by adding ex_nominatim to your list of dependencies in mix.exs:
def deps do
[
{:ex_nominatim, "~> 3.0"}
]
endThe code can be found on Github/waseigo/ex_nominatim.
Documentation has been published on HexDocs.
There is also a thread open on the Elixir Programming Language Forum: ExNominatim - A full-featured client for the OpenStreetMap Nominatim API V1.
Usage
By calling the endpoint functions of the ExNominatim module you will be hitting the public Nominatim API server with each endpoint's default options as described in the API documentation; i.e., all requests use https://nominatim.openstreetmap.org as the value of :base_url in opts and the default parameters for each endpoint are handled by the API according to its documentation.
Please respect the Nominatim Usage Policy when using the public server.
Optional configuration and default parameters
In the more likely scenario where you use ExNominatim in your own application (e.g., in a Phoenix application), you can override all defaults across all endpoints and then even for each endpoint through your application's configuration, e.g. in the config/config.exs file of a Phoenix app. For example:
config :ex_nominatim, ExNominatim,
all: [
base_url: "http://localhost:8080",
force: true,
format: "json",
process: true,
atomize: true
],
search: [format: "geocodejson", force: false],
reverse: [namedetails: 1],
lookup: [],
details: [],
status: [format: "json"]The configuration above has the following effects:
- Requests to all endpoints will use the self-hosted Nominatim API instance at port 8080 of
localhost, accessible over HTTP. - Request parameter validation errors (
valid?: falsein the request parameters struct) will be ignored for requests to all endpoints, except for requests to the/searchendpoint. - Unless requested otherwise in
opts, requests to the/searchendpoint will return data in GeocodeJSON format (instead of the defaultjsonv2). - Requests to the
/reverseendpoint will set:namedetailsto 1 (unless otherwise set inopts). - Requests to the
/statusendpoint will return JSON instead of the default text (HTTP status code 200 and textOKor HTTP status 500 and a detailed error mesage). - The responses from all endpoints will be processed automatically using
ExNominatim.Report.process/1, and any maps and contents thereof (or map contents of structs's keys) will be converted from binary keys to atom keys usingExNominatim.Report.atomize/1.
Refer to the documentation of the main ExNominatim module for more information.
Rate Limiting
Built-in rate limiting (zero dependencies) that enforces 1 req/s for the public Nominatim server by default.
| Config value | Behavior |
|---|---|
:auto (default) | 1 req/s for nominatim.openstreetmap.org, no limit for other servers |
true | 1 req/s for all servers |
false | Disabled |
integer (e.g. 5) | N req/s for all servers |
# Disable for self-hosted servers
config :ex_nominatim, ExNominatim,
all: [rate_limit: false]
# Or explicitly enable for all servers
config :ex_nominatim, ExNominatim,
all: [rate_limit: true]
# Or set a custom rate (e.g. 5 req/s)
config :ex_nominatim, ExNominatim,
all: [rate_limit: 5]The rate limiter uses an ETS table (no GenServer, no bottleneck) and is checked after cache lookup but before HTTP dispatch — cached results bypass it entirely. Errors return {:error, {:rate_limited, retry_after_ms}}. Set rate_limit_retry: true to automatically sleep and retry up to 3 times.
Telemetry
ExNominatim emits the following Telemetry events:
| Event | Measurements | Metadata |
|---|---|---|
[:ex_nominatim, :request, :stop] | duration | endpoint, base_url, status |
[:ex_nominatim, :request, :exception] | duration | endpoint, base_url, error |
[:ex_nominatim, :cache, :hit] | — | endpoint |
[:ex_nominatim, :cache, :miss] | — | endpoint |
[:ex_nominatim, :rate_limit, :deny] | — | endpoint, base_url, retry_after_ms |
Attach handlers via :telemetry.attach/4 to build dashboards, alert on errors, or log cache hit rates.
Geohash
Pass geohash: true (or an integer precision) to append a :geohash key to each result that has lat/lon fields:
{:ok, %{body: [%{lat: "38.0", lon: "23.7", geohash: "sryj481k2m4d", ...}]}} =
ExNominatim.search(q: "Athens", geohash: true)Requires the optional geohash hex package in your deps:
{:geohash, "~> 1.0"}Custom Req options
Pass any Req option via :req_opts — useful for custom headers, timeouts, or connection options:
ExNominatim.search(q: "Athens", req_opts: [
headers: [authorization: "Bearer xyz"],
receive_timeout: 5000
])Caching
Optionally cache successful Nominatim responses via Cachex:
# mix.exs
{:cachex, "~> 4.0"}
# config/config.exs
config :ex_nominatim, :cache_name, :ex_nominatim # default
config :ex_nominatim, :cache_ttl, 86_400_000 # 24 hoursPass cache: ExNominatim.CachexCache to any endpoint:
ExNominatim.search(q: "Athens", cache: ExNominatim.CachexCache)Only successful results are cached. Errors never cache. When cache: nil (default), caching is disabled with zero overhead.
You can implement custom cache backends via the ExNominatim.Cache protocol.
Ideas and someday/maybe features
- none at the moment
Who made this?
Copyright 2024, made by Isaak Tsalicoglou, Managing Director of OVERBRING Labs in Athens, Attica, Greece.
Many thanks to all the volunteers and contributors of OpenStreetMap and Nominatim.
Support
If this library saves you time or helps your project, consider saying thanks by purchasing a copy of Northwind Elixir Traders, an exploratory-learning book that teaches Elixir, Ecto, and SQLite all in one hands-on project, with its source code released under the Apache-2.0 License.

See what readers are saying on the book's ElixirForum thread.