# Overture Maps API: full documentation
> Every page of https://www.overturemapsapi.com/docs in one file. Index: https://www.overturemapsapi.com/llms.txt. OpenAPI spec: https://www.overturemapsapi.com/openapi.json.
# Overture Maps API — REST API for Places, Buildings & Address Data
Source: https://www.overturemapsapi.com/docs/intro
> The OpenSource Overture Maps API is a simple REST API for Overture Maps data — query places, buildings, addresses and divisions without Athena, BigQuery or SQL. Hosted or self-hostable.
# What is the Overture Maps Data and Why does it need an API?
**[Overture Maps Foundation](https://overturemaps.org/)** is a phenomenal non-profit organization that provides free and open-source maps data to the public. This data is accessible as downloadable data dumps, which can be easily queried with tools like Athena, as well as through public datasets hosted in BigQuery and the Snowflake Marketplace.
However, working directly with these datasets requires a solid understanding of SQL, especially given their large size. Without careful querying, it's easy to incur high costs due to the extensive data volume. Experience in handling large datasets is essential to manage usage efficiently.
Introducing the Overture Maps API — a simple RESTful API designed for web developers to access Overture Maps data without needing to know Athena, BigQuery, or complex cost optimization strategies. This API abstracts the complexity, allowing developers to query the data easily and efficiently.
The API is available as a cloud service for testing and exploration. Additionally, a self-hosted version is offered for those who prefer to run it on their own infrastructure.
## Getting Started
Get started by **making API Requests** after **[Trying the Demo](https://demo.overturemapsapi.com)** and reviewing the **[API Documentation](../reference)**.
Then follow a **step-by-step guide** — for example, [building a local business directory](./guides/build-a-local-business-directory) for any country and category.
Prefer to run it yourself? The API is open source — **[deploy to your own infrastructure](./deploy-to-gcp)** with the GCP / BigQuery guide.
### Making API requests
Make some test requests to the API to see how it works e.g.
Download every **Starbucks** within 10km of New York City as a JSON file:
```bash
curl -o starbucks-near-nyc.json -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=40.7128' -d 'lng=-74.0060' -d 'radius=10000' -d 'brand_name=Starbucks'
```
Or see 10 **Tesco** stores near London in your terminal:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=51.5074' -d 'lng=-0.1278' -d 'radius=10000' -d 'brand_name=Tesco' -d 'limit=10'
```
:::tip The demo key is for nearby search
`DEMO-API-KEY` works for lat/lng radius queries around a few demo cities (New York, London, Paris, Sydney). For country-wide pulls and your own locations, [grab a free API key](https://www.overturemapsapi.com) — it only takes a minute.
:::
... and then explore the other **[./places endpoints](./api-endpoints/places)** and make API requests for Brands and Categories of places for each Country, or search by latitude and longitude.
Then build something cool with the data!
### Deploying to your own infrastructure
- [Deploying to Google Cloud Platform](./deploy-to-gcp/) - GCP has a $400 free credit for new users and this is enough to run the API and get a feel for how it works.
- AWS / Athena / Snowflake - Coming soon - please [contact us](mailto:aden@thatapicompany.com) if you would like to help with this.
### Who are we & why did we make it?
We are **[ThatAPICompany](https://thatapicompany.com/)** - a specialist dev Agency helping clients make amazing APIs that get used, and creating public APIs like TheCatAPI.com to help educate the next generation of developers how to use APIs to make amazing things!
We have built this API to make it easier for developer of any skill level to access the Overture Maps data, and to help the Overture Maps Foundation to get more people using their data.
---
# What Is Overture Maps? The Data, the Themes and How to Query It
Source: https://www.overturemapsapi.com/docs/what-is-overture-maps
> Overture Maps explained for developers: who makes it, the six themes (places, buildings, addresses, divisions, transportation, base), licences, release cadence, and how to query it without downloading GeoParquet.
# What is Overture Maps?
**Overture Maps** is a free, open map dataset of the whole world, published by the **Overture Maps Foundation**. It covers places of interest, building footprints, addresses, administrative boundaries, roads and the base layer of land and water. Overture combines data from OpenStreetMap, its member companies and other open sources into one schema, with stable IDs, and publishes a new release every month.
This page explains what's in it and how to get at it. If you want the short version: Overture publishes very large files for you to download and query yourself, and this API lets you ask for just the part you need instead.

Three Overture themes around Les Halles, Paris, each from a single API call. Map: OpenFreeMap, © OpenMapTiles, data © OpenStreetMap contributors.
## Who makes it
The Overture Maps Foundation was launched in December 2022 by Amazon Web Services, Meta, Microsoft and TomTom ([TomTom announcement](https://www.tomtom.com/newsroom/behind-the-map/the-overture-foundation-and-its-tech/)). It is a Joint Development Foundation project, an affiliate of the Linux Foundation ([Overture FAQ](https://overturemaps.org/about/faq/)). The data is open and free to use under the licences below.
## The six themes
Overture organises its data into six themes. As of the `2026-10-21.0` release, with schema v2.0.0 ([release notes](https://docs.overturemaps.org/blog/2026/10/21/release-notes/)):
| Theme | What it holds | Size (Oct 2026) | In this API |
| --- | --- | --- | --- |
| **Places** | Points of interest: shops, restaurants, services, landmarks, with categories, brands, websites and phone numbers | 88,418,121 places | [`/places`](./api-endpoints/places) |
| **Buildings** | Building footprints with height, floors, class and roof details | 2,533,842,612 buildings | [`/buildings`](./api-endpoints/buildings) |
| **Addresses** | Address points from national and local open sources | 475,263,892 addresses | [`/addresses`](./api-endpoints/addresses) |
| **Divisions** | Countries, regions, cities and neighbourhoods, with boundaries | | [`/divisions`](./api-endpoints/divisions) |
| **Transportation** | The road, rail and path network | | [`/transportation`](./api-endpoints/transportation) |
| **Base** | Land, water, land use and infrastructure | | [`/base`](./api-endpoints/base) |
Overture's release notes give totals per theme for places, buildings and addresses, and per feature type for the others.
Every feature carries a **GERS ID**, Overture's Global Entity Reference System identifier. It's meant to stay stable across releases, so you can join Overture data to your own records and follow a place or building over time ([What is GERS?](https://docs.overturemaps.org/gers/)).
## How often it changes
Overture publishes a new release every month ([Accessing the Overture catalog](https://docs.overturemaps.org/getting-data/cloud-sources/)). Each release can add places, retire old IDs and change the schema, so it's worth reading the [release notes](https://docs.overturemaps.org/blog/) or the [changelog](./changelog), where we summarise what each release means for API callers. Upcoming dates are on Overture's [release calendar](https://docs.overturemaps.org/release-calendar/).
## Licences
Licences differ by theme ([Overture attribution and licensing](https://docs.overturemaps.org/attribution/)):
- **Base, buildings, divisions and transportation:** ODbL.
- **Places:** set per data provider. Most is CDLA Permissive 2.0; Foursquare's contribution is Apache 2.0 and AllThePlaces' is CC0 1.0.
- **Addresses:** set by each country's source, ranging from CC0 1.0 to CC BY 4.0 and national open licences.
Every feature lists its sources, with their licence, in `sources[].license`. Overture asks for the attribution "Overture Maps Foundation, overturemaps.org".
## How to get the data
### Option 1: download and query it yourself
Overture publishes its data as **GeoParquet** files on Amazon S3 and Microsoft Azure, and partners host copies on platforms such as BigQuery, Databricks and Snowflake ([Overture documentation](https://docs.overturemaps.org/)). Overture's own quick-start tools are the Python CLI and DuckDB, and you can browse a release in the [Overture Explorer](https://explore.overturemaps.org/).
That suits analysts working with a whole country or theme at once. It means picking the right release, writing spatial SQL, and handling files measured in gigabytes, then doing it all again every month.
### Option 2: ask an API for just what you need
This API runs Overture's data behind a normal REST interface. You send a location and a few filters and get JSON or GeoJSON back, for example every café within 800 m of a point:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=-33.8910' -d 'lng=151.2769' -d 'radius=800' -d 'categories=cafe' -d 'limit=5'
```
There are no files to download, no SQL, and no monthly re-import: the API moves to new Overture releases for you, and the [changelog](./changelog) records each one. On top of the raw data it adds brand logos and websites from Wikidata ([enriched data](./enriched-data)), filters such as `has_contact` and `operating_status`, and type-ahead search by name.
`DEMO-API-KEY` works near New York, London, Paris and Sydney. [Sign up for a free key](https://account.overturemapsapi.com) to search anywhere.
## Where to go next
- **See the data on a map:** the [live demo](https://demo.overturemapsapi.com) lets you explore places, brands, buildings and boundaries without writing code.
- **Make your first request:** [Getting started](./intro).
- **Build something:** the [guides](./category/guides), such as [Build a "what's near me" feature](./guides/build-a-whats-near-me-feature) and [Map every location of a retail chain](./guides/map-every-location-of-a-retail-chain).
---
Source: https://www.overturemapsapi.com/docs/authentication
# Authentication
## Demo Key Usage
The API requires an API key to be passed in the header of each request as the `x-api-key` header. For General API usage, you can use the following key:
```
x-api-key: DEMO-API-KEY
```
This will limit the number of requests you can make to the API and the available endpoints, but it is enough to get started and see how the API works.
## Cloud Service
The API in the example requests is a cloud service, so you don't need to worry about setting up servers, databases, or any other infrastructure. You can just start using the API right away.
However this is an expensive service to provide so whilst we are happy to pick up the bill for the demo, we will need to charge for usage of the expensive endpoints like the `./buildings` endpoint, and including building shapes geometry in the `./places` response.
If you would like to use the API for a commercial project, please [contact us](mailto:aden@thatapicompany.com) to discuss pricing.
## Self-Hosted
If you want to run the API on your own infrastructure, you can do so by following the instructions in the [Deploy to GCP](./deploy-to-gcp) section.
The to give each yof your users API Keys, you can use the [API Key Management](./api-key-management) section to create and manage API keys for your users, add rate-limiting, and other security features.
---
# Use the Overture Maps API with Claude, ChatGPT, Cursor and other AI tools
Source: https://www.overturemapsapi.com/docs/use-with-ai-tools
> Connect Overture Maps places, buildings and boundaries to Claude, ChatGPT, Cursor and other AI assistants with the MCP server or the OpenAPI spec, and point your coding assistant at llms.txt.
# Use with AI tools
There are three ways to put Overture Maps data in front of an AI assistant, depending on what you want it to do:
| You want the assistant to… | Use |
| --- | --- |
| Answer questions with live Overture data ("find cafes near the Eiffel Tower") | The [MCP server](#mcp-server-claude-cursor-and-other-mcp-clients) |
| Call the API from a custom GPT or an agent framework | The [OpenAPI spec](#openapi-spec-custom-gpts-and-agent-frameworks) |
| Write code against the API for you | The [docs for AI tools](#docs-for-coding-assistants) |
All three need an API key. The public `DEMO-API-KEY` works for small searches near New York, London, Paris and Bondi Beach. [Sign up for a free key](https://account.overturemapsapi.com) to search anywhere.
## MCP server (Claude, Cursor and other MCP clients)
[`@thatapicompany/overture-maps-mcp`](https://www.npmjs.com/package/@thatapicompany/overture-maps-mcp) is an open-source [Model Context Protocol](https://modelcontextprotocol.io) server with five read-only tools: `search_places`, `list_place_categories`, `list_place_countries`, `search_divisions` and `find_buildings`. Its responses are compact summaries rather than full GeoJSON, so they don't fill up the assistant's context.
### Claude Desktop
Add this to `claude_desktop_config.json` (Settings → Developer → Edit Config), then restart Claude:
```json
{
"mcpServers": {
"overture-maps": {
"command": "npx",
"args": ["-y", "@thatapicompany/overture-maps-mcp"],
"env": { "OVERTURE_API_KEY": "your-api-key" }
}
}
}
```
### Claude Code
```bash
claude mcp add overture-maps --env OVERTURE_API_KEY=your-api-key -- npx -y @thatapicompany/overture-maps-mcp
```
### Cursor and other MCP clients
Any client that runs local (stdio) MCP servers works the same way. In Cursor, add the `mcpServers` block above to `~/.cursor/mcp.json`. Elsewhere, run `npx -y @thatapicompany/overture-maps-mcp` with `OVERTURE_API_KEY` set in the environment.
Then ask things like:
- "Find pharmacies within 1 km of King's Cross that have a website."
- "Which administrative region is Springfield, Illinois in?"
- "How tall are the buildings around St Paul's Cathedral?"
## OpenAPI spec (custom GPTs and agent frameworks)
The spec lives at [`https://api.overturemapsapi.com/api-docs.json`](https://api.overturemapsapi.com/api-docs.json), with a copy at [`https://www.overturemapsapi.com/openapi.json`](https://www.overturemapsapi.com/openapi.json). Every operation has a readable `operationId` (`searchPlaces`, `listPlaceCategories`, `searchBuildings` and so on) and a description of when to use it, so tool-calling models pick the right endpoint.
**ChatGPT custom GPT:** in the GPT editor, open *Configure → Actions → Create new action*, choose *Import from URL* and paste the spec URL. Set *Authentication* to *API Key*, auth type *Custom*, header name `x-api-key`.
**Agent frameworks:** most can turn an OpenAPI spec into tools directly. Send the key in the `x-api-key` header on every request.
## Docs for coding assistants
If you're asking Claude, ChatGPT, Copilot or Cursor to write code against the API, give it the docs in a form it reads well:
- [`/llms.txt`](https://www.overturemapsapi.com/llms.txt): a short index of the API and these docs
- [`/llms-full.txt`](https://www.overturemapsapi.com/llms-full.txt): every docs page in one file
- Any docs page as plain markdown: add `.md` to its URL, for example [`/docs/api-endpoints/places.md`](https://www.overturemapsapi.com/docs/api-endpoints/places.md)
Every endpoint page has a parameter reference and a Python example you can paste into a prompt.
## Tips for good results
- **Ask for a place and a radius.** Searches by `lat`, `lng` and `radius` are the fastest and work on every plan. If you only have a place name, the assistant can look it up with `GET /divisions?name=...` first.
- **Let it find the category first.** `GET /places/categories` returns the category values that exist in an area, so the assistant doesn't guess (`coffee_shop`, not `coffee shops`).
- **Let it read the error.** Error responses explain how to fix the request, for example "search by lat/lng instead" or "this query needs a paid plan". Assistants can pass that straight back to you.
- **Watch your quota.** Agents can make many calls quickly. Each response carries `X-Quota-Remaining`, and a `429` comes with a `Retry-After` header. See [Usage limits](./limits).
---
# Overture Places API — Query POIs by Category, Brand, Country & Location
Source: https://www.overturemapsapi.com/docs/api-endpoints/places
> Query Overture Maps places (POIs) via a REST API. Filter by category, taxonomy, brand, country or lat/long radius, get contact details, and paginate whole countries.
# GET ./places
Allows you to get all the Places in the Overture Maps Database. You can filter by Lat/Long, Brand, Country and Categories.
## Query Parameters
Consult the [Open API documentation](/reference) for the full list of query parameters that can be used to filter the results. Here are some examples
### By Latitude / Longitude / Radius
Query params:
`lat`, `lng`, `radius`
Example Request to GET all the 'categories=cafe' within 2000m of Bondi Beach:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places?lat=-33.8910&lng=151.2769&radius=2000&categories=cafe'
```
### Categories
e.g. `categories=water_park` - Comma separated string of Categories to filter
Example Request to GET all the Water Parks within 25km of Sydney
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=-33.8688' -d 'lng=151.2093' -d 'radius=25000' -d 'categories=water_park'
```
:::info Overture is retiring `categories` in September 2026
Overture replaces the places `categories` property with `basic_category` + `taxonomy` upstream in September 2026. **Nothing breaks in this API**: the `categories` response field and filter are staying (derived from the taxonomy once the upstream column is gone), and the `categories` filter already matches values from both vocabularies. Note the *values* inside `categories` will shift to the new taxonomy vocabulary at that point. If you match on specific category strings, we recommend the `taxonomy` filter below.
:::
### Taxonomy (hierarchy-aware)
`taxonomy` — comma-separated Overture taxonomy categories. Matches the primary category **or any ancestor in the taxonomy hierarchy**, so one value covers every descendant: `taxonomy=food_and_drink` matches every restaurant, café, bar and bakery.
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=40.7128' -d 'lng=-74.0060' -d 'radius=1500' -d 'taxonomy=food_and_drink'
```
Every place also returns its full taxonomy in the response:
```json
"taxonomy": {
"primary": "coffee_shop",
"hierarchy": ["food_and_drink", "non_alcoholic_beverage_venue", "coffee_shop"],
"alternates": []
},
"basic_category": "coffee_shop"
```
### Operating Status
`operating_status` — filter by operating status, e.g. `open` or `permanently_closed`. Places without signals have a null status and are excluded when this filter is used. The field is also present on every place response.
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=51.5074' -d 'lng=-0.1278' -d 'radius=1000' -d 'operating_status=open'
```
### Brand Details (logos, websites) via Wikidata
`enrichment_fields=brand` — adds an `ext_brand` object to branded places with the brand's logo URL, official website, industry and parent organisation, sourced from Wikidata (CC0):
```json
"ext_brand": {
"label": "Bank of America",
"logo_url": "http://commons.wikimedia.org/wiki/Special:FilePath/Bank%20of%20America%20logo.svg",
"website": "https://www.bankofamerica.com/",
"industry": "financial sector"
}
```
Logo URLs are Wikimedia Commons links — append `?width=200` for a resized image. Present where Overture links the place's brand to a Wikidata QID (~1.5M places across ~3,000 chains).
### Contact details (websites, phones, emails, socials)
Every place returns its contact details **by default** — no special parameter is required:
```json
"websites": ["https://www.example-cafe.co.uk"],
"phones": ["+441234567890"],
"emails": ["hello@example-cafe.co.uk"],
"socials": ["https://www.facebook.com/examplecafe"]
```
:::tip These are returned by default
You do **not** need `enrichment_fields` for websites or phones. `enrichment_fields=brand` is only for the Wikidata brand layer (logos, industry, parent). The four contact arrays above are always in the response when Overture has them.
:::
Coverage varies by country and category. In well-mapped markets, websites and phones are populated for the large majority of businesses; emails and socials are sparser.
#### Filter to places that have contact details — `has_contact`
`has_contact` returns **only** places that already have at least one of the listed contact fields. It's comma-separated and matched with **OR**, which is ideal when you only want businesses you can actually reach or verify:
```bash
# only places with a website
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=51.5074' -d 'lng=-0.1278' -d 'radius=2000' -d 'categories=restaurant' -d 'has_contact=website'
# places with a website OR a social link (catches businesses that use a Facebook page instead of a site)
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=51.5074' -d 'lng=-0.1278' -d 'radius=2000' -d 'categories=restaurant' -d 'has_contact=website,social'
```
Allowed values: `website`, `phone`, `email`, `social`. The `Pagination-Count` header reflects the **filtered** total, so you can see how many places match before paging through them.
### Brands / Retail Chain
e.g. `brand_name=H&M` - The name of the Brand to filter by e.g. `Uniqlo`, `McDonalds`
Example Request to GET Uniqlo stores within 10km of New York City
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=40.7128' -d 'lng=-74.0060' -d 'radius=10000' -d 'brand_name=Uniqlo'
```
Example of a response for a 7-Eleven store near New York City:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=40.7128' -d 'lng=-74.0060' -d 'radius=10000' -d 'limit=1' -d 'brand_name=7-Eleven'
```
### By Name
`name` filters to places whose own name matches — an additional filter on top of your lat/lng/radius (or country) search, not a standalone text search. It's a case-insensitive exact match against the place's primary name (and any localized common name, where Overture has one):
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=40.785091' -d 'lng=-73.968285' -d 'radius=3000' -d 'name=Central Park'
```
Combine it with other filters as needed, e.g. `brand_name` to pin a chain down to one specific location by name.
### By Partial Name (type-ahead)
`name_prefix` finds places whose name starts with what someone has typed so far, or has a word that does. `blue bo` and `bottle` both match "Blue Bottle Coffee". It's case-insensitive and needs at least 2 characters.
With `lat`/`lng`, results come back nearest first, so a small `limit` gives you the closest few matches for a suggestions dropdown:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=40.7128' -d 'lng=-74.0060' -d 'radius=5000' -d 'name_prefix=blue bo' -d 'limit=5'
```
Each call is a normal request and counts towards your monthly allowance, so debounce keystrokes (around 300 ms) and wait for 2 or 3 characters before searching. Responses typically take a second or two, so show a loading state.
### By Country
e.g. `?country=US` - The ISO 3166-1 alpha-2 country code to filter by. e.g. `US`, `GB`, `FR` (UK is GB).
Country-level queries (a `country` with no `lat`/`lng`) are available on [paid plans](/pricing) and must include a narrowing filter such as `categories`, `taxonomy`, `brand_name` or `name`. On the Free plan and with the demo key, search by `lat`, `lng` and `radius` (up to 25 km) instead; you can still add `country` alongside them as an extra filter. A Free key sending a country-level query gets a `403` explaining this. See [Usage limits](../limits).
Example Request to GET 10 restaurants in the US (paid plan):
```bash
curl -H "x-api-key: YOUR-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'country=US' -d 'categories=restaurant' -d 'limit=10'
```
### Pagination
`limit` (page size) + `page` (0-indexed). Totals come back in the `Pagination-Count` / `Pagination-Page` / `Pagination-Limit` response headers.
[Find out more - Pagination](../pagination)
### Enriched Responses
e.g. Conflating with the OpenStreetMap equivalent
[Find out more - Enriched Data Options](../enriched-data)
## Parameter reference
Generated from the [OpenAPI spec](https://api.overturemapsapi.com/api-docs.json) (operation `searchPlaces`). Find points of interest near a point, or across a country on paid plans. Best query: `lat` + `lng` + `radius` (metres) with optional `categories`, `taxonomy`, `brand_name` or `name_prefix`. Country-level searches (`country` with no lat/lng) need a paid plan and a narrowing filter. Get valid category values from `GET /places/categories`. Page with `limit` and `page` and read the total from the `Pagination-Count` header. `enrichment_fields=brand` adds Wikidata brand details. The demo key is limited to small lat/lng searches.
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | number | | Latitude coordinate. Required if country code is not provided. |
| `lng` | number | | Longitude coordinate. Required if country code is not provided. |
| `radius` | integer | `1000` | Search radius in meters, defaulting to 1000 meters if not provided. |
| `limit` | integer | `100` | Limit on the number of results returned, defaulting to 100 if not provided. Use `page` to paginate for more. |
| `page` | integer | `0` | Page number for pagination (0-indexed), used together with `limit` as the page size. Responses include Pagination-Count (total matching results), Pagination-Page and Pagination-Limit headers. Defaults to 0. |
| `format` | `json`, `csv`, `geojson` | `json` | Response format, defaulting to JSON. Options are "json", "csv", or "geojson". |
| `includes` | string | | Array of fields to include in the properties field of the response, provided as a comma-separated string. Any fields not in the list will be excluded from the properties object. This is used to make the response lighter so your application is faster. |
| `country` | string | | ISO 3166 country code consisting of 2 characters. Required if lat/lng are not provided. |
| `source` | string | | Filter places to only those with a source dataset matching this value. |
| `brand_wikidata` | string | | Wikidata brand ID associated with the place. |
| `brand_name` | string | | Brand name associated with the place. |
| `name` | string | | Filter by the place's own name — an additional filter on top of lat/lng/radius (or country) search, not a standalone text search. Case-insensitive exact match against names.primary or any localized value in names.common. |
| `name_prefix` | string | | Partial-name search for type-ahead: matches places whose primary name starts with this text, or has a word that does (so "blue bo" and "bottle" both match "Blue Bottle Coffee"). Case-insensitive, at least 2 characters. Combine with lat/lng/radius and a small limit: results come nearest first. |
| `min_confidence` | number | `0.5` | Minimum confidence score for the places to be returned, defaulting to 0.5 if not provided. |
| `categories` | string | | Array of category names, provided as a comma-separated string. Matches the legacy Overture categories vocabulary and the newer taxonomy/basic_category vocabulary. |
| `taxonomy` | string | | Array of Overture taxonomy categories, provided as a comma-separated string. Matches the primary category or any ancestor in the taxonomy hierarchy, so e.g. "food_and_drink" matches every descendant category. |
| `operating_status` | string | | Filter by operating status, e.g. "open" or "permanently_closed". Places without signals have a null operating status and are excluded when this filter is used. |
| `enrichment_fields` | string | | Comma-separated list of enrichment fields to include. Use "brand" for Wikidata-sourced brand details (logo, website, industry, parent) on branded places. |
| `has_contact` | string | | Return only places that already have at least one of the listed contact fields. Comma-separated, matched with OR: e.g. "website" returns places with a website; "website,social" returns places with a website OR a social link. Useful for filtering to businesses you can verify. Allowed values: website, phone, email, social. |
## Python example
```python
import requests
resp = requests.get(
"https://api.overturemapsapi.com/places",
headers={"x-api-key": "DEMO-API-KEY"}, # use your own key outside the demo cities
params={
"lat": 51.5138,
"lng": -0.0984,
"radius": 500,
"categories": "cafe",
"limit": 10,
},
timeout=30,
)
if resp.status_code == 429:
raise SystemExit(f"Rate limited, retry in {resp.headers['Retry-After']}s")
resp.raise_for_status() # 4xx bodies are {"statusCode", "message"}; the message says how to fix the request
results = resp.json()
print(resp.headers.get("Pagination-Count"), "results in total")
for place in results:
print(place["properties"]["names"]["primary"], place["properties"].get("categories", {}).get("primary"))
```
Results are paginated: pass `page` (0-indexed) and `limit`, and read the total from the `Pagination-Count` header. See [Pagination](/docs/pagination) and [Usage limits](/docs/limits).
---
# Overture Place Categories API — List POI Categories
Source: https://www.overturemapsapi.com/docs/api-endpoints/places-categories
> List the place categories available in the Overture Maps dataset for a country, via the REST API.
# GET ./places/categories
Example Request to GET the Categories of all Brands in the France:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places/categories' \
-d 'country=FR'
```
## Counts
- `places` - The number of places in the category
- `brands` - The number of brands in the category
## Response Example
```JSON
[{"primary":"beauty_salon","counts":{"places":67490,"brands":227}}]
```
## Parameter reference
Generated from the [OpenAPI spec](https://api.overturemapsapi.com/api-docs.json) (operation `listPlaceCategories`). List the place categories present in a country or a lat/lng area, with place and brand counts. Use it to find valid values for the `categories` filter on `GET /places` (for example `cafe`, `pharmacy`, `restaurant`).
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | number | | Latitude coordinate. Required if country code is not provided. |
| `lng` | number | | Longitude coordinate. Required if country code is not provided. |
| `radius` | integer | `1000` | Search radius in meters, defaulting to 1000 meters if not provided. |
| `limit` | integer | `100` | Limit on the number of results returned, defaulting to 100 if not provided. Use `page` to paginate for more. |
| `page` | integer | `0` | Page number for pagination (0-indexed), used together with `limit` as the page size. Responses include Pagination-Count (total matching results), Pagination-Page and Pagination-Limit headers. Defaults to 0. |
| `format` | `json`, `csv`, `geojson` | `json` | Response format, defaulting to JSON. Options are "json", "csv", or "geojson". |
| `includes` | string | | Array of fields to include in the properties field of the response, provided as a comma-separated string. Any fields not in the list will be excluded from the properties object. This is used to make the response lighter so your application is faster. |
| `country` | string | | ISO 3166 country code consisting of 2 characters. |
## Python example
```python
import requests
resp = requests.get(
"https://api.overturemapsapi.com/places/categories",
headers={"x-api-key": "DEMO-API-KEY"}, # use your own key outside the demo cities
params={
"lat": 51.5138,
"lng": -0.0984,
"radius": 1000,
"limit": 20,
},
timeout=30,
)
if resp.status_code == 429:
raise SystemExit(f"Rate limited, retry in {resp.headers['Retry-After']}s")
resp.raise_for_status() # 4xx bodies are {"statusCode", "message"}; the message says how to fix the request
results = resp.json()
print(resp.headers.get("Pagination-Count"), "results in total")
for category in results:
print(category["primary"], category["counts"]["places"])
```
Results are paginated: pass `page` (0-indexed) and `limit`, and read the total from the `Pagination-Count` header. See [Pagination](/docs/pagination) and [Usage limits](/docs/limits).
---
Source: https://www.overturemapsapi.com/docs/api-endpoints/places-brands
# GET ./places/brands
Returns every brand (retail chain, franchise, operator) present in the search area, with a count of its places — and, where the brand is linked to Wikidata, its **logo, official website, industry and parent organisation**.
Example Request to GET all Brands in the US:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places/brands' \
-d 'country=US'
```
## Brand Details from Wikidata
Brands with a Wikidata QID are automatically enriched — no extra parameters needed:
```json
{
"names": { "primary": "Bank of America" },
"wikidata": "Q487907",
"ext_wikidata_label": "Bank of America",
"ext_logo_url": "http://commons.wikimedia.org/wiki/Special:FilePath/Bank%20of%20America%20logo.svg",
"ext_website": "https://www.bankofamerica.com/",
"ext_industry": "financial sector",
"counts": { "places": 12 }
}
```
- `ext_logo_url` is a Wikimedia Commons link; append `?width=200` for a resized image, ready for an `
` tag
- `ext_parent` names the parent organisation where known
- Wikidata data is CC0 (no attribution required); logo *usage* is subject to normal trademark law
- Fields are simply absent for brands without a Wikidata link
To fetch the places of a specific brand, use `/places` with `brand_wikidata` (preferred) or `brand_name` — see [Places](./places).
## Parameter reference
Generated from the [OpenAPI spec](https://api.overturemapsapi.com/api-docs.json) (operation `listPlaceBrands`). List brands (chains) with how many places each has, for a country or a lat/lng area. Use it to find the exact `brand_name` or `brand_wikidata` value to pass to `GET /places`, for example to map every location of a retail chain.
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | number | | Latitude coordinate. Required if country code is not provided. |
| `lng` | number | | Longitude coordinate. Required if country code is not provided. |
| `radius` | integer | `1000` | Search radius in meters, defaulting to 1000 meters if not provided. |
| `limit` | integer | `100` | Limit on the number of results returned, defaulting to 100 if not provided. Use `page` to paginate for more. |
| `page` | integer | `0` | Page number for pagination (0-indexed), used together with `limit` as the page size. Responses include Pagination-Count (total matching results), Pagination-Page and Pagination-Limit headers. Defaults to 0. |
| `format` | `json`, `csv`, `geojson` | `json` | Response format, defaulting to JSON. Options are "json", "csv", or "geojson". |
| `includes` | string | | Array of fields to include in the properties field of the response, provided as a comma-separated string. Any fields not in the list will be excluded from the properties object. This is used to make the response lighter so your application is faster. |
| `country` | string | | ISO 3166 country code consisting of 2 characters. Required if lat/lng are not provided. |
| `categories` | string | | Array of category names, provided as a comma-separated string. |
## Python example
```python
import requests
resp = requests.get(
"https://api.overturemapsapi.com/places/brands",
headers={"x-api-key": "DEMO-API-KEY"}, # use your own key outside the demo cities
params={
"lat": 51.5138,
"lng": -0.0984,
"radius": 1000,
"limit": 20,
},
timeout=30,
)
if resp.status_code == 429:
raise SystemExit(f"Rate limited, retry in {resp.headers['Retry-After']}s")
resp.raise_for_status() # 4xx bodies are {"statusCode", "message"}; the message says how to fix the request
results = resp.json()
print(resp.headers.get("Pagination-Count"), "results in total")
for brand in results:
name = (brand.get("names") or {}).get("primary")
if name: # places without a brand are counted under an empty name
print(name, brand.get("wikidata"), brand["counts"]["places"])
```
Results are paginated: pass `page` (0-indexed) and `limit`, and read the total from the `Pagination-Count` header. See [Pagination](/docs/pagination) and [Usage limits](/docs/limits).
---
# Overture Places by Country — List Countries & Place Counts
Source: https://www.overturemapsapi.com/docs/api-endpoints/places-countries
> List every country in the Overture Maps places dataset and how many places each has, via a simple REST API call.
# GET ./places/countries
Example Request to GET all the Countries in the Overture Maps Database:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places/countries'
```
## Counts
- `places` - The number of places in the category
- `brands` - The number of brands in the category
## Response Example
example response for the number of places and brands in South Africa:
```JSON
[{"country":"ZA","counts":{"places":67490,"brands":227}}]
```
## Parameter reference
Generated from the [OpenAPI spec](https://api.overturemapsapi.com/api-docs.json) (operation `listPlaceCountries`). List the countries that have places, with place and brand counts for each. Use it to check coverage before a country-level search.
This endpoint takes no query parameters.
## Python example
```python
import requests
resp = requests.get(
"https://api.overturemapsapi.com/places/countries",
headers={"x-api-key": "DEMO-API-KEY"}, # use your own key outside the demo cities
timeout=30,
)
if resp.status_code == 429:
raise SystemExit(f"Rate limited, retry in {resp.headers['Retry-After']}s")
resp.raise_for_status() # 4xx bodies are {"statusCode", "message"}; the message says how to fix the request
results = resp.json()
print(resp.headers.get("Pagination-Count"), "results in total")
for country in results[:10]:
print(country["country"], country["counts"]["places"])
```
Results are paginated: pass `page` (0-indexed) and `limit`, and read the total from the `Pagination-Count` header. See [Pagination](/docs/pagination) and [Usage limits](/docs/limits).
---
Source: https://www.overturemapsapi.com/docs/api-endpoints/places-buildings
# GET ./places/buildings
Merges the 'places' and 'buildings' themes to return places with building shapes, using the nearest building shape to the place. This will inherently not be 100% accurate, as the building shape may not match the place exactly, but it will give a good approximation of the building shape for the place.
Reasons for missmatches between the place and building shapes include:
- the place being in a building with multiple businesses
- the place being in a building with multiple floors
the lat / lng of the place being in the street
- the building shape being underground (e.g. a subway station)
Building shapes is quite an expensive dataset, so optimsiations have been taken to reduce the cost of this endpoint.
Ways to improve in future versions include:
- using the OSM dataset as the primary source, instead of the Microsoft from space building shapes dataset. Getting the lat/lng of the place from OSM and using the nearest building shape from OSM.
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places/buildings?lat=40.7128&lng=-74.006&radius=1000&categories=cafe'
```
## Response Example
Example for Places with Buildings in New York
```JSON
[ {
"id": "08f2a107059b12890338d9477e377478",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-74.0556772,
40.7300326
],
[
-74.0555967,
40.7299798
],
[
-74.0554715,
40.7300894
],
[
-74.0555321,
40.7301583
],
[
-74.0556772,
40.7300326
]
]
]
},
"properties": {
"id": "08f2a107059b12890338d9477e377478",
"version": 0,
"sources": [
{
"property": "",
"dataset": "meta",
"record_id": "510009399165285",
"update_time": "2024-08-02T00:00:00.000Z",
"confidence": null
}
],
"names": {
"primary": "Newark Super Market",
"common": null,
"rules": null
},
"categories": {
"primary": "supermarket",
"alternate": []
},
"confidence": 0.9380668566986265,
"websites": [],
"socials": [
"https://www.facebook.com/510009399165285"
],
"emails": [],
"phones": [
"+12012220019"
],
"addresses": [
{
"freeform": "515 Newark Ave",
"locality": "Jersey City",
"postcode": "07306-1347",
"region": "NJ",
"country": "US"
}
],
"building": {
"id": "08b2a107059b1fff0200537c72a74492",
"distance": 0,
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-74.0556772,
40.7300326
],
[
-74.0555967,
40.7299798
],
[
-74.0554715,
40.7300894
],
[
-74.0555321,
40.7301583
],
[
-74.0556772,
40.7300326
]
]
]
}
},
"ext_name": "Newark Super Market",
"ext_building": {
"id": "08b2a107059b1fff0200537c72a74492",
"distance": 0,
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-74.0556772,
40.7300326
],
[
-74.0555967,
40.7299798
],
[
-74.0554715,
40.7300894
],
[
-74.0555321,
40.7301583
],
[
-74.0556772,
40.7300326
]
]
]
}
},
"ext_place_geometry": {
"type": "Point",
"coordinates": [
-74.0554888,
40.7300943
]
}
}
}]
```
## Pagination
Supports `limit` + `page` with `Pagination-Count` / `Pagination-Page` / `Pagination-Limit` response headers — see [Pagination](../pagination).
## Parameter reference
Generated from the [OpenAPI spec](https://api.overturemapsapi.com/api-docs.json) (operation `searchPlacesWithBuildings`). Same filters as `GET /places`, and each place also carries the footprint of the building it sits in. Use it when you need the shape or size of the premises, for example for site analysis or rooftop mapping. Slower than `/places`, so keep the radius small.
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | number | | Latitude coordinate. Required if country code is not provided. |
| `lng` | number | | Longitude coordinate. Required if country code is not provided. |
| `radius` | integer | `1000` | Search radius in meters, defaulting to 1000 meters if not provided. Capped lower than /places because building lookups are more expensive per meter of radius. |
| `limit` | integer | `100` | Limit on the number of results returned, defaulting to 100 if not provided. Use `page` to paginate for more. |
| `page` | integer | `0` | Page number for pagination (0-indexed), used together with `limit` as the page size. Responses include Pagination-Count (total matching results), Pagination-Page and Pagination-Limit headers. Defaults to 0. |
| `format` | `json`, `csv`, `geojson` | `json` | Response format, defaulting to JSON. Options are "json", "csv", or "geojson". |
| `includes` | string | | Array of fields to include in the properties field of the response, provided as a comma-separated string. Any fields not in the list will be excluded from the properties object. This is used to make the response lighter so your application is faster. |
| `country` | string | | ISO 3166 country code consisting of 2 characters. Required if lat/lng are not provided. |
| `source` | string | | Filter places to only those with a source dataset matching this value. |
| `brand_wikidata` | string | | Wikidata brand ID associated with the place. |
| `brand_name` | string | | Brand name associated with the place. |
| `name` | string | | Filter by the place's own name — an additional filter on top of lat/lng/radius (or country) search, not a standalone text search. Case-insensitive exact match against names.primary or any localized value in names.common. |
| `name_prefix` | string | | Partial-name search for type-ahead: matches places whose primary name starts with this text, or has a word that does (so "blue bo" and "bottle" both match "Blue Bottle Coffee"). Case-insensitive, at least 2 characters. Combine with lat/lng/radius and a small limit: results come nearest first. |
| `min_confidence` | number | `0.5` | Minimum confidence score for the places to be returned, defaulting to 0.5 if not provided. |
| `categories` | string | | Array of category names, provided as a comma-separated string. Matches the legacy Overture categories vocabulary and the newer taxonomy/basic_category vocabulary. |
| `taxonomy` | string | | Array of Overture taxonomy categories, provided as a comma-separated string. Matches the primary category or any ancestor in the taxonomy hierarchy, so e.g. "food_and_drink" matches every descendant category. |
| `operating_status` | string | | Filter by operating status, e.g. "open" or "permanently_closed". Places without signals have a null operating status and are excluded when this filter is used. |
| `enrichment_fields` | string | | Comma-separated list of enrichment fields to include. Use "brand" for Wikidata-sourced brand details (logo, website, industry, parent) on branded places. |
| `has_contact` | string | | Return only places that already have at least one of the listed contact fields. Comma-separated, matched with OR: e.g. "website" returns places with a website; "website,social" returns places with a website OR a social link. Useful for filtering to businesses you can verify. Allowed values: website, phone, email, social. |
## Python example
```python
import requests
resp = requests.get(
"https://api.overturemapsapi.com/places/buildings",
headers={"x-api-key": "DEMO-API-KEY"}, # use your own key outside the demo cities
params={
"lat": 51.5138,
"lng": -0.0984,
"radius": 300,
"categories": "cafe",
"limit": 10,
},
timeout=30,
)
if resp.status_code == 429:
raise SystemExit(f"Rate limited, retry in {resp.headers['Retry-After']}s")
resp.raise_for_status() # 4xx bodies are {"statusCode", "message"}; the message says how to fix the request
results = resp.json()
print(resp.headers.get("Pagination-Count"), "results in total")
for place in results:
print(place["properties"]["names"]["primary"], place["geometry"]["type"])
```
Results are paginated: pass `page` (0-indexed) and `limit`, and read the total from the `Pagination-Count` header. See [Pagination](/docs/pagination) and [Usage limits](/docs/limits).
---
# Overture Buildings API: Footprints, Heights and Floors by Location
Source: https://www.overturemapsapi.com/docs/api-endpoints/buildings
> Query Overture Maps building footprints by lat/lng radius. Polygons with height, floors, class, roof and facade details, as JSON or GeoJSON, with pagination. No GeoParquet downloads.
# GET ./buildings
Returns Overture Maps **building footprints** around a point: the polygon for each building, plus whatever Overture knows about it, such as height, number of floors, class, roof shape and facade material. It's the buildings theme of Overture on its own. If you want *places* matched to the building they sit in, use [`/places/buildings`](./places-buildings) instead.

376 buildings within 450 m of St Paul's, from one /buildings request with format=geojson. Map: OpenFreeMap, © OpenMapTiles, data © OpenStreetMap contributors.
## Query parameters
| Parameter | Description |
| --- | --- |
| `lat`, `lng` | Centre of the search. Required. |
| `radius` | Search radius in metres. Default `1000`, maximum `25000`. |
| `limit` | Results per page. Default `100`, maximum `25000`. |
| `page` | Page number, 0-indexed. See [Pagination](/docs/pagination). |
| `format` | `json` (default) or `geojson`. |
| `includes` | Comma-separated list of property names to return, to keep responses small. Properties with no value are left out. |
The full parameter list is in the [OpenAPI reference](/reference).
## Example
Buildings within 200 m of St Paul's Cathedral in London, first three results:
```bash
curl -i -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/buildings' \
-d 'lat=51.5138' -d 'lng=-0.0984' -d 'radius=200' -d 'limit=3'
```
```
Pagination-Count: 43
Pagination-Page: 0
Pagination-Limit: 3
```
`Pagination-Count` is the total for the search: 43 buildings within 200 m. Each result is a building with a `Polygon` (or `MultiPolygon`) geometry:
```json
{
"id": "551debe5-6bb3-4c63-809d-c861141916dd",
"geometry": { "type": "Polygon", "coordinates": [[[-0.0990366, 51.5134959], "..."]] },
"properties": {
"names": { "primary": "St Paul's Cathedral" },
"subtype": "religious",
"class": "cathedral",
"height": 35,
"has_parts": true,
"is_underground": false,
"num_floors": null,
"roof_shape": null,
"facade_material": null,
"sources": [{ "dataset": "OpenStreetMap", "license": "ODbL-1.0" }],
"theme": "buildings",
"type": "building"
}
}
```
The `id` is the building's [GERS](https://docs.overturemaps.org/gers/) ID, the same stable identifier Overture uses everywhere, so you can join it to other Overture data.
## What's in the properties
How much each building carries depends on its sources. In the St Paul's example, the cathedral has a `height` but no `num_floors`, while St Paul's Chapterhouse next door has `num_floors: 3`, a height of about 26.6 m, `facade_material: "brick"` and `roof_shape: "mansard"`. Many smaller buildings have only a footprint. Within 450 m of St Paul's, 68 of 376 buildings have a `height` and 217 have `num_floors`.
| Property | What it is |
| --- | --- |
| `names.primary` | The building's name, where it has one |
| `subtype`, `class` | What the building is for, from broad (`religious`) to specific (`cathedral`) |
| `height`, `min_height` | Height in metres, and where the building starts above ground |
| `num_floors`, `num_floors_underground`, `min_floor` | Floor counts |
| `roof_shape`, `roof_material`, `roof_color`, `roof_height`, `roof_direction`, `roof_orientation` | Roof details |
| `facade_material`, `facade_color` | Facade details |
| `has_parts` | `true` when Overture also models separate building parts, such as a tower or dome |
| `is_underground` | `true` for underground structures |
| `sources` | Which dataset each value came from, with its licence |
If you only need a few of these, use `includes`, so the response only carries those properties:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/buildings' \
-d 'lat=51.5138' -d 'lng=-0.0984' -d 'radius=200' -d 'includes=height,num_floors,class'
```
## Draw buildings in 3D
Ask for `format=geojson` and the response is a `FeatureCollection` you can hand straight to MapLibre, Mapbox or deck.gl. This is how the image at the top of this page was made:
```typescript
import maplibregl from "maplibre-gl";
const params = new URLSearchParams({
lat: "51.5138",
lng: "-0.0984",
radius: "450",
limit: "2000",
format: "geojson",
});
const res = await fetch(`https://api.overturemapsapi.com/buildings?${params}`, {
headers: { "x-api-key": "YOUR-API-KEY" },
});
const buildings: GeoJSON.FeatureCollection = await res.json();
map.addSource("buildings", { type: "geojson", data: buildings });
map.addLayer({
id: "buildings-3d",
type: "fill-extrusion",
source: "buildings",
paint: {
// height if known, otherwise floors x 3.2 m, otherwise 8 m
"fill-extrusion-height": [
"coalesce",
["get", "height"],
["*", ["get", "num_floors"], 3.2],
8,
],
"fill-extrusion-color": [
"interpolate", ["linear"],
["coalesce", ["get", "height"], 8],
0, "#BFDBFE", 30, "#3B82F6", 100, "#1E3A8A",
],
"fill-extrusion-opacity": 0.9,
},
});
```
Try it without writing any code in the [Buildings demo](https://demo.overturemapsapi.com/buildings).
## Tips
- **Keep the radius tight.** Building polygons are much larger than place points. A few hundred metres in a city centre already returns hundreds of buildings, so start small and page with `limit` and `page` rather than using a large radius.
- **Check `Pagination-Count` first.** Ask for `limit=1` to see how many buildings an area holds before pulling them all.
- **Expect gaps in heights and floors.** Use a fallback, as in the example above, rather than assuming every building has a height.
- **The demo key** works within 10 km of New York, London, Paris and Bondi Beach. [Sign up for a free key](https://account.overturemapsapi.com) to search anywhere.
## Parameter reference
Generated from the [OpenAPI spec](https://api.overturemapsapi.com/api-docs.json) (operation `searchBuildings`). Find buildings within `radius` metres of a `lat`/`lng`, with footprint geometry and attributes such as height and number of floors where known. A location is required. Use `format=geojson` to draw them on a map.
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | number | | Latitude coordinate. Required if country code is not provided. |
| `lng` | number | | Longitude coordinate. Required if country code is not provided. |
| `radius` | number | `1000` | Search radius in meters, defaulting to 1000 meters if not provided. |
| `limit` | number | `100` | Limit on the number of results returned, defaulting to 100 if not provided. Use `page` to paginate for more. |
| `page` | number | `0` | Page number for pagination (0-indexed), used together with `limit` as the page size. Responses include Pagination-Count (total matching results), Pagination-Page and Pagination-Limit headers. Defaults to 0. |
| `format` | `json`, `csv`, `geojson` | `json` | Response format, defaulting to JSON. Options are "json", "csv", or "geojson". |
| `includes` | list of string (comma-separated) | | Array of fields to include in the properties field of the response, provided as a comma-separated string. Any fields not in the list will be excluded from the properties object. This is used to make the response lighter so your application is faster. |
| `country` | string | | ISO 3166 country code consisting of 2 characters. Required if lat/lng are not provided. |
## Python example
```python
import requests
resp = requests.get(
"https://api.overturemapsapi.com/buildings",
headers={"x-api-key": "DEMO-API-KEY"}, # use your own key outside the demo cities
params={
"lat": 51.5138,
"lng": -0.0984,
"radius": 200,
"limit": 10,
},
timeout=30,
)
if resp.status_code == 429:
raise SystemExit(f"Rate limited, retry in {resp.headers['Retry-After']}s")
resp.raise_for_status() # 4xx bodies are {"statusCode", "message"}; the message says how to fix the request
results = resp.json()
print(resp.headers.get("Pagination-Count"), "results in total")
for building in results:
props = building["properties"]
print(building["id"], props.get("class"), props.get("height"), props.get("num_floors"))
```
Results are paginated: pass `page` (0-indexed) and `limit`, and read the total from the `Pagination-Count` header. See [Pagination](/docs/pagination) and [Usage limits](/docs/limits).
## Related
- [`/places/buildings`](./places-buildings): places, each matched to its nearest building shape
- [`/places`](./places): points of interest, with categories, brands and contact details
- [Overture's buildings documentation](https://docs.overturemaps.org/guides/buildings/): the upstream schema and sources
---
Source: https://www.overturemapsapi.com/docs/api-endpoints/divisions
# GET ./divisions
Administrative boundaries from the Overture Maps `divisions` theme — countries, regions, counties, localities and neighborhoods, each with a polygon boundary. Search them by name worldwide, filter by subtype, country or bounding box, query around a point, or fetch a single division by ID.
Every result includes `primary_name`, `names` (primary + common names by language), the ISO `country` and `region` codes, and a structured `bbox`.
## Query Parameters
Consult the [Open API documentation](/reference) for the full list of query parameters. Here are the main ones:
### Search by Name
`name` — case-insensitive substring match against the primary name and English common name. Also matches an exact division ID. Minimum 2 characters.
Example: find divisions called Westminster in Great Britain:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/divisions' \
-d 'name=westminster' -d 'country=GB' -d 'limit=10'
```
```json
[
{
"id": "531326d0-51f4-4c9e-8af5-d704aeea7830",
"bbox": { "xmin": -0.2161, "xmax": -0.1111, "ymin": 51.4838, "ymax": 51.5398 },
"properties": {
"subtype": "neighborhood",
"primary_name": "City of Westminster",
"names": { "primary": "City of Westminster", "common": { "en": "Westminster" } },
"country": "GB",
"region": "GB-ENG"
}
}
]
```
Name searches return metadata only by default — see [Geometry](#geometry) below.
### By Subtype
`subtype` — comma-separated list of division subtypes to include:
| Subtype | Approx. count | Example |
|---|---|---|
| `locality` | 550k | Paris (the city) |
| `neighborhood` | 320k | SoHo |
| `microhood` | 86k | a sub-neighborhood pocket |
| `macrohood` | 46k | a group of neighborhoods |
| `county` | 40k | San Francisco County |
| `localadmin` | 21k | a local administrative area |
| `region` | 4.7k | California, GB-ENG |
| `country` | 378 | France |
| `dependency` | 105 | Puerto Rico |
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/divisions' \
-d 'name=washington' -d 'subtype=county,locality'
```
### By Admin Level
`admin_level` — the division's position in its country's hierarchy: `0` is the country, `1` the first-level subdivision (states/regions), `2` the next (counties), and so on. Comma-separated list. Where subtype naming varies between countries, admin_level gives you a consistent way to say "give me the state-level areas":
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/divisions' \
-d 'country=US' -d 'name=new york' -d 'admin_level=1'
```
That query cleanly returns New York **State** (`admin_level: 1`) rather than the city or its neighborhoods.
Every division response also includes `admin_level`, `is_land`, `is_territorial` and `division_id` (the parent division feature this area belongs to).
### By Bounding Box
`bbox=xmin,ymin,xmax,ymax` (lng/lat order) — only return divisions whose bounding box intersects yours. Ideal for restricting a search to the visible map viewport:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/divisions' \
-d 'name=washington' -d 'subtype=county,locality' -d 'bbox=-125,45,-116,49'
```
### By Latitude / Longitude / Radius
`lat`, `lng`, `radius` — all division areas containing or near a point, ordered by distance. Works exactly as on the other endpoints, and includes each division's polygon geometry:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/divisions' \
-d 'lat=51.5074' -d 'lng=-0.1278' -d 'radius=1000'
```
At least one of `name`, `bbox`, `country` or `lat`/`lng` is required.
## Geometry
Division polygons can be very large — a country outline can run to megabytes of coordinates — so search-style requests keep responses lean:
- `name` searches default to **metadata only** (`include_geometry=false`)
- `lat`/`lng`, `country`-only and `format=geojson` requests include geometry, as they always have
- pass `include_geometry=true` or `include_geometry=false` on any request to override
## GET ./divisions/{id}
Fetch a single division area by ID, always including its full polygon geometry:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET \
'https://api.overturemapsapi.com/divisions/531326d0-51f4-4c9e-8af5-d704aeea7830'
```
Returns `404` if the ID is unknown.
:::note Geometry fallback
A small number of upstream records (currently the Russia and Australia country land areas) have missing geometry in the source data. For these, the API serves the boundary from the division's sibling area (land + territorial waters) and flags it with `"ext_geometry_source": "maritime"` in the properties — so you always receive a usable boundary. Records with their own geometry are never flagged.
:::
## Pagination
All division searches support `limit` + `page` with `Pagination-Count` / `Pagination-Page` / `Pagination-Limit` response headers — see [Pagination](../pagination).
## Recommended Pattern: Search Box over a Map
1. **Search** as the user types: `GET /divisions?name=...&bbox=&limit=10` — fast, metadata-only.
2. **Select**: the chosen result's `bbox` lets you frame the map immediately.
3. **Fetch**: `GET /divisions/{id}` for the full boundary polygon of the selected division only.
## Parameter reference
Generated from the [OpenAPI spec](https://api.overturemapsapi.com/api-docs.json) (operation `searchDivisions`). Find countries, regions, localities and neighbourhoods by `name`, `country`, `subtype` or `admin_level`, or those containing or near a `lat`/`lng`. Use it to geocode a place name to a point or boundary before searching `/places`. Name searches omit boundary geometry by default (set `include_geometry=true`, or fetch one area with `GET /divisions/{id}`), because polygons can be megabytes each.
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | number | | Latitude coordinate. Required if no country, name or bbox filter is provided. |
| `lng` | number | | Longitude coordinate. Required if no country, name or bbox filter is provided. |
| `radius` | number | `1000` | Search radius in meters, defaulting to 1000 meters if not provided. |
| `limit` | number | `100` | Limit on the number of results returned, defaulting to 100 if not provided. Use `page` to paginate for more. |
| `page` | number | `0` | Page number for pagination (0-indexed), used together with `limit` as the page size. Responses include Pagination-Count (total matching results), Pagination-Page and Pagination-Limit headers. Defaults to 0. |
| `format` | `json`, `csv`, `geojson` | `json` | Response format, defaulting to JSON. Options are "json", "csv", or "geojson". |
| `includes` | list of string (comma-separated) | | Array of fields to include in the properties field of the response, provided as a comma-separated string. Any fields not in the list will be excluded from the properties object. This is used to make the response lighter so your application is faster. |
| `country` | string | | ISO 3166 country code consisting of 2 characters. Required if lat/lng are not provided. |
| `name` | string | | Case-insensitive substring match against the division primary name and English common name. Also matches an exact division ID. |
| `subtype` | list of string (comma-separated) | | Filter by division subtype, provided as a comma-separated list (e.g. "county,locality"). |
| `admin_level` | string | | Filter by admin_level — the division's position in its country's hierarchy (0 = country). Provided as a comma-separated list (e.g. "1,2"). |
| `bbox` | string | | Bounding box filter as "xmin,ymin,xmax,ymax" (lng/lat order). Returns divisions whose bounding box intersects it. |
| `include_geometry` | boolean | | Whether to include the full polygon geometry in each result. Defaults to false for name searches (fetch geometry via GET /divisions/{id}) and true otherwise. Division polygons can be megabytes each, so leave this off for search boxes. |
## Python example
```python
import requests
resp = requests.get(
"https://api.overturemapsapi.com/divisions",
headers={"x-api-key": "DEMO-API-KEY"}, # use your own key outside the demo cities
params={
"name": "Camden",
"country": "GB",
"limit": 5,
},
timeout=30,
)
if resp.status_code == 429:
raise SystemExit(f"Rate limited, retry in {resp.headers['Retry-After']}s")
resp.raise_for_status() # 4xx bodies are {"statusCode", "message"}; the message says how to fix the request
results = resp.json()
print(resp.headers.get("Pagination-Count"), "results in total")
for area in results:
props = area["properties"]
print(area["id"], props["names"]["primary"], props.get("subtype"))
```
Results are paginated: pass `page` (0-indexed) and `limit`, and read the total from the `Pagination-Count` header. See [Pagination](/docs/pagination) and [Usage limits](/docs/limits).
---
Source: https://www.overturemapsapi.com/docs/api-endpoints/addresses
# GET ./addresses
Point addresses from the Overture Maps `addresses` theme — street number, street, unit, postcode and country for hundreds of millions of address points, sourced from open government address datasets such as OpenAddresses.
## Query Parameters
Consult the [Open API documentation](/reference) for the full list of query parameters.
### By Latitude / Longitude / Radius
`lat`, `lng`, `radius` — address points near a location, ordered by distance:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/addresses' \
-d 'lat=40.7128' -d 'lng=-74.0060' -d 'radius=200'
```
Example response:
```json
[
{
"id": "90abd8ae-ed7f-41fd-b083-374ff701b2ce",
"geometry": { "type": "Point", "coordinates": [-74.0059, 40.7127] },
"properties": {
"number": "254",
"street": "BROADWAY",
"unit": null,
"postcode": "10007",
"country": "US",
"address_levels": ["NY", "New York"],
"sources": [{ "dataset": "OpenAddresses/NY/NYC Open Data" }]
}
}
]
```
### Other Parameters
- `limit` — maximum number of results (default 25000)
- `format` — `json` (default) or `geojson`
- `includes` — comma-separated list of property fields to keep, for lighter responses
## Use Cases
- Reverse lookup: "what addresses are at/near this point?"
- Address density and delivery-area analysis
- Validating or enriching your own address data against open sources
## Pagination
Supports `limit` + `page` with `Pagination-Count` / `Pagination-Page` / `Pagination-Limit` response headers — see [Pagination](../pagination).
## Parameter reference
Generated from the [OpenAPI spec](https://api.overturemapsapi.com/api-docs.json) (operation `searchAddresses`). Find address points within `radius` metres of a `lat`/`lng`. A location is required. Use it for address lookup, validation or reverse geocoding.
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | number | | Latitude coordinate. Required if country code is not provided. |
| `lng` | number | | Longitude coordinate. Required if country code is not provided. |
| `radius` | number | `1000` | Search radius in meters, defaulting to 1000 meters if not provided. |
| `limit` | number | `100` | Limit on the number of results returned, defaulting to 100 if not provided. Use `page` to paginate for more. |
| `page` | number | `0` | Page number for pagination (0-indexed), used together with `limit` as the page size. Responses include Pagination-Count (total matching results), Pagination-Page and Pagination-Limit headers. Defaults to 0. |
| `format` | `json`, `csv`, `geojson` | `json` | Response format, defaulting to JSON. Options are "json", "csv", or "geojson". |
| `includes` | list of string (comma-separated) | | Array of fields to include in the properties field of the response, provided as a comma-separated string. Any fields not in the list will be excluded from the properties object. This is used to make the response lighter so your application is faster. |
| `country` | string | | ISO 3166 country code consisting of 2 characters. Required if lat/lng are not provided. |
## Python example
```python
import requests
resp = requests.get(
"https://api.overturemapsapi.com/addresses",
headers={"x-api-key": "DEMO-API-KEY"}, # use your own key outside the demo cities
params={
"lat": 40.7128,
"lng": -74.006,
"radius": 100,
"limit": 10,
},
timeout=30,
)
if resp.status_code == 429:
raise SystemExit(f"Rate limited, retry in {resp.headers['Retry-After']}s")
resp.raise_for_status() # 4xx bodies are {"statusCode", "message"}; the message says how to fix the request
results = resp.json()
print(resp.headers.get("Pagination-Count"), "results in total")
for address in results:
props = address["properties"]
print(props.get("number"), props.get("street"), props.get("postcode"))
```
Results are paginated: pass `page` (0-indexed) and `limit`, and read the total from the `Pagination-Count` header. See [Pagination](/docs/pagination) and [Usage limits](/docs/limits).
---
Source: https://www.overturemapsapi.com/docs/api-endpoints/base
# GET ./base
Land use and land cover polygons from the Overture Maps `base` theme — parks, forests, grass, sand, wetlands, residential/commercial land use and more. Useful for giving your maps geographic context underneath places and buildings.
The endpoint combines the Overture `land_use` and `land_cover` types into one response; each feature carries its `type`, `subtype` and `class` so you can tell them apart.
## Query Parameters
Consult the [Open API documentation](/reference) for the full list of query parameters.
### By Latitude / Longitude / Radius
`lat`, `lng`, `radius` — base features near a location, ordered by distance:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/base' \
-d 'lat=40.7128' -d 'lng=-74.0060' -d 'radius=500'
```
Example response (geometry truncated):
```json
[
{
"id": "8739d581-aa1c-3a56-915d-7e1a4d7dce61",
"geometry": { "type": "Polygon", "coordinates": ["..."] },
"properties": {
"theme": "base",
"type": "land",
"subtype": "park",
"class": "park",
"sources": [{ "dataset": "OpenStreetMap" }]
}
}
]
```
### Other Parameters
- `limit` — maximum number of results (default 25000)
- `format` — `json` (default) or `geojson` (drop straight onto a map)
- `includes` — comma-separated list of property fields to keep, for lighter responses
## Use Cases
- Rendering parks, green space and water context around your points of interest
- Land-use classification of an area (residential vs commercial vs industrial)
- Environmental and urban-planning analysis
## Pagination
Supports `limit` + `page` with `Pagination-Count` / `Pagination-Page` / `Pagination-Limit` response headers — see [Pagination](../pagination).
## Parameter reference
Generated from the [OpenAPI spec](https://api.overturemapsapi.com/api-docs.json) (operation `searchBaseFeatures`). Find base map features (land use, land cover, water and similar) within `radius` metres of a `lat`/`lng`. A location is required.
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | number | | Latitude coordinate. Required if country code is not provided. |
| `lng` | number | | Longitude coordinate. Required if country code is not provided. |
| `radius` | number | `1000` | Search radius in meters, defaulting to 1000 meters if not provided. |
| `limit` | number | `100` | Limit on the number of results returned, defaulting to 100 if not provided. Use `page` to paginate for more. |
| `page` | number | `0` | Page number for pagination (0-indexed), used together with `limit` as the page size. Responses include Pagination-Count (total matching results), Pagination-Page and Pagination-Limit headers. Defaults to 0. |
| `format` | `json`, `csv`, `geojson` | `json` | Response format, defaulting to JSON. Options are "json", "csv", or "geojson". |
| `includes` | list of string (comma-separated) | | Array of fields to include in the properties field of the response, provided as a comma-separated string. Any fields not in the list will be excluded from the properties object. This is used to make the response lighter so your application is faster. |
| `country` | string | | ISO 3166 country code consisting of 2 characters. Required if lat/lng are not provided. |
## Python example
```python
import requests
resp = requests.get(
"https://api.overturemapsapi.com/base",
headers={"x-api-key": "DEMO-API-KEY"}, # use your own key outside the demo cities
params={
"lat": 40.7128,
"lng": -74.006,
"radius": 200,
"limit": 10,
},
timeout=30,
)
if resp.status_code == 429:
raise SystemExit(f"Rate limited, retry in {resp.headers['Retry-After']}s")
resp.raise_for_status() # 4xx bodies are {"statusCode", "message"}; the message says how to fix the request
results = resp.json()
print(resp.headers.get("Pagination-Count"), "results in total")
for feature in results:
props = feature["properties"]
print(props.get("type"), props.get("subtype"), props.get("class"))
```
Results are paginated: pass `page` (0-indexed) and `limit`, and read the total from the `Pagination-Count` header. See [Pagination](/docs/pagination) and [Usage limits](/docs/limits).
---
Source: https://www.overturemapsapi.com/docs/api-endpoints/transportation
# GET ./transportation
Road, rail and path segments from the Overture Maps `transportation` theme. Each segment is a LineString with a `subtype` (e.g. `road`, `rail`, `water`), a `class` (e.g. `motorway`, `primary`, `footway`, `cycleway`), connector references for routing graphs, and access restrictions where known.
## Query Parameters
Consult the [Open API documentation](/reference) for the full list of query parameters.
### By Latitude / Longitude / Radius
`lat`, `lng`, `radius` — segments near a location, ordered by distance:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/transportation' \
-d 'lat=40.7128' -d 'lng=-74.0060' -d 'radius=500'
```
Example response (geometry truncated):
```json
[
{
"id": "5280d450-e605-44dc-afb6-6c4c0ffa960c",
"geometry": { "type": "LineString", "coordinates": ["..."] },
"properties": {
"subtype": "road",
"class": "footway",
"connectors": [
{ "connector_id": "896ec260-951e-424b-b730-eb8b5c12f4d7", "at": 0 },
{ "connector_id": "f8e95a2c-cb0c-409c-baaa-a16739203e0e", "at": 1 }
],
"access_restrictions": [{ "access_type": "allowed" }],
"sources": [{ "dataset": "OpenStreetMap", "license": "ODbL-1.0" }]
}
}
]
```
### Other Parameters
- `limit` — maximum number of results (default 25000)
- `format` — `json` (default) or `geojson` (handy for dropping straight onto a map)
- `includes` — comma-separated list of property fields to keep, for lighter responses
## Use Cases
- Drawing the street network around a location
- Walkability / access analysis (footways, cycleways, road classes)
- Seeding a routing graph for a local area using segment connectors
## Pagination
Supports `limit` + `page` with `Pagination-Count` / `Pagination-Page` / `Pagination-Limit` response headers — see [Pagination](../pagination).
## Parameter reference
Generated from the [OpenAPI spec](https://api.overturemapsapi.com/api-docs.json) (operation `searchTransportationSegments`). Find road, rail and path segments within `radius` metres of a `lat`/`lng`. A location is required.
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | number | | Latitude coordinate. Required if country code is not provided. |
| `lng` | number | | Longitude coordinate. Required if country code is not provided. |
| `radius` | number | `1000` | Search radius in meters, defaulting to 1000 meters if not provided. |
| `limit` | number | `100` | Limit on the number of results returned, defaulting to 100 if not provided. Use `page` to paginate for more. |
| `page` | number | `0` | Page number for pagination (0-indexed), used together with `limit` as the page size. Responses include Pagination-Count (total matching results), Pagination-Page and Pagination-Limit headers. Defaults to 0. |
| `format` | `json`, `csv`, `geojson` | `json` | Response format, defaulting to JSON. Options are "json", "csv", or "geojson". |
| `includes` | list of string (comma-separated) | | Array of fields to include in the properties field of the response, provided as a comma-separated string. Any fields not in the list will be excluded from the properties object. This is used to make the response lighter so your application is faster. |
| `country` | string | | ISO 3166 country code consisting of 2 characters. Required if lat/lng are not provided. |
## Python example
```python
import requests
resp = requests.get(
"https://api.overturemapsapi.com/transportation",
headers={"x-api-key": "DEMO-API-KEY"}, # use your own key outside the demo cities
params={
"lat": 40.7128,
"lng": -74.006,
"radius": 100,
"limit": 10,
},
timeout=30,
)
if resp.status_code == 429:
raise SystemExit(f"Rate limited, retry in {resp.headers['Retry-After']}s")
resp.raise_for_status() # 4xx bodies are {"statusCode", "message"}; the message says how to fix the request
results = resp.json()
print(resp.headers.get("Pagination-Count"), "results in total")
for segment in results:
props = segment["properties"]
print(props.get("subtype"), props.get("class"), segment["geometry"]["type"])
```
Results are paginated: pass `page` (0-indexed) and `limit`, and read the total from the `Pagination-Count` header. See [Pagination](/docs/pagination) and [Usage limits](/docs/limits).
---
Source: https://www.overturemapsapi.com/docs/pagination
# Pagination
All search endpoints (`/places`, `/places/buildings`, `/buildings`, `/addresses`, `/base`, `/transportation`, `/divisions`) support pagination via two query parameters:
- **`limit`** — the page size (max 25,000)
- **`page`** — the page number, 0-indexed, defaulting to `0`
Response **bodies are unchanged** — you get the same JSON array (or GeoJSON FeatureCollection) as always. Pagination metadata travels in response headers:
| Header | Meaning |
|---|---|
| `Pagination-Count` | Total results matching the query, across **all** pages |
| `Pagination-Page` | The page served (0-indexed) |
| `Pagination-Limit` | The page size used |
| `X-Total-Count` | Number of results in *this* response |
The `Pagination-*` headers are exposed via CORS, so they are readable from browser JavaScript.
## Example
Page through every division called Springfield in the US, 25 at a time:
```bash
curl -i -H "x-api-key: YOUR_API_KEY" -X GET -G 'https://api.overturemapsapi.com/divisions' \
-d 'country=US' -d 'name=springfield' -d 'limit=25' -d 'page=0'
```
```text
HTTP/2 200
Pagination-Count: 56
Pagination-Page: 0
Pagination-Limit: 25
```
`Pagination-Count: 56` with a page size of 25 means three pages (`page=0`, `page=1`, `page=2`).
## Guarantees
- **Stable, disjoint pages**: results are deterministically ordered (by distance with an ID tiebreaker for lat/lng queries, by ID otherwise), so the same query never repeats or skips a result across pages.
- **Backwards compatible**: requests without a `page` parameter behave exactly as before.
- **Counts are totals**: `Pagination-Count` is the number of results matching your filters across all pages — handy for rendering result counts and page controls without fetching everything.
:::tip
For very large exports (beyond 25,000 × pages), [contact us](./support) about bulk data delivery instead of paging through everything.
:::
---
# Usage limits
Source: https://www.overturemapsapi.com/docs/limits
> Monthly request limits, the X-Quota headers, what counts as a request, and which queries need a paid plan on the hosted Overture Maps API.
# Usage limits
These limits apply to the hosted API at `api.overturemapsapi.com`. If you self-host, you set your own.
## Monthly requests
Every plan includes a monthly allowance of requests. Past the allowance your requests keep working, with an `X-Quota-Warning` header, until you reach the plan's refusal point: 200 on Free, 2,000 on Premium, 6,000 on Growth and 20,000 on Scale. That margin is room to upgrade, not a second allowance. At the limit the API returns `429` with `"error": "monthly_quota_exceeded"` and a `Retry-After` header, until 00:00 UTC on the 1st of next month. Each plan's allowance is on the [pricing page](/pricing), and your own numbers are in the headers below.
Every response to a key with a monthly limit carries (the demo key has none):
| Header | Meaning |
|---|---|
| `X-Quota-Plan` | Your plan |
| `X-Quota-Limit` | Monthly allowance |
| `X-Quota-Hard-Limit` | The point where requests are refused |
| `X-Quota-Used` | Requests counted so far this month |
| `X-Quota-Remaining` | Requests left before the allowance |
| `X-Quota-Reset` | When the count resets (Unix seconds, UTC) |
| `X-Quota-Warning` | Present once you are past the allowance |
The headers are exposed via CORS, so browser code can read them.
Only successful requests count. Errors (`4xx`/`5xx`), including validation errors, `403`s and `429`s, do not use up your allowance.
## Per-minute rate limit
There is also a per-minute limit on each endpoint: 60 requests a minute on the Free plan and 120 on paid plans, counted per account. Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (seconds until the window resets). Going over returns `429` with `"error": "rate_limited"` and a `Retry-After` header; wait that many seconds and retry. Like other errors, these `429`s do not use up your monthly allowance.
If you need a higher per-minute rate, [get in touch](/contact).
## Country-level place queries
A `/places` query with a `country` and no `lat`/`lng` reads a whole country, so it is a [paid-plan](/pricing) feature. On the Free plan, search by `lat`, `lng` and `radius` (up to 25 km); `country` still works as an extra filter alongside them. A Free key sending a country-level query gets a `403` with this explanation, and it does not count towards your allowance.
## The demo key
`DEMO-API-KEY` is for trying the API out. It covers lat/lng searches within 10 km of a few demo cities (New York, London, Paris and Bondi Beach), returns small pages, and does not support country-level queries. On `/divisions` it supports searches by `name` without geometry, or by `lat`/`lng` near a demo city. [Sign up](https://account.overturemapsapi.com) for a free key to search anywhere.
---
Source: https://www.overturemapsapi.com/docs/enriched-data
# Enriched Responses
On the Hosted paid versions you can get access to the wealth of other data we Join onto the Overture Data using the GERS ID.
## Places
The most common thing we are asked about is how to join the Overture places to the same place if it exists in OSM. Usually this is because the client already has a lot of data pipelines that themselves join to OSM data, however they have seen that OSM data has a staleness problem, and are keen to use the more up-to-date Overture data instead.
To help with this use-case we provide a set of enriched endpoints that allow you to easily access the Overture data and join it with your existing OSM data.
### Example: OpenStreetMap
Example Response for `GET /places/` with the GERS ID ('id') and OSM ID ('osm_uid')
```JSON
{
"id": "4c69f544-5df1-4b53-a5d9-35a1774b3910",
"...",
"enrichment": {
"source": "hosted",
"fields": {
"osm_uid": "u4216537394",
"osm_name": "woolworths metro - bondi beach"
}
}
}
```
This then allows you to easily merge any additional data from OSM - e.g. https://www.openstreetmap.org/node/4216537394 - and enrich your existing dataset with the latest information.
#### Other examples of our enriched data:
We have a team continuously updating and improving our enriched data offerings. Some examples include:
- advanced tags
- translations / localisation
- opening hours & dates
- website
- footfall (country dependent)
- photos
- restaurant menus
- conflate the same place to Tripadviser and other Mapping services
- [contact us for more...](mailto:aden@thatapicompany.com)
---
# API Key Management — Rate Limiting & Domain Locking (Self-Hosted)
Source: https://www.overturemapsapi.com/docs/api-key-management
> Create and manage API keys, add rate limiting and domain locking when self-hosting the open-source Overture Maps API.
# API Key Management
If you are self-hosting the API, you will need to manage API keys for your users. This section explains how to create and manage API keys for your users, add rate-limiting, and other security features.
## Auth API
You can use the Auth API to create and manage API keys for your users. The Auth API is a separate service that allows you to create and manage API keys for your users. You can create a **Free** account on [theAuthAPI.com](https://theAuthAPI.com) and create an Access Key for your app. You can then create any number of API keys for secure access to the API, and rate-limit them for cost control. It is the best in-class for managing API keys for your users.
- ENV VAR: 'AUTH_API_ACCESS_KEY=live_access_12345abcde'
## Hardcoded
You can also use the hardcoded API key in the code. This is not recommended for production use, but it is useful for testing and development. You can set the API key in the code and use it to access the API. This is useful for testing and development, but it is not recommended for production use and is set to limit the available endpoints due to costs that could be incurred.
Default is 'DEMO-API-KEY'
---
# Changelog
Source: https://www.overturemapsapi.com/docs/changelog
> What's new in the Overture Maps API — new endpoints, fields and filters, plus the Overture data releases that flow through to your queries.
# Changelog
What's changed in the Overture Maps API, newest first. This covers **two streams**:
- **API** — new endpoints, fields, filters and behaviour in this API.
- **Data** — the monthly [Overture Maps data releases](https://docs.overturemaps.org/blog/) that flow through to your query results (new categories, coverage, schema changes). We summarise the parts that affect API callers; the full upstream notes are linked on each entry.
Overture releases roughly monthly (mid-month). New here? Start with the [introduction](./intro).
---
## 2026-11-13 · API — Better docs and spec for AI tools and SDK generators
- **OpenAPI spec cleaned up.** Every operation now has a unique, readable `operationId` (`searchPlaces`, `listPlaceCategories`, `searchBuildings` and so on) and a description of when to use it, every query parameter has a type, and the `400`, `401`, `403` and `429` responses are documented with their error body and `Retry-After` header. The API key now applies to every operation, so generated clients send it. Get the spec at [`/api-docs.json`](https://api.overturemapsapi.com/api-docs.json) or [`www.overturemapsapi.com/openapi.json`](https://www.overturemapsapi.com/openapi.json).
- **Parameter references and Python examples** on every endpoint page, generated from the spec.
- **Docs for AI tools:** [`/llms-full.txt`](https://www.overturemapsapi.com/llms-full.txt) has every docs page in one file, any docs page is available as markdown by adding `.md` to its URL, and the new [Use with AI tools](./use-with-ai-tools) page covers the MCP server, custom GPTs and coding assistants.
- **Wrong paths get directions.** A request to an endpoint that doesn't exist now returns JSON with links to the docs, the spec and `llms.txt`, instead of a bare `Cannot GET`.
## 2026-10-21 · Data — Overture `2026-10-21.0` (schema v2.0.0)
- **Places up 8.5% to 88.4M.** BrightQuery grew from 10.26M to 15.77M places, with the biggest additions in France (1.95M), the United Kingdom (1.45M) and Spain (1.23M). It now covers the UK, France, Spain, the Netherlands, Belgium, Switzerland, Austria, Sweden, Norway and Finland. Expect many new `/places` results in those countries.
- **Three new sources.** Uberall adds 424,600 places across 217 countries, Birdeye adds 11,855 (mostly US) and OpenTravel adds 2,698 Hilton properties. If you filter or group on `sources`, you will see the new labels.
- **Source switches without ID changes.** In those ten BrightQuery countries, 1,861,523 existing places now carry BrightQuery data in place of a previous Meta, Foursquare or Microsoft record. They keep their GERS IDs, so cached IDs stay valid, but any source counts you track for those providers will drop.
- **Place IDs churned by 11.92%.** Overture added 8,335,526 places and removed 1,372,826. If you cache place IDs, expect some to have gone, so re-check stored IDs against [`/places`](./api-endpoints/places).
- **`operating_status` coverage rose from 23.74% to 32.54%.** Status is now chosen by majority vote across contributing sources, and `sources` records which source supplied it. `open` grew 47.6% to 27.1M, `permanently_closed` grew 70.2% to 1.67M and `temporarily_closed` went from 29 to 3,496. Filtering on `operating_status=open` returns noticeably more places than last month.
- **Addresses (still alpha upstream):** total up 0.23% to 475.3M. Uruguay's countrywide dataset was replaced by 19 departmental datasets from Direcciones Geográficas del Uruguay, so Uruguay `sources` labels on [`/addresses`](./api-endpoints/addresses) change, and `address_levels` coverage there rose from 85.76% to 100%. A refresh of the US National Address Database added 935,021 addresses (+1.11%), and New Caledonia grew 24.48% to 51,702.
- **Base:** `infrastructure` +0.86%, `land` +0.93%, `water` +0.92%, `land_use` +0.62%, `land_cover` and `bathymetry` unchanged. The OSM cut-off is 2026-10-10. See [`/base`](./api-endpoints/base).
- **Buildings are unchanged.** Overture re-promoted the September data, so [`/places/buildings`](./api-endpoints/places-buildings) returns the same 2.53bn buildings and 4.49M building parts as last month.
- **Divisions** changed only slightly (`division` +0.27%, `division_area` +0.33%), with new Esri Community Maps name values. GERS churn is 0.45% for `division` and 0.56% for `division_area`.
- **Transportation:** road length is up 254,743 km (+0.27%) and TomTom segments are up 1.54%. GERS churn is 0.70% for connectors and 0.83% for segments.
- **No schema change.** The release stays on schema v2.0.0, and the post lists no breaking changes or deprecations. The API still serves `categories` as a field and filter derived from `taxonomy`.
- Full upstream notes: [Overture 2026-10-21 release](https://docs.overturemaps.org/blog/2026/10/21/release-notes/).
## November 2026 · API updates
- **Monthly request limits are now enforced.** Each plan has an allowance and a refusal point. Between the two, requests work with an `X-Quota-Warning` header, and at the limit you get `429` with `monthly_quota_exceeded`. Every response carries `X-Quota-*` headers, and errors do not count. See [Usage limits](./limits).
- **Per-minute limits follow your plan.** The limit is now counted per account rather than per IP: 60 requests a minute on Free and 120 on paid plans, with `X-RateLimit-*` headers and a `429` `rate_limited` error that includes `Retry-After`. See [Usage limits](./limits).
- **Country-level `/places` queries need a paid plan.** A `country` with no `lat`/`lng` now returns `403` on the Free plan. Search by `lat`, `lng` and `radius` instead, with `country` as an extra filter if you like. See [Places](./api-endpoints/places).
- **`/transportation` segments return line geometries.** See [Transportation](./api-endpoints/transportation).
- **Clearer error for country-only calls to point endpoints.** `/buildings`, `/addresses`, `/base`, `/transportation` and `/places/buildings` need a `lat` and `lng`. A country-only call now gets a `400` saying so, where it used to return a `500`.
- **Coordinates of `0` are valid.** Searches on the equator (`lat=0`) or the prime meridian (`lng=0`) were previously treated as having no location. They now filter by position as expected.
- **Demo key on `/divisions`.** `DEMO-API-KEY` works for name searches without geometry, or `lat`/`lng` near a demo city. See [Divisions](./api-endpoints/divisions).
## 2026-10 · API: partial-name search
- **New `name_prefix` filter on [`/places`](./api-endpoints/places).** Matches places whose name, or a word in it, starts with the text you pass, so it can back a type-ahead location field. Combine it with `lat`/`lng` and a small `limit` for the nearest matches first.
## 2026-09-23 · Data — Overture `2026-09-23.0` (schema v2.0.0)
- **`categories` is gone upstream.** Overture's schema v2.0.0 removes the places `categories` property entirely, in favour of `taxonomy` and `basic_category`. This API keeps the `categories` response field and filter working, now derived from `taxonomy`, and [`/places/categories`](./api-endpoints/places-categories) now lists taxonomy values. The values themselves now follow the taxonomy vocabulary, so any legacy category string that differs from its taxonomy equivalent will stop matching. If you filter on specific strings, use the [`taxonomy` filter](./api-endpoints/places).
- **Places up 10.6% to 81.5M.** BrightQuery expanded beyond the US into Germany, Italy and Denmark (2.29M to 10.26M places) and added more US places under `services_and_business`. Expect many new results in those countries.
- **`operating_status=open` returns far more.** Places marked open grew 66.2%, from 11.05M to 18.36M.
- **Addresses: source labels have changed.** `sources[].dataset` values on [`/addresses`](./api-endpoints/addresses) now use a `provider/resource` form, so update any code that filters or groups on those strings. `record_id`, `update_time` and `license` are now populated on every address source. Coverage grew 0.29% to 474.2M, with new data for New Caledonia, French Polynesia, three Taiwanese counties and New Orleans, plus roughly 1M new addresses in Italy with better location precision.
- **Buildings: new `shelter` class** on around 321,000 buildings, many of them previously unclassed `civic` buildings. If you filter on `class` via [`/places/buildings`](./api-endpoints/places-buildings), re-check those. Buildings +0.17% to 2.53bn, building parts +3.39%.
- **Base: new IDs for every `land_use` feature.** Overture changed how base-theme IDs are generated, so `land_use` IDs from [`/base`](./api-endpoints/base) have all changed; don't rely on IDs cached before this release. `land_use` grew 0.86%, `land_cover` is unchanged.
- **Divisions** saw only minor, incremental changes (+0.63%). **Transportation** road length is up 263,339 km (+0.28%) and TomTom segments up 5.5%.
- Full upstream notes: [Overture 2026-09-23 release](https://docs.overturemaps.org/blog/2026/09/23/release-notes/).
## 2026-08-19 · Data — Overture `2026-08-19.0` (schema v1.18.0)
- **Last release before `categories` disappears.** Overture removes the `categories` property in the **September 2026** release, replaced by `basic_category` and `taxonomy`. If you filter or match on specific category strings, move to the [`taxonomy` filter](./api-endpoints/places) this month. Overture also shipped further taxonomy and category clean-up in this release, so some places will have shifted category.
- **`operating_status` is much better populated.** 1.64M more places now carry an "open" status, so `operating_status=open` returns a good deal more than it did in July. Places with no signal stay null and are still excluded when you use the filter.
- **US place coverage shifted source mix.** BrightQuery nearly tripled its US contribution (+1.46M places, +175%) and AllThePlaces grew 13.4% (+190,735). Total places fell 0.8% to 73.63M as de-duplication and clean-up removed more than the new sources added, so expect some IDs you cached in July to have gone.
- **Buildings:** `building` is down 0.61% to 2.53bn while `building_part` is up 1.45%. Microsoft ML Buildings (+13.09M) and OpenStreetMap (+3.81M) both extended coverage, and hundreds of thousands of buildings picked up updated `num_floors`, `height` and `roof_shape`. Worth a re-pull if you use those on [`/places/buildings`](./api-endpoints/places-buildings).
- **Base:** `infrastructure` +1.80%, `land` +1.62%, `land_use` +1.25%, `water` +0.82%. Transit features grew across `parking`, `parking_space`, `bus_stop`, `stop_position` and `bicycle_parking`, and mountain landforms grew in every class.
- **Divisions:** geometry refinements were the main change this month, and some Vietnamese `microhood` divisions were reclassified as `neighborhood`. If you match on subtype, re-check those.
- **Transportation:** road length is up 414,396 km (+0.45%) and TomTom segments up 5%, with better `road_surface`, `class` and `access_restrictions` coverage.
- **Addresses:** a small net gain (+0.02%) with refreshed inputs from Fresno County, CA and Statistics Canada, plus new points from LINZ (New Zealand) and ICAR.
- Schema stays at v1.18.0, so no breaking schema changes this month.
- Full upstream notes: [Overture 2026-08-19 release](https://docs.overturemaps.org/blog/2026/08/19/release-notes/).
## August 2026 · API updates
- **`name` filter on `/places` and `/places/buildings`.** Case-insensitive exact match against the place's primary name and any localised common name. It narrows an existing lat/lng/radius or country search rather than acting as a standalone text search, and it counts as a narrowing filter for country-level queries. See [Places](./api-endpoints/places).
- **`/places/buildings` radius is now capped at 2,000 m.** Building lookups cost more per metre of radius, so wider searches are rejected rather than run slowly. Page through results with `limit` and `page` instead. See [Pagination](./pagination).
## 2026-07-22 · Data — Overture `2026-07-22.0` (schema v1.18.0)
- **Three new place categories** are now queryable: `arts_and_entertainment`, `lodging` and `cultural_and_historic` (previously filtered out). Use them via `categories` or `taxonomy`.
- **Better place matching** upstream — more matched places and fewer duplicates across all countries.
- **Heads-up — `categories` is being retired.** Overture deprecated the `categories` property and will remove it in the **September 2026** release, replaced by `basic_category` + `taxonomy`. This API keeps the `categories` field and filter working (derived from the taxonomy), but if you match on specific category strings, move to the [`taxonomy` filter](./api-endpoints/places) before September.
- Full upstream notes: [Overture 2026-07-22 release](https://docs.overturemaps.org/blog/2026/07/22/release-notes/).
## July 2026 · API updates
- **Pagination on every search endpoint** — `page` (0-indexed) with `limit` as the page size, and `Pagination-Count` / `Pagination-Page` / `Pagination-Limit` response headers. Ordering is deterministic so pages never overlap. See [Pagination](./pagination).
- **`has_contact` filter** — return only places that already have a website, phone, email or social (comma-separated, OR-matched). Ideal for lead lists and verification.
- **Contact details by default** — `websites`, `phones`, `emails` and `socials` are now always in the response when Overture has them; no parameter needed.
- **Brand enrichment** — add `enrichment_fields=brand` to get `ext_brand` (logo, official website, industry, parent organisation) from Wikidata, on ~1.5M places across ~3,000 chains.
- **New fields** — `taxonomy` (hierarchy-aware), `basic_category`, `operating_status` and `admin_level`, ahead of the upstream category migration.
- **Faster, cheaper place queries** — the `/places` backend now prunes spatially, so lat/lng radius queries return quicker.
- **Demo key scoped to nearby search** — `DEMO-API-KEY` now supports lat/lng radius queries only; country-level pulls need a [free API key](https://www.overturemapsapi.com).
- **New: Overture Maps MCP server** — connect the API to Claude and other LLM tools.
- **New guides** — [Build a local business directory](./guides/build-a-local-business-directory) and [Map every location of a retail chain](./guides/map-every-location-of-a-retail-chain).
---
# Deploy the Overture Maps API to Google Cloud (GCP + BigQuery)
Source: https://www.overturemapsapi.com/docs/deploy-to-gcp
> Self-host the open-source Overture Maps API on your own GCP account using BigQuery, with a step-by-step deployment guide.
# Deploy to GCP (Google Cloud Platform)
Google hosts the Overture Maps dataset in it's public BigQuery dataset. This allows us to deploy the API to Google Cloud Platform (GCP) and take advantage of the free tier for hosting and querying the data. The $400 free credit is more than enough to get started and test the API. We can also use the free tier for Cloud Storage to cache the data for faster response times.
## Architecture
We can use the Cloud Run service to deploy the API. This allows us to scale the API based on demand and only pay for the resources we use. We can also use the Cloud Build service to automate the deployment process.
Results of Queries can be cached in Google Cloud Storage (GCS) to reduce the cost of querying the BigQuery dataset. The cache key (name of file) is an encoded version of the query string. The cache is set to expire by the bucket settings, and can be manually cleared if needed.
Firestore could be used in future to cache metadata about individual entities as repeatably requesting these from BigQuery would be expensive e.g. Building shapes for Places by place.id.
In production you should consider migrating the parts of the dataset you need to a private BigQuery dataset or a different database for speed and cost, especially for building shapes.

## Setup Steps
- Create GCP Account with free credit
- Authenticate with GCP
- Create GCS bucket for cache (and set up lifecycle rules to delete old files after X days)
- Fork github repo
- Update the API key / Setup TheAuthAPI.com Free account
- Setup a Service Account with the right permissions
- Deploy to CloudRun by connecting to your github repo, and apply env vars, and have it use the service-account
- Test the new API endpoint
## API Key management
You can either use the hardcoded API key in the code, or use the Auth API by going to theAuthAPI.com and creating an account. You can then create an Access Key for the App and add it as an Env var, and then create any number of API Keys for secure access to the API, and rate-limit them for cost control.
## Datasets
### BigQuery
- [Place](https://console.cloud.google.com/bigquery?project=bigquery-public-data&p=bigquery-public-data&d=overture_maps&t=place&page=table)
Example Query
```SQL
SELECT *
FROM `bigquery-public-data.overture_maps.place`
WHERE ST_DWithin(geometry, ST_GeogPoint(16.3738, 48.2082), 500)
```
### Recommended Strategy to reduce costs
As there is one large table for each theme of data, a simple strategy to reduce costs is to create a new dataset in BigQuery, and copy the tables you need filtering down to just the country you are interested in. Another option would be the shard the tables by country, and then only query the tables you need.
Using a metadata store such as Firebase in Datastore mode - this would allow storing of building geometries and other data matched to place that is too expensive to query in BigQuery.
### Service Account roles
For a service account in GCP that a Cloud Run instance will use to access BigQuery and Google Cloud Storage (GCS), you’ll need to grant it specific roles to ensure it has permissions to create and run BigQuery jobs, as well as read and write files in a GCS bucket. Here are the recommended roles:
BigQuery Permissions:
- `BigQuery User` (roles/bigquery.user): Grants permissions to create and run jobs in BigQuery.
- `BigQuery Data Viewer` (roles/bigquery.dataViewer): Allows the service account to view datasets and tables, if it needs access to view data.
- (Optional) `BigQuery Job User` (roles/bigquery.jobUser): This role can also be helpful if your queries require advanced job control features, though usually, bigquery.user suffices.
Google Cloud Storage (GCS) Permissions:
- `Storage Object Viewer` (roles/storage.objectViewer): Grants read access to objects in the bucket.
- `Storage Object Creator` (roles/storage.objectCreator): Grants permission to write files to the bucket, including creating new objects.
## Billing Alerts
To set a maximum amount of BigQuery billed usage for a service account in Google Cloud Platform (GCP), you can use **BigQuery custom quotas** with **Budget Alerts** and **Cost Management** policies. While GCP does not directly offer a "hard limit" on the gigabytes processed by a specific service account in BigQuery, you can combine these methods to closely monitor and restrict usage:
### 1. Set a Budget with Alerts
Setting a budget for BigQuery can help you track and get alerts when usage approaches your desired monthly limit. Here’s how to set this up:
1. **Go to the GCP Console** and navigate to **Billing** > **Budgets & alerts**.
2. Click **Create Budget** and select the billing account.
3. Define your budget, naming it appropriately (e.g., "BigQuery Service Account Budget").
4. Set the **amount** for your budget to align with the estimated monthly cost for your maximum GB usage.
5. Configure **alerts** at specific percentages of the budget (e.g., 50%, 80%, 100%) to receive notifications when the limit is nearing.
6. Assign specific recipients or roles to get notified via email.
> **Note**: Although this will alert you to high usage, it won’t automatically stop queries when usage exceeds this amount.
### 2. Use Cost Controls with Organization Policies
To enforce restrictions on usage, you can use **GCP organization policies** to prevent BigQuery from running queries beyond a certain threshold:
1. Go to **IAM & Admin** > **Organization Policies**.
2. Use policies like `constraints/bigquery.restrictQueries` to restrict which projects, datasets, or tables can be accessed by a service account.
3. By applying restrictions on certain datasets or tables, you can indirectly limit how much data the service account can process, thus affecting the billed usage.
### 3. Monitoring Usage with Cloud Monitoring and Alerts
For more granular control and monitoring, set up **Cloud Monitoring** to track BigQuery usage specifically by the service account:
1. In **Cloud Monitoring**, go to **Metrics Explorer**.
2. Use the `BigQuery > Bytes Billed` metric, and filter by **service account**.
3. Set up **alert policies** based on this metric to notify you when the usage approaches your set threshold.
By combining these approaches, you can closely monitor and manage the BigQuery usage of a service account to stay within the specified limits.
---
Source: https://www.overturemapsapi.com/docs/guides/build-a-local-business-directory
# Build a local business directory
A common use of the Overture Maps API is to build a **local business discovery or directory product**: find the businesses in a place, sort them by category, and enrich them with contact details so users (or your own pipeline) can reach out or verify them.
This guide walks through that end to end. The running example builds a directory of **bakeries in France**, but every step works for any country and category — swap `country` and `categories` for your own.
## What you'll build
For a chosen country and category, you'll pull every matching business, keep only the ones you can actually contact, and get back their websites, phones, socials and location — ready to load into your own database.
## Step 1 — Find the businesses
Start with a country code (ISO 3166-1 alpha-2) and a category. Country-level queries need a [paid plan](/pricing); the Free plan and the demo key search by `lat`, `lng` and `radius` (up to 25 km), so on Free, build the directory city by city. Ask for a single result first to see the shape of the data:
```bash
curl -H "x-api-key: YOUR-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'country=FR' -d 'categories=bakery' -d 'limit=1'
```
- **`categories`** matches the exact category. If you want a whole family of businesses (say every kind of food-and-drink venue), use **`taxonomy`** instead — it's hierarchy-aware, so `taxonomy=food_and_drink` matches bakeries, cafés, restaurants and bars in one call.
- **`min_confidence`** (default `0.5`) trades precision for recall. Lower it toward `0.4` to catch more of the long tail, raise it toward `0.8` if you only want high-confidence records. It's worth tuning against a sample for your category.
## Step 2 — You already get contact details
Contact details come back **by default** — you do not need any special parameter:
```json
"names": { "primary": "Boulangerie du Coin" },
"categories": { "primary": "bakery" },
"websites": ["https://boulangerie-du-coin.fr"],
"phones": ["+33123456789"],
"emails": [],
"socials": ["https://www.facebook.com/boulangerieducoin"],
"addresses": [{ "freeform": "12 Rue de la Paix", "locality": "Paris", "postcode": "75002", "country": "FR" }]
```
:::tip Don't use `enrichment_fields` for websites or phones
A common early mistake is adding `website` or `phone` to `enrichment_fields`. That parameter is **only** for the Wikidata brand layer (Step 5). The `websites`, `phones`, `emails` and `socials` arrays are always in the response when Overture has them.
:::
Coverage varies by country and category. In well-mapped markets, websites and phones are populated for the large majority of businesses; emails and socials are thinner. In smaller territories the total number of places is lower, so expect a shorter long tail.
## Step 3 — Keep only businesses you can verify
If your product verifies or enriches businesses via their own website (or a social page), filter to the records that actually have something to work with, using **`has_contact`**. It's comma-separated and matched with **OR**:
```bash
# bakeries with a website OR a social link
curl -H "x-api-key: YOUR-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'country=FR' -d 'categories=bakery' -d 'has_contact=website,social'
```
`website,social` is a good default: it catches businesses that use a Facebook or Instagram page instead of a dedicated site, which in many markets is most of the ones without a website. Allowed values are `website`, `phone`, `email`, `social`.
The `Pagination-Count` response header reflects the **filtered** total, so you can see how many businesses match before you pull them all.
## Step 4 — Pull the whole country
Results are paginated at up to 100 per page (`limit`), with `page` 0-indexed. The `Pagination-Count` header tells you the total, so you loop until you've collected them all:
```javascript
const base = "https://api.overturemapsapi.com/places";
const params = new URLSearchParams({
country: "FR",
categories: "bakery",
has_contact: "website,social",
min_confidence: "0.50",
limit: "100",
format: "geojson",
});
let page = 0;
const all = [];
while (true) {
const res = await fetch(`${base}?${params}&page=${page}`, {
headers: { "x-api-key": process.env.OVERTURE_API_KEY },
});
const batch = await res.json();
all.push(...batch.features);
const total = Number(res.headers.get("Pagination-Count")); // filtered total
if (all.length >= total) break;
page++;
}
console.log(`Collected ${all.length} bakeries with a website or social`);
```
For a **local** directory (a town or neighbourhood) rather than a whole country, swap `country` for `lat` / `lng` / `radius` and the same pagination applies.
## Step 5 — Add brand details for chains (optional)
For chain or franchise locations, add `enrichment_fields=brand` to get the brand's logo, official website, industry and parent company, sourced from Wikidata:
```json
"ext_brand": {
"label": "Paul",
"logo_url": "http://commons.wikimedia.org/wiki/Special:FilePath/Paul%20logo.svg",
"website": "https://www.paul.fr/",
"industry": "bakery"
}
```
This is handy for de-duplicating or badging chain locations, and for filling in an official website where the individual location doesn't have its own.
## Tips
- **Confidence**: sample your category at a couple of `min_confidence` levels before committing — the right threshold differs by category and country.
- **Category vs taxonomy**: use `categories` for a specific type, `taxonomy` when you want a whole branch of the hierarchy in one query.
- **Coverage**: it varies by market. If you need denser contact data in a specific country, [get in touch](/docs/support) — we're continually improving coverage.
- **Pagination**: always drive your loop off the `Pagination-Count` header rather than assuming a fixed number of pages. See [Pagination](/docs/pagination) for details.
That's the whole loop: choose a country and category, filter to the businesses you can reach, page through the country, and load the results into your own product.
---
# Build a "what's near me" feature
Source: https://www.overturemapsapi.com/docs/guides/build-a-whats-near-me-feature
> Build a nearby places feed for a mobile or web app with the OpenSource Overture Maps API. Radius search, category chips, walking distances, and hiding closed or unreachable places. Worked example around Bondi Beach.
# Build a "what's near me" feature
Almost every location app ships some version of "what's near me": open the app, see the cafés, bars and shops around you, nearest first. With the Overture Maps API that's one request per screen. No GeoParquet downloads, no spatial database, no tile pipeline.
This guide builds that feed around **Bondi Beach in Sydney**. Sydney is one of the demo key's cities, so every request below works as written with `DEMO-API-KEY`. Swap in your own coordinates and [your own key](https://www.overturemapsapi.com) when you're ready to go anywhere else.

The finished feed, built from live `/places` responses within 800 m of Bondi Beach. Map: OpenFreeMap, © OpenMapTiles, data © OpenStreetMap contributors.
## What you'll build
A list of places around the user that:
- comes back nearest first, with a walking distance on each row
- switches between broad and specific categories with a row of chips ("Food & drink", "Coffee", "Bars", "Bakeries")
- hides places known to be closed
- only shows places someone can actually contact or look up
## Step 1: Make one nearby request
Pass the user's position as `lat` and `lng`, a `radius` in metres, and a category filter. `taxonomy=food_and_drink` is the broad one: it matches every restaurant, café, bar and bakery in one go.
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=-33.8910' -d 'lng=151.2769' -d 'radius=800' -d 'taxonomy=food_and_drink' -d 'limit=25'
```
Each result carries the place's name, coordinates, category, address and contact details:
```json
{
"geometry": { "type": "Point", "coordinates": [151.2698344, -33.8891709] },
"properties": {
"names": { "primary": "Grill'd Burgers - Bondi Beach" },
"basic_category": "restaurant",
"taxonomy": {
"primary": "burger_restaurant",
"hierarchy": ["food_and_drink", "restaurant", "meat_restaurant", "burger_restaurant"]
},
"operating_status": "open",
"confidence": 0.975,
"websites": ["https://grilld.com.au/restaurants/new-south-wales/bondi-beach"],
"phones": ["+61 2 9179 8703"],
"addresses": [{ "freeform": "89-93 Hall Street", "locality": "Bondi Beach", "postcode": "2026", "country": "AU" }]
}
}
```
Results come back **nearest first**, so the top of the list is the closest place. `radius` defaults to 1,000 m and goes up to 25,000 m on your own key. The demo key caps the radius at 2,000 m and returns smaller pages.
## Step 2: Get the user's location
In the browser, `navigator.geolocation` gives you the coordinates. Round them to three decimal places (roughly 100 m) before you query:
```typescript
function getPosition(): Promise<{ lat: number; lng: number }> {
return new Promise((resolve, reject) =>
navigator.geolocation.getCurrentPosition(
({ coords }) =>
resolve({
lat: Math.round(coords.latitude * 1000) / 1000,
lng: Math.round(coords.longitude * 1000) / 1000,
}),
reject,
{ enableHighAccuracy: false, timeout: 10_000 },
),
);
}
```
Rounding does two jobs. Responses are cached by their exact parameters, so a user wandering down the same street keeps hitting the cache instead of making a fresh query every few metres. And you never send anyone's precise position to a third party when 100 m is plenty for "what's near me".
## Step 3: Fetch, then add distances
The API sorts by distance but doesn't return the distance itself, so work it out on the client with the haversine formula. It's a few lines and needs no library:
```typescript
type Place = {
name: string;
category: string;
lat: number;
lng: number;
distanceM: number;
phone?: string;
website?: string;
status: string | null;
};
const API = "https://api.overturemapsapi.com/places";
function distanceM(aLat: number, aLng: number, bLat: number, bLng: number): number {
const R = 6_371_000;
const toRad = (d: number) => (d * Math.PI) / 180;
const dLat = toRad(bLat - aLat);
const dLng = toRad(bLng - aLng);
const h =
Math.sin(dLat / 2) ** 2 +
Math.cos(toRad(aLat)) * Math.cos(toRad(bLat)) * Math.sin(dLng / 2) ** 2;
return Math.round(2 * R * Math.asin(Math.sqrt(h)));
}
export async function nearby(
lat: number,
lng: number,
filter: Record,
radius = 800,
): Promise {
const params = new URLSearchParams({
lat: String(lat),
lng: String(lng),
radius: String(radius),
limit: "25",
has_contact: "phone,website", // Step 6
...filter,
});
const res = await fetch(`${API}?${params}`, {
headers: { "x-api-key": "YOUR-API-KEY" },
});
if (!res.ok) throw new Error(`Places request failed: ${res.status}`);
const features: any[] = await res.json();
return features
.map(({ geometry, properties: p }) => {
const [pLng, pLat] = geometry.coordinates;
return {
name: p.names.primary,
category: (p.basic_category ?? "").replaceAll("_", " "),
lat: pLat,
lng: pLng,
distanceM: distanceM(lat, lng, pLat, pLng),
phone: p.phones?.[0],
website: p.websites?.[0],
status: p.operating_status,
};
})
.filter((place) => !CLOSED.has(place.status ?? "")) // Step 5
.sort((a, b) => a.distanceM - b.distanceM);
}
const CLOSED = new Set(["permanently_closed", "temporarily_closed"]);
```
Around Bondi, the first rows are Etheus at 64 m, Sejuiced at 89 m and Surfish at 92 m.
:::tip Keep your key off the client
A key shipped in a mobile app or a public web page can be copied. Either call the API from your own backend, or lock the key to your domains. See [Securing API keys with domain locking](/blog/2026/06/29/securing-api-keys-with-domain-locking).
:::
## Step 4: Add category chips
Each chip is just a different filter. `taxonomy` is hierarchy-aware: it matches a place's primary category **or any ancestor**, so one broad value covers everything beneath it. `categories` matches specific values, and takes a comma-separated list.
| Chip | Filter | Around Bondi (800 m) |
| --- | --- | --- |
| Food & drink | `taxonomy=food_and_drink` | a full page of 25 |
| Coffee | `categories=coffee_shop,cafe` | a full page of 25 |
| Bars | `taxonomy=bar` | 17 |
| Bakeries | `taxonomy=bakery` | 5 |
```typescript
const CHIPS: Record> = {
"Food & drink": { taxonomy: "food_and_drink" },
Coffee: { categories: "coffee_shop,cafe" },
Bars: { taxonomy: "bar" },
Bakeries: { taxonomy: "bakery" },
};
const places = await nearby(lat, lng, CHIPS["Coffee"]);
```
`categories=coffee_shop,cafe` is the useful pairing for "Coffee": Overture files some coffee places as `cafe` and some as `coffee_shop`, and people looking for a flat white don't care which. Browse the full vocabulary with [`/places/categories`](../api-endpoints/places-categories), which accepts the same `lat`/`lng`/`radius` so you can build chips from what actually exists near the user.
## Step 5: Hide closed places
There's an `operating_status` filter, but for this feature filter on the client instead. Most places carry no status signal at all (`operating_status: null`), and `operating_status=open` drops every one of them. Within 800 m of Bondi, `taxonomy=food_and_drink&operating_status=open` returns a single place, while the same search without it fills the page.
So treat "unknown" as open and remove only the places known to be closed. That's the `CLOSED` set in Step 3.
## Step 6: Only show places people can reach
A list entry is only useful if the user can ring it, book it or look it up. `has_contact=phone,website` keeps places with **either** a phone number or a website (the values are OR'd), which removes most of the long tail of unnamed or stale entries. It's already in the `nearby()` call above. Drop it if you'd rather show everything.
For tighter results, add `min_confidence` (0 to 1). It trades recall for precision on low-confidence records.
## Step 7: When nothing comes back
In quiet areas a small radius can return an empty list. Widen it in steps rather than starting large, so city users still get the nearest places first:
```typescript
export async function nearbyWithFallback(lat: number, lng: number, filter: Record) {
for (const radius of [500, 1_500, 5_000]) {
const places = await nearby(lat, lng, filter, radius);
if (places.length > 0) return { radius, places };
}
return { radius: 5_000, places: [] };
}
```
Show the radius you ended up using ("within 1.5 km"), so the distances make sense.
## Going further
- **Search as you type.** Add `name_prefix` for a suggestions box. With `lat`/`lng` it also returns nearest first. See [By Partial Name](../api-endpoints/places#by-partial-name-type-ahead).
- **Brand logos.** Add `enrichment_fields=brand` to get logos and official websites for chains. See [Build a local business directory](./build-a-local-business-directory).
- **Put it on a map.** Add `format=geojson` and hand the response straight to MapLibre, Leaflet or Mapbox. [Map every location of a retail chain](./map-every-location-of-a-retail-chain) shows the Leaflet version.
- **More results.** Page through with `limit` and `page`. See [Pagination](/docs/pagination).
That's the whole feature: one radius request per chip, distances on the client, closed places filtered out, and a widening radius for quiet areas.
---
# Export Overture places to GeoJSON and CSV
Source: https://www.overturemapsapi.com/docs/guides/export-overture-places-to-geojson-and-csv
> Save Overture Maps places as a GeoJSON file for QGIS, kepler.gl or geojson.io, and as a CSV for spreadsheets. No GeoParquet downloads, no DuckDB. Worked example with bakeries in Le Marais, Paris.
# Export Overture places to GeoJSON and CSV
The usual route to an Overture extract is to install DuckDB (or a plugin that needs it), query the GeoParquet files on S3 or Azure, and write the result out. That works, but it is a lot of setup when all you want is "every bakery in this neighbourhood, as a file I can open". With the Overture Maps API it's a few HTTP requests and a short script.
This guide exports every bakery within 800 m of **Place des Vosges in Paris**. Paris is one of the demo key's cities, so everything below runs as written with `DEMO-API-KEY`. Use [your own key](https://account.overturemapsapi.com) to export anywhere else.

The finished export: 58 places from three `/places` pages, saved as GeoJSON and CSV. Map: OpenFreeMap, © OpenMapTiles, data © OpenStreetMap contributors.
## What you'll build
A small TypeScript script that:
- asks `/places` for GeoJSON instead of plain JSON
- pages through every result, however many pages there are
- trims each place down to the fields you need
- writes `marais-bakeries.geojson` for GIS tools and `marais-bakeries.csv` for spreadsheets
## Step 1: Ask for GeoJSON
Add `format=geojson` to any `/places` search and the response is a GeoJSON `FeatureCollection` instead of a bare array:
```bash
curl -i -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=48.8556' -d 'lng=2.3655' -d 'radius=800' -d 'categories=bakery' \
-d 'format=geojson' -d 'limit=25'
```
```
Pagination-Count: 58
Pagination-Page: 0
Pagination-Limit: 25
```
```json
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "b9458638-3e3d-4dba-961d-b9729f87af59",
"geometry": { "type": "Point", "coordinates": [2.36470019, 48.85616796] },
"properties": {
"names": { "primary": "Patisserie Boulangerie Mr Jacques" },
"taxonomy": { "primary": "bakery", "hierarchy": ["food_and_drink", "casual_eatery", "bakery"] },
"websites": ["http://patisseriemrjacques.com/"],
"phones": ["+33183920019"],
"addresses": [{ "freeform": "19 Pl. des Vosges", "locality": "Paris", "postcode": "75004", "country": "FR" }]
}
}
]
}
```
(Properties shortened here.) `Pagination-Count` tells you the total before you fetch anything else: 58 bakeries in this search. The same `format=geojson` works on [`/buildings`](../api-endpoints/buildings) and [`/transportation`](../api-endpoints/transportation), so the rest of this guide applies to footprints and street segments too.
## Step 2: Page through everything
One request returns one page. With 58 results and the demo key's page size of 25 (the demo key returns at most 25 per page, whatever `limit` you send), that's three pages: 25, 25 and 8. Keep requesting `page=0`, `page=1` and so on until you have `Pagination-Count` features. See [Pagination](/docs/pagination) for the details.
```typescript
type Feature = {
type: "Feature";
id: string;
geometry: { type: "Point"; coordinates: [number, number] };
properties: Record;
};
type FeatureCollection = { type: "FeatureCollection"; features: Feature[] };
const API = "https://api.overturemapsapi.com/places";
const KEY = process.env.OVERTURE_API_KEY ?? "DEMO-API-KEY";
async function exportPlaces(filters: Record): Promise {
const features: Feature[] = [];
let total = Infinity;
for (let page = 0; features.length < total; page++) {
const params = new URLSearchParams({ ...filters, format: "geojson", page: String(page) });
const res = await fetch(`${API}?${params}`, { headers: { "x-api-key": KEY } });
if (!res.ok) throw new Error(`Page ${page} failed: ${res.status} ${await res.text()}`);
total = Number(res.headers.get("Pagination-Count") ?? 0);
const batch = (await res.json()) as FeatureCollection;
if (batch.features.length === 0) break; // nothing left
features.push(...batch.features);
}
return { type: "FeatureCollection", features };
}
```
The result is one `FeatureCollection` holding every page, ready to save. Every page is a normal request and counts towards your [monthly allowance](/docs/limits), so check `Pagination-Count` with a single small request before you export something large.
## Step 3: Keep only the fields you need
A full place record carries sources, version numbers, brand objects and more. For an export you usually want a handful of fields. `includes` returns only the properties you list:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=48.8556' -d 'lng=2.3655' -d 'radius=800' -d 'categories=bakery' \
-d 'format=geojson' -d 'limit=25' -d 'includes=names,taxonomy,addresses,websites,phones'
```
For this first page of 25 bakeries, the response drops from 31,022 bytes to 12,895. Each feature keeps its `id` and `geometry`. Properties with no value are left out, so don't assume every key is present.
## Step 4: Write GeoJSON and CSV files
GeoJSON can be saved as it comes. For a CSV, flatten each feature into one row: name, category, coordinates, the first address, website and phone. The API returns JSON and GeoJSON, so do this conversion on your side. (`format=csv` appears in the OpenAPI reference, but in our tests it currently returns JSON.)
```typescript
import { writeFile } from "node:fs/promises";
const COLUMNS = ["id", "name", "category", "lat", "lng", "address", "postcode", "website", "phone"] as const;
function csvCell(value: unknown): string {
const s = value == null ? "" : String(value);
return /[",\n]/.test(s) ? `"${s.replaceAll('"', '""')}"` : s;
}
function toCsv(fc: FeatureCollection): string {
const rows = fc.features.map(({ id, geometry, properties: p }) => {
const [lng, lat] = geometry.coordinates;
const address = p.addresses?.[0];
return [
id,
p.names?.primary,
p.taxonomy?.primary,
lat,
lng,
address?.freeform,
address?.postcode,
p.websites?.[0],
p.phones?.[0],
].map(csvCell).join(",");
});
return [COLUMNS.join(","), ...rows].join("\n") + "\n";
}
const places = await exportPlaces({
lat: "48.8556",
lng: "2.3655",
radius: "800",
categories: "bakery",
limit: "25",
includes: "names,taxonomy,addresses,websites,phones",
});
await writeFile("marais-bakeries.geojson", JSON.stringify(places));
await writeFile("marais-bakeries.csv", toCsv(places));
console.log(`Saved ${places.features.length} places`);
```
Put Steps 2 and 4 in one file called `export-places.mts` (the `.mts` extension lets Node treat it as a module, so top-level `await` works) and run it with Node 20 or later:
```bash
npx tsx export-places.mts
# Saved 58 places
```
GeoJSON coordinates are `[lng, lat]`, which is why the code swaps them into separate `lat` and `lng` columns. The first rows of the CSV:
```
id,name,category,lat,lng,address,postcode,website,phone
b9458638-3e3d-4dba-961d-b9729f87af59,Patisserie Boulangerie Mr Jacques,bakery,48.85616796,2.36470019,19 Pl. des Vosges,75004,http://patisseriemrjacques.com/,+33183920019
fc8df7d3-3e70-41f9-abb5-a3ccb7b2c127,Boulangerie de la Place des Vosges Paris 03,bakery,48.85603,2.36711,6 Rue du Pas-de-la-Mule,75003,,+33157408385
4e1b71a1-e07a-411e-97ae-7ca607f7f9b4,Lolilol Chimney Cake,bakery,48.85580915,2.36374267,19 Rue de Turenne,75004,http://lolilolchimneycake.com/,+33782813550
```
Of the 58 bakeries, 47 have a website and 55 have a phone number. The `id` column is each place's [GERS](https://docs.overturemaps.org/gers/) ID, so you can join the export back to Overture data later.
## Step 5: Open the files
- **QGIS**: drag `marais-bakeries.geojson` onto the map canvas, or use Layer → Add Layer → Add Vector Layer. No plugin and no DuckDB needed.
- **geojson.io or kepler.gl**: drop the GeoJSON file onto the page to see the points on a map.
- **Excel, Numbers or Google Sheets**: open or import `marais-bakeries.csv`. Postcodes and phone numbers are plain text in the file, but some spreadsheet apps reformat them on import, so set those columns to text if leading zeros matter.
## Going further
- **Export a different slice.** Swap `categories=bakery` for any other filter on [`/places`](../api-endpoints/places): `taxonomy=food_and_drink` for every place to eat and drink, `brand_name` for one chain (see [Map every location of a retail chain](./map-every-location-of-a-retail-chain)), or `has_contact=website` to keep only places with a website.
- **Find what's worth exporting.** [Profile a neighbourhood](./profile-a-neighbourhood) uses `/places/categories` to count every category in a radius first.
- **Export footprints instead of points.** [`/buildings`](../api-endpoints/buildings) takes the same `format=geojson`, `includes` and paging.
- **Whole countries.** On a paid plan, replace `lat`, `lng` and `radius` with `country` plus a narrowing filter, and use a larger `limit`. See [Usage limits](/docs/limits).
---
# Map every location of a retail chain
Source: https://www.overturemapsapi.com/docs/guides/map-every-location-of-a-retail-chain
> Use the OpenSource Overture Maps API to pull every location of a brand across a country as GeoJSON and drop it on a map — worked example maps all 2,334 Greggs in the UK.
# Map every location of a retail chain
One of the most eye-catching things you can do with the Overture Maps API is put **every location of a brand** on a single map — "all the Greggs in the UK", "every Starbucks in the US", "all the Lidls in Germany". It makes for a great shareable graphic, and it's a five-minute job against the open Overture Maps data.
The running example maps **every Greggs in the UK** — all **2,334** of them at the time of writing. Every step works for any brand and country: swap `brand_name` and `country` for your own.
Greggs wordmark via [Wikimedia](https://en.wikipedia.org/wiki/File:Greggs2014.svg) (public domain).
## What you'll build
A single GeoJSON file containing one brand's locations across a whole country, ready to drop straight onto a map (via [geojson.io](https://geojson.io), Leaflet, Mapbox, QGIS — anything that reads GeoJSON).
## Step 1 — Find the brand
Filter places by **`brand_name`** and a country code (ISO 3166-1 alpha-2). Country-level queries need a [paid plan](/pricing); the Free plan and the demo key search by `lat`, `lng` and `radius` (up to 25 km), so on Free you can map a chain around one city at a time. Ask for a single result first to see the shape of the data, and check the `Pagination-Count` header for the total:
```bash
curl -i -H "x-api-key: YOUR-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'brand_name=Greggs' -d 'country=GB' -d 'limit=1'
```
```
Pagination-Count: 2334
```
That header is the total number of matching locations — 2,334 Greggs across the UK — so you know how many you're about to pull.
:::tip Pin to the exact brand with `brand_wikidata`
`brand_name` matches on the brand's name, which is usually exactly what you want. If you need to disambiguate a name shared by more than one company, use **`brand_wikidata`** with the brand's Wikidata QID instead — it matches the precise brand entity rather than a text string. Greggs, for example, is [`Q3403981`](https://www.wikidata.org/wiki/Q3403981).
:::
## Step 2 — Get it as GeoJSON
Add **`format=geojson`** and the response comes back as a standard `FeatureCollection` — every location a `Point` feature with its details in `properties`:
```json
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [-4.75910491, 55.94903626] },
"properties": {
"names": { "primary": "Greggs" },
"categories": { "primary": "bakery", "alternate": ["sandwich_shop", "fast_food_restaurant"] },
"websites": ["http://www.greggs.co.uk/"],
"phones": ["+441475601892"],
"brand": { "names": { "primary": "Greggs" }, "wikidata": null },
"addresses": [{ "freeform": "…", "locality": "…", "postcode": "…", "country": "GB" }]
}
}
]
}
```
Because it's already GeoJSON, most mapping tools will read it with no conversion.
## Step 3 — Pull the whole country
Results are paginated at up to 100 per page (`limit`), with `page` 0-indexed. Drive the loop off the `Pagination-Count` header and collect every feature into one file:
```javascript
import { writeFileSync } from "node:fs";
const base = "https://api.overturemapsapi.com/places";
const params = new URLSearchParams({
brand_name: "Greggs",
country: "GB",
format: "geojson",
limit: "100",
});
let page = 0;
const features = [];
while (true) {
const res = await fetch(`${base}?${params}&page=${page}`, {
headers: { "x-api-key": process.env.OVERTURE_API_KEY },
});
const batch = await res.json();
features.push(...batch.features);
const total = Number(res.headers.get("Pagination-Count"));
if (features.length >= total) break;
page++;
}
writeFileSync(
"greggs-uk.geojson",
JSON.stringify({ type: "FeatureCollection", features }),
);
console.log(`Saved ${features.length} locations to greggs-uk.geojson`);
```
At ~100 per page that's 24 requests for the full 2,334 — a few seconds of work.
## Step 4 — Put it on a map
The quickest way to see it: open [geojson.io](https://geojson.io) and drag `greggs-uk.geojson` onto the map. You'll get the whole country dotted with locations, instantly.
To embed it in a page, Leaflet is a couple of lines:
```html
```
## Step 5 — Add brand details (optional)
For chains, add **`enrichment_fields=brand`** to pull the brand's logo, official website, industry and parent company from Wikidata, returned as `ext_brand`:
```json
"ext_brand": {
"label": "Greggs",
"logo_url": "http://commons.wikimedia.org/wiki/Special:FilePath/Greggs%20logo.svg",
"website": "https://www.greggs.co.uk/",
"industry": "bakery"
}
```
Not every brand has a Wikidata match, so `ext_brand` only appears when Overture can resolve one — handy for badging the map with the official logo, or filling in the brand's canonical website. See [Build a local business directory](./build-a-local-business-directory) for more on the enrichment layer.
## Tips
- **`brand_name` vs `brand_wikidata`**: `brand_name` is the simple, readable filter; `brand_wikidata` pins to the exact brand entity when a name is ambiguous.
- **Coverage varies by market**: well-mapped countries return a fuller set. The `Pagination-Count` header always tells you the true total for your filter before you pull it.
- **Pagination**: drive the loop off `Pagination-Count`, not a fixed page count. See [Pagination](/docs/pagination).
- **Confidence**: add `min_confidence` (default `0.5`) if you want to trade recall for precision on the long tail.
## Publish it
A finished "every X in the country" map is genuinely shareable — it does well on LinkedIn and mapping communities, and it's a natural credit back to the API. Export the GeoJSON, publish the map, and link the data source. It's one of the cheapest pieces of content you can make and one of the most re-postable.
That's the whole loop: filter by `brand_name` and `country`, pull the country as GeoJSON, and drop it on a map.
---
# Profile a neighbourhood with Overture places data
Source: https://www.overturemapsapi.com/docs/guides/profile-a-neighbourhood
> Get the category mix of any neighbourhood from the Overture Maps API. Count places by category inside a radius, roll them up into Overture's 13 top-level taxonomy groups, and compare two areas. Worked example in Soho and Canary Wharf, London.
# Profile a neighbourhood with Overture places data
Site selection, property listings and local-insight dashboards all ask the same first question: what is actually around this point? Lots of offices, or lots of bars? Hotels, clinics, schools? With the raw Overture release you'd download the places GeoParquet and group by category in DuckDB. With the Overture Maps API it's one request for the detailed mix and a handful more for the big picture.
This guide profiles two very different parts of **London**: Soho and Canary Wharf. London is one of the demo key's cities, so every request below works as written with `DEMO-API-KEY`. Swap in your own coordinates and [your own key](https://www.overturemapsapi.com) when you're ready to go anywhere else.

Both profiles are built from live `/places/categories` and `/places` responses within 500 m. Map: OpenFreeMap, © OpenMapTiles, data © OpenStreetMap contributors.
## What you'll build
A profile for any point that shows:
- every place category within a radius, with a count for each, busiest first
- the share of places in each of Overture's 13 top-level groups (food and drink, shopping, lodging and so on)
- a side-by-side comparison of two areas
- the actual places behind any category, when you want to drill in
## Step 1: Count categories inside a radius
[`/places/categories`](../api-endpoints/places-categories) is usually shown with `country`, but it also takes `lat`, `lng` and `radius`. It returns one row per category with the number of places and the number of brands in it, sorted by place count:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places/categories' \
-d 'lat=51.5136' -d 'lng=-0.1365' -d 'radius=500'
```
```json
[
{ "primary": "corporate_or_business_office", "counts": { "places": 474, "brands": 0 } },
{ "primary": "hotel", "counts": { "places": 389, "brands": 4 } },
{ "primary": "clothing_store", "counts": { "places": 325, "brands": 57 } },
{ "primary": "professional_service", "counts": { "places": 284, "brands": 6 } },
{ "primary": "information_technology_company", "counts": { "places": 282, "brands": 0 } }
]
```
Within 500 m of central Soho that's 802 categories in a single response. The whole list comes back at once: `limit` and other `/places` filters such as `taxonomy` have no effect on this endpoint, so filter and slice on your side.
Run the same request for Canary Wharf (`lat=51.5054`, `lng=-0.0235`) and you get 503 categories, led by `information_technology_company` (140), `corporate_or_business_office` (108) and `financial_service` (97). Two lines of output already tell you these are different places.
## Step 2: Fetch a profile in TypeScript
```typescript
type CategoryCount = {
primary: string;
counts: { places: number; brands: number };
};
const API = "https://api.overturemapsapi.com";
const HEADERS = { "x-api-key": "YOUR-API-KEY" };
export async function categoryMix(
lat: number,
lng: number,
radius = 500,
): Promise {
const params = new URLSearchParams({
lat: String(lat),
lng: String(lng),
radius: String(radius),
});
const res = await fetch(`${API}/places/categories?${params}`, { headers: HEADERS });
if (!res.ok) throw new Error(`Categories request failed: ${res.status}`);
return (await res.json()) as CategoryCount[];
}
const soho = await categoryMix(51.5136, -0.1365);
console.log(soho.slice(0, 10)); // the ten busiest categories
```
The `brands` count is a useful second signal. In Soho, `clothing_store` has 57 brands among 325 places, so it's chain-heavy, while `corporate_or_business_office` has 474 places and no brands at all.
## Step 3: Roll up into the 13 top-level groups
Hundreds of fine-grained categories are great for detail and hard to compare. Overture's taxonomy puts every category under one of 13 top-level groups:
`services_and_business`, `shopping`, `food_and_drink`, `health_care`, `lifestyle_services`, `travel_and_transportation`, `education`, `cultural_and_historic`, `sports_and_recreation`, `lodging`, `community_and_government`, `arts_and_entertainment`, `geographic_entities`
The [`taxonomy` filter](../api-endpoints/places#taxonomy-hierarchy-aware) on `/places` matches a place's category or any ancestor, so `taxonomy=food_and_drink` covers every restaurant, café, bar and bakery. You don't need the places themselves, only how many there are, and that comes back in the `Pagination-Count` response header. So ask for one result per group and read the header:
```bash
curl -s -D - -o /dev/null -H "x-api-key: DEMO-API-KEY" -G 'https://api.overturemapsapi.com/places' \
-d 'lat=51.5136' -d 'lng=-0.1365' -d 'radius=500' -d 'taxonomy=food_and_drink' -d 'limit=1' \
| grep -i pagination-count
```
```
pagination-count: 1670
```
Do that for all 13 groups, plus once with no filter for the total, and divide:
```typescript
const GROUPS = [
"services_and_business", "shopping", "food_and_drink", "health_care",
"lifestyle_services", "travel_and_transportation", "education",
"cultural_and_historic", "sports_and_recreation", "lodging",
"community_and_government", "arts_and_entertainment", "geographic_entities",
] as const;
type Group = (typeof GROUPS)[number];
async function countPlaces(lat: number, lng: number, radius: number, taxonomy?: Group): Promise {
const params = new URLSearchParams({
lat: String(lat),
lng: String(lng),
radius: String(radius),
limit: "1",
});
if (taxonomy) params.set("taxonomy", taxonomy);
const res = await fetch(`${API}/places?${params}`, { headers: HEADERS });
if (!res.ok) throw new Error(`Places request failed: ${res.status}`);
return Number(res.headers.get("Pagination-Count") ?? 0);
}
export async function groupShares(lat: number, lng: number, radius = 500) {
const [total, ...counts] = await Promise.all([
countPlaces(lat, lng, radius),
...GROUPS.map((g) => countPlaces(lat, lng, radius, g)),
]);
return {
total,
groups: GROUPS.map((group, i) => ({
group,
places: counts[i],
share: Math.round((counts[i] / total) * 1000) / 10, // one decimal place
})).sort((a, b) => b.places - a.places),
};
}
```
That's 14 small requests per profile, well inside the demo key's 60 requests a minute. `Pagination-Count` is exposed to browsers through CORS, so this works client-side as well as on a server. The group counts add up to slightly less than the total, because a few places carry no taxonomy.
## Step 4: Compare two neighbourhoods
Here are the results for both areas at 500 m:
| Group | Soho | Canary Wharf |
| --- | --- | --- |
| All places | 11,000 | 2,467 |
| Services & business | 5,037 (45.8%) | 1,338 (54.2%) |
| Food & drink | 1,670 (15.2%) | 271 (11.0%) |
| Shopping | 1,575 (14.3%) | 247 (10.0%) |
| Arts & entertainment | 506 (4.6%) | 30 (1.2%) |
| Lodging | 441 (4.0%) | 21 (0.9%) |
| Health care | 209 (1.9%) | 95 (3.9%) |
Compare shares, not raw counts. Soho has four times as many places in the same radius, so it "wins" every row on counts alone. As shares, the story is clearer: Canary Wharf leans harder on business services and has proportionally more health care, while Soho's mix tilts towards food, shopping, entertainment and hotels.
```typescript
const [a, b] = await Promise.all([
groupShares(51.5136, -0.1365), // Soho
groupShares(51.5054, -0.0235), // Canary Wharf
]);
for (const g of a.groups) {
const other = b.groups.find((x) => x.group === g.group)!;
console.log(`${g.group.padEnd(28)} ${String(g.share).padStart(5)}% ${String(other.share).padStart(5)}%`);
}
```
Keep the radius the same when you compare, and keep it walkable. 500 m is a good default for a dense city centre; suburbs usually need 1,000 m or more to say anything useful. The demo key caps the radius at 2,000 m.
## Step 5: Drill into one category
When a number stands out, list the places behind it. Pass the category from Step 1 to the `categories` filter on `/places`:
```bash
curl -H "x-api-key: DEMO-API-KEY" -X GET -G 'https://api.overturemapsapi.com/places' \
-d 'lat=51.5136' -d 'lng=-0.1365' -d 'radius=500' -d 'categories=cocktail_bar' -d 'limit=5'
```
Results come back nearest first. Around central Soho the first three are Milk & Honey, Anam Bar and The Blind Pig, each with its name, coordinates, address, contact details and `operating_status`.
The two endpoints can disagree by a few places. `/places/categories` counts 110 cocktail bars in this radius, while the `/places` search above reports a `Pagination-Count` of 107. Treat a profile as a picture of the area, not an exact audit, and use `/places` when you need the precise list.
## Going further
- **Show the places on a map.** Add `format=geojson` to the drill-down request and hand it to MapLibre or Leaflet. [Map every location of a retail chain](./map-every-location-of-a-retail-chain) shows the Leaflet version.
- **Build a feed from the same filters.** The `taxonomy` and `categories` values you profiled are the same ones that power category chips in [Build a "what's near me" feature](./build-a-whats-near-me-feature).
- **Profile whole countries.** `/places/categories?country=GB` returns the national mix, which makes a handy baseline for "is this area unusual?". See [GET /places/categories](../api-endpoints/places-categories).
- **More results.** Page through any `/places` list with `limit` and `page`. See [Pagination](/docs/pagination).
- **What the data is.** [What is Overture Maps?](../what-is-overture-maps) covers the themes, licences and release cadence.
That's the whole profile: one `/places/categories` call for the detail, one header read per taxonomy group for the big picture, and shares rather than raw counts when you compare.
---
Source: https://www.overturemapsapi.com/docs/roadmap
# Roadmap & API Design Principles
We aim to have endpoints to cover all the Overture Maps data themes.
Currently these are:
- [Places](https://docs.overturemaps.org/schema/concepts/by-theme/places/) The Overture places theme includes more than 50M places and points of interest around the world. The data is sourced from Meta and Microsoft.
- [Addresses](https://docs.overturemaps.org/schema/reference/addresses/address/) The Overture Address type is a compilation of open address datasets usually published by local authorized sources. In our initial release, they use the datasets that are collected and distributed by OpenAddresses.
- [Buildings](https://docs.overturemaps.org/schema/concepts/by-theme/buildings/) The Overture buildings theme captures the compilation of many building attributes from a variety of open data sources including OpenStreetMap, Esri Community Maps, Microsoft, and Google. This includes [3d Building Parts](https://docs.overturemaps.org/schema/concepts/by-theme/buildings/3d_buildings/)
- [Transportation](https://docs.overturemaps.org/schema/concepts/by-theme/transportation/) The Overture transportation theme is the collection of features and properties that describe the infrastructure and conventions of how people and objects travel around the world. Transportation data includes highways, footways, cycleways, railways, ferry routes, and public transportation.
- [Roads](https://docs.overturemaps.org/schema/concepts/by-theme/transportation/roads/) In the Overture transportation theme, a road is any kind of road, street or path, including a dedicated path for walking or cycling, but excluding a railway. Road segments comprise the majority of ground-based transportation segments. Roads are modeled using segment features with the subtype property value set to the value road.
## Current Roadmap
### API Roadmap
- [x] **[./Places](./api-endpoints/places)** endpoint for Overture Maps 'Place' Theme
- [x] **[./Places/Brands](./api-endpoints/places-brands)** endpoint
- [x] **[./Places/Categories](./api-endpoints/places-categories)** endpoint
- [x] **[./Places/Countries](./api-endpoints/places-countries)** endpoint
- [x] **[./Places/Buildings](./api-endpoints/places-buildings)** endpoint
- [x] /Buildings endpoint for Overture Maps 'Building' Theme
- [ ] /Addresses endpoint for Overture Maps 'Address' Theme
- [ ] /Transportation & Roads endpoint sfor Overture Maps 'Transportation' Theme
- [ ] /Divisions endpoint for Overture Maps 'Division' Theme
- [ ] /Base endpoint for Overture Maps 'Base' Theme
## Future Roadmap
As new Overture Maps data themes are released, we will add endpoints to cover them.
## API Design
Response objects should be as close to the Overture Schema as possible, and use the `ext_` prefix for any additional fields. This allows us to easily map the response to the schema, and also allows us to add additional fields without breaking the schema.
### Extras
For any extra fields we add such as counts of the Places, Brands and Categories by Country, or number of Places (store lcoations) by Brand we will add these using the recommended `ext_` prefix to the field name e.g. `ext_counts`
### Query Parameters
Request parameters should use the Overture fields for reference, with underscore separators for filtering by nested fields. For example, `brand_wikidata` for filtering by `brand.wikidata`.
Where a filter is a name that could be used to filter against multiple fields, we should use the `name` field. For example, `brand_name=McDonalds` should filter against `brand.names.primary` and `brand.names.common`.
### Security
OWASP Top 10 security risks should be considered when designing the API
### Cost control
Rate limiting and caching should be used to control costs. We can use the free tier for Cloud Run and Cloud Storage to cache the data for faster response times. In production you should consider using Redis instead of Cloud storage for caching, and migrating the parts of the dataset you need to a private BigQuery dataset or a different database for speed and cost, especially for building shapes
### Strictness
As this is an educational API we should stick to the principle of being flexible in what we accept and strict in what we return. This means we should accept a wide range of input parameters, but return a strict response format.
---
Source: https://www.overturemapsapi.com/docs/support
# Support
You can ask questions in our popular [Discord](https://discord.gg/SRaRFcQ7Yw) or email me at [Aden@thatapicompany.com](mailto:aden@thatapicompany.com) and I will do my best to help you out.