Skip to main content
Technology & EngineeringMaps Geolocation Services180 lines

OpenStreetMap & Nominatim

OpenStreetMap tile usage and Nominatim geocoding API: forward/reverse geocoding, tile servers, Overpass API queries, and attribution requirements

Quick Summary11 lines
You are an expert in integrating OpenStreetMap tile services and the Nominatim geocoding API for mapping and geolocation.

## Key Points

- **Omitting OSM attribution on maps** -- The ODbL license requires attribution. Displaying OSM tiles without the copyright notice is a license violation.
- Always include proper OSM attribution on any map displaying OSM tiles — this is a legal requirement under the ODbL license.
- Respect the Nominatim usage policy: maximum 1 request/second on the public instance, include a valid `User-Agent`, and do not bulk-geocode against the public server.
- Self-host Nominatim or use a commercial provider (e.g., LocationIQ, Geoapify, OpenCage) for any production application with significant geocoding volume.
- Omitting the `User-Agent` header causes Nominatim to return 403 errors or silently block your IP.
skilldb get maps-geolocation-services-skills/OpenStreetMap & NominatimFull skill: 180 lines
Paste into your CLAUDE.md or agent config

OpenStreetMap & Nominatim — Maps & Geolocation

You are an expert in integrating OpenStreetMap tile services and the Nominatim geocoding API for mapping and geolocation.

Core Philosophy

OpenStreetMap is open data, not a free service. The map data is licensed under the Open Database License (ODbL), which means you can use it for any purpose -- including commercial -- as long as you provide attribution and share any modifications to the data itself. But the public tile servers and Nominatim instance are community resources with strict usage policies: they are not CDNs for production applications. If your application serves significant traffic, you must use a commercial tile provider or self-host.

Nominatim is a geocoding engine, not a geocoding service. The public instance at nominatim.openstreetmap.org is rate-limited to 1 request per second, requires a User-Agent header, and explicitly prohibits bulk geocoding. For production workloads, self-host Nominatim with a country or region extract, or use a commercial Nominatim provider (LocationIQ, Geoapify, OpenCage). The geocoding quality is the same either way -- it is all the same engine and data.

Attribution is a legal requirement, not a courtesy. Any map displaying OSM tiles must include the copyright notice: "OpenStreetMap contributors". Any application using Nominatim should include OSM attribution in geocoding results. Omitting attribution violates the ODbL license and can result in legal action from the OpenStreetMap Foundation.

Anti-Patterns

  • Using tile.openstreetmap.org as a CDN for production applications -- The public tile server has usage policies that prohibit high-traffic commercial use. Self-host tiles or use a commercial provider (Mapbox, Stadia Maps, Thunderforest).
  • Bulk geocoding against the public Nominatim instance -- The public instance allows 1 request per second maximum. Bulk geocoding gets your IP blocked. Self-host Nominatim or use a commercial geocoding API.
  • Omitting the User-Agent header in Nominatim requests -- Requests without a User-Agent are deprioritized or blocked outright. Set a descriptive User-Agent that identifies your application and includes contact information.
  • Omitting OSM attribution on maps -- The ODbL license requires attribution. Displaying OSM tiles without the copyright notice is a license violation.
  • Using the Overpass API for real-time data queries in hot paths -- Overpass queries can take seconds for complex spatial queries. Cache results and use Overpass for batch/background data extraction, not per-request lookups.

Overview

OpenStreetMap (OSM) is a collaborative, open-data map of the world. Nominatim is the official OSM geocoding engine that converts addresses to coordinates (forward geocoding) and coordinates to addresses (reverse geocoding). The Overpass API allows querying OSM data by tags, geometry, and relations. OSM tiles can be consumed by any map renderer (Leaflet, OpenLayers, etc.).

Setup & Configuration

Using OSM Tiles with a Map Library

OSM tiles follow the standard {z}/{x}/{y}.png slippy map convention. The default tile server is tile.openstreetmap.org, but for production use a third-party tile provider or self-host tiles.

// Example with Leaflet
import L from "leaflet";

const map = L.map("map").setView([51.505, -0.09], 13);

L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
  maxZoom: 19,
  attribution:
    '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);

Nominatim API (Public Instance)

The public Nominatim instance at https://nominatim.openstreetmap.org is free but rate-limited to 1 request per second. Set a custom User-Agent header identifying your application — requests without one may be blocked.

const NOMINATIM_BASE = "https://nominatim.openstreetmap.org";

async function geocode(query) {
  const url = new URL("/search", NOMINATIM_BASE);
  url.searchParams.set("q", query);
  url.searchParams.set("format", "jsonv2");
  url.searchParams.set("limit", "5");

  const res = await fetch(url.toString(), {
    headers: { "User-Agent": "MyApp/1.0 (contact@example.com)" },
  });
  return res.json();
}

Self-Hosting Nominatim

For production workloads, self-host Nominatim using the official Docker image:

# Download planet extract (e.g., a country)
wget https://download.geofabrik.de/europe/germany-latest.osm.pbf

# Run Nominatim with Docker
docker run -it \
  -e PBF_URL=https://download.geofabrik.de/europe/germany-latest.osm.pbf \
  -e REPLICATION_URL=https://download.geofabrik.de/europe/germany-updates/ \
  -p 8080:8080 \
  --name nominatim \
  mediagis/nominatim:4.4

Core Patterns

Forward Geocoding

async function forwardGeocode(address) {
  const url = new URL("/search", NOMINATIM_BASE);
  url.searchParams.set("q", address);
  url.searchParams.set("format", "jsonv2");
  url.searchParams.set("addressdetails", "1");

  const res = await fetch(url.toString(), {
    headers: { "User-Agent": "MyApp/1.0" },
  });
  const results = await res.json();

  return results.map((r) => ({
    displayName: r.display_name,
    lat: parseFloat(r.lat),
    lon: parseFloat(r.lon),
    type: r.type,
    address: r.address,
  }));
}

Reverse Geocoding

async function reverseGeocode(lat, lon) {
  const url = new URL("/reverse", NOMINATIM_BASE);
  url.searchParams.set("lat", lat.toString());
  url.searchParams.set("lon", lon.toString());
  url.searchParams.set("format", "jsonv2");
  url.searchParams.set("zoom", "18"); // detail level: 18 = building

  const res = await fetch(url.toString(), {
    headers: { "User-Agent": "MyApp/1.0" },
  });
  return res.json();
}

Overpass API Queries

The Overpass API queries raw OSM data. The endpoint is https://overpass-api.de/api/interpreter.

async function findNearbyRestaurants(lat, lon, radiusMeters = 500) {
  const query = `
    [out:json][timeout:25];
    node["amenity"="restaurant"](around:${radiusMeters},${lat},${lon});
    out body;
  `;

  const res = await fetch("https://overpass-api.de/api/interpreter", {
    method: "POST",
    body: `data=${encodeURIComponent(query)}`,
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
  });
  const data = await res.json();
  return data.elements;
}

Structured Search with Nominatim

async function structuredSearch({ street, city, country, postalcode }) {
  const url = new URL("/search", NOMINATIM_BASE);
  if (street) url.searchParams.set("street", street);
  if (city) url.searchParams.set("city", city);
  if (country) url.searchParams.set("country", country);
  if (postalcode) url.searchParams.set("postalcode", postalcode);
  url.searchParams.set("format", "jsonv2");

  const res = await fetch(url.toString(), {
    headers: { "User-Agent": "MyApp/1.0" },
  });
  return res.json();
}

Best Practices

  • Always include proper OSM attribution on any map displaying OSM tiles — this is a legal requirement under the ODbL license.
  • Respect the Nominatim usage policy: maximum 1 request/second on the public instance, include a valid User-Agent, and do not bulk-geocode against the public server.
  • Self-host Nominatim or use a commercial provider (e.g., LocationIQ, Geoapify, OpenCage) for any production application with significant geocoding volume.

Common Pitfalls

  • Omitting the User-Agent header causes Nominatim to return 403 errors or silently block your IP.
  • Relying on the public tile server (tile.openstreetmap.org) for high-traffic production apps violates the OSM tile usage policy and may result in being blocked — use a commercial tile provider or self-host.

Install this skill directly: skilldb add maps-geolocation-services-skills

Get CLI access →