Skip to content

Points of interest

A point of interest (POI) is a place such as a café, school, pharmacy or station. Each one has a name, a location and a category, the sources that list it, and whether an official register confirms it. This page explains where places come from, how reliable they are and how to filter them.

Sources

We build the places dataset from three open sources, one country at a time.

  • Foursquare Open Places is strongest for shops, restaurants and services.
  • OpenStreetMap is strongest for schools, parks, places of worship and transport. Coverage varies by country.
  • Overture Maps is a Linux Foundation project that combines several sources, including the two above. We count an Overture record as extra evidence only when it did not come from Foursquare or OpenStreetMap.

Where an official register exists, we use it as well. See Official registers.

How we match records

For each country, we match records that describe the same place. Matched records become one place, and we record which sources it came from.

A match needs three things to agree.

  • The names must be similar. We compare names by the words they share and by edit distance.
  • The locations must be close. Two records with different names match only when they are within 15 m of each other. Records with similar names can be further apart.
  • The two categories must be compatible.

You do not need to know how a place was matched to use it. The sources and verified fields carry the result.

How a place is verified

Every place is at one of three steps. Use them to decide which places to show.

StepWhat it meansUse it for
Verifiedverified is true. An official register confirms the place, or it is a TravelTime transport station.Anything
Two or more sourcessource_count is 2 or 3. At least two independent apps and maps list the place.Customer-facing results and analytics
One sourcesource_count is 1. One app or map lists the place.Searches where finding more places matters more than every result being right

A verified place can have any source_count. By default, every POI endpoint returns verified places and places with two or more sources. This request returns cafés within 500 m of Trafalgar Square with that default filter.

bash

curl -H "Authorization: Bearer $POI_API_KEY" \
  "$POI_BASE/v1/pois/near?country=GB&lat=51.5074&lng=-0.1278&radius_m=500&category=cafe&limit=5"

A place is verified when we match it to a record in an official register we have added, such as the Food Hygiene Rating Scheme in the UK, or when it is a station from TravelTime's public transport data. GET /v1/catalog lists each country's registers under gov_registers. A register never changes source_count.

verified_by lists what verifies the place, one entry per register record. Each entry has these keys. A key is absent when we do not know its value.

  • name is the register's name.
  • record_id is the record's id in the register, and record_name its name there.
  • matched_on says how we matched the record: name and location, name and postcode, location and category, added by the register, or our own data for a transport station.
  • name_match names the rule the two names met, such as identical.
  • distance_m is the distance between the record and the place, in metres.

Some places are in a register but in no app or map. We add them from the register. Such a place has origin: "register", sources: [], source_count: 0 and verified: true, and its verified_by entry has matched_on: "added by the register". A transport station has sources: ["traveltime"], source_count: 1 and one verified_by entry, {"name": "TravelTime public transport data", "matched_on": "our own data"}.

Closed is a flag, not a step. When a register records a place as closed, closed is {"by": "<register name>", "on": "YYYY-MM-DD"}, with on set to null when the register gives no date. Otherwise closed is null.

For each register with a judged sample, GET /v1/catalog shows how often its matches are right, as match_accuracy on the register's entry in sources. Its correct counts only the sampled pairs judged same or different; unsure pairs are left out.

Source counts and min_sources

We recommend the default filter for most uses. Set min_sources=1 when you need every place and can accept lower precision. Set min_sources=3 for work where every result must be right, and expect to miss real places.

source_count is the number of independent apps and maps that list a place: Foursquare, OpenStreetMap and Overture, or TravelTime for a station. sources names them. An Overture record that copies a Foursquare or OpenStreetMap record is left out of both.

min_sources sets the lowest source_count a place needs, with the values 1, 2 or 3. Every POI list endpoint accepts it, and so does every cell endpoint that counts or lists places. Verified places pass every value, and so do places in a public transport category. Unset applies 2, so min_sources=1 widens the results and 3 narrows them.

Every endpoint that takes min_sources also takes two more filters.

  • verified=true returns only verified places. verified=false returns only unverified places that pass min_sources. Leave it out for both.
  • include_closed is true by default. include_closed=false leaves out places a register marks closed.

bash

curl -H "Authorization: Bearer $POI_API_KEY" \
  "$POI_BASE/v1/pois/near?country=GB&lat=51.5074&lng=-0.1278&radius_m=500&category=cafe&limit=5&min_sources=1"

This request returns verified pharmacies within 1 km of Trafalgar Square, leaving out closed ones.

bash

curl -H "Authorization: Bearer $POI_API_KEY" \
  "$POI_BASE/v1/pois/near?country=GB&lat=51.5074&lng=-0.1278&radius_m=1000&category=pharmacy&verified=true&include_closed=false&limit=5"

Single-source places include duplicates and closed businesses. We keep them for min_sources=1.

The Accuracy page gives agreement and coverage for every country.

Official registers

Some categories have an official register that lists every place in them. Where one exists, we use it as the primary source for that category. A place in the register is included even when no other source lists it. A place from another source that the register does not list loses that category, and stays in the data under any others it has. These registers do this:

  • Hospitals, GPs and dentists in England come from the Care Quality Commission register of regulated providers.
  • Schools in England come from the Department for Education's Get Information About Schools register.
  • Hospitals in Australia come from AIHW MyHospitals.

Public transport stations come from TravelTime's own timetable data, in every country. See Public transport.

The API applies registers for you. You do not need to know which categories have one. See How a place is verified.

Response fields

Every POI endpoint returns GeoJSON. A list endpoint returns a FeatureCollection. Each feature has a point geometry, one longitude and latitude, and these properties.

PropertyWhat it holds
idThe place's id in this country's dataset. Ids change when the dataset updates.
nameThe name we chose from the sources.
brandThe chain name, such as Tesco, or null. See Filter by brand.
categoryThe primary category.
categoriesEvery category the place belongs to.
categories_detailThe detail types under those categories, such as sushi_restaurant.
category_groupsThe groups those categories belong to, such as food_and_drink.
sourcesThe apps and maps that list the place: foursquare, osm, overture, or traveltime for a station.
source_countThe number of entries in sources. See Source counts and min_sources.
verifiedtrue when a register or TravelTime's transport data confirms the place.
verified_byOne entry per record that verifies the place. See How a place is verified.
closed{by, on} when a register records the place as closed, otherwise null.
origin"register" on a place a register added. Absent on every other place.
date_refreshedWhen a source last changed the record, as an ISO 8601 UTC time. Absent when no source records a time.

Search results rank older records lower in categories that change often. Use date_refreshed for your own rules on age.

Full parameter and response shapes are in the API reference.

Get one place

GET /v1/pois/{id} returns one place as a GeoJSON Feature. The id comes from a list or search response. There is no lookup by name or address. Add include_sources=true to get sources_detail, which holds each source's own id for the place.

bash

POI_ID=$(curl -s -H "Authorization: Bearer $POI_API_KEY" \
  "$POI_BASE/v1/pois/search?country=GB&q=British+Museum&category=museum&limit=1" \
  | jq -r '.features[0].properties.id')

curl -H "Authorization: Bearer $POI_API_KEY" \
  "$POI_BASE/v1/pois/$POI_ID?country=GB&include_sources=true"

This endpoint ignores min_sources, verified and include_closed, so a place stays readable by id after you tighten a list filter. An unknown id returns 404 with the code poi_not_found. An id that is not a UUID returns 422.

Ids are assigned when a country's dataset updates. Watch last_refreshed in GET /v1/stats?country=GB and look stored places up again when it changes. See Data updates.

Filter by category

The category parameter accepts a comma-separated list of ids. Each id can be a group, a category or a detail type. A broader id includes every detail type under it. category=restaurant includes every cuisine. category=food_and_drink also includes cafés, bars and bakeries. An unknown id returns 422 with suggestions.

There are 16 groups: food_and_drink, shopping, health_and_wellness, finance, business_and_services, lodging, automotive, government, culture, education, places_of_worship, entertainment_and_recreation, sports, transportation, geographical_areas and uncategorised.

GET /v1/categories returns the full tree for a country, with the number of places in each entry.

bash

curl -H "Authorization: Bearer $POI_API_KEY" "$POI_BASE/v1/categories?country=GB"

Filter by brand

/v1/pois/near, /v1/pois/in-bbox, /v1/pois/search and the per-cell endpoints below accept a brand parameter. It keeps the places of one chain. The match is on the whole brand name and ignores case, so brand=Tesco and brand=tesco are the same filter. Tesco Express is a separate brand. Places with no brand never match.

bash

curl -H "Authorization: Bearer $POI_API_KEY" \
  "$POI_BASE/v1/pois/near?country=GB&lat=51.5074&lng=-0.1278&radius_m=500&category=cafe&brand=Costa&limit=3"

There is no endpoint that lists brands. Read the brand property of places in an area to see which brands a country has. Brands come from the sources, mainly OpenStreetMap tags, so large chains are covered better than regional ones.

Count places per cell

GET /v1/h3/pois returns a count per H3 cell. The count is the number of distinct places in the cell that pass the default filter, or the min_sources, verified and include_closed you send. A place with several categories is counted once. POST /v1/h3/pois takes the same query as a JSON body, for long cell lists.

bash

curl -H "Authorization: Bearer $POI_API_KEY" \
  "$POI_BASE/v1/h3/pois?country=GB&cells=89195da49b7ffff&category=cafe"

Add breakdown=true for a count per category. By default a place counts towards each of its categories, so the breakdown can add up to more than count. Add match=primary to count each place under its main category only, the same rule the place searches use with match=primary. The breakdown then never adds up to more than count, and category=supermarket&match=primary gives the same number as a search.

Add brand=Tesco to count one chain, or brand_breakdown=true for a count per brand in each cell, such as {"Tesco": 3, "Aldi": 1}. Places with no brand are left out of that map.

bash

curl -H "Authorization: Bearer $POI_API_KEY" \
  "$POI_BASE/v1/h3/pois?country=GB&cells=89195da49b7ffff&category=supermarket&match=primary&brand_breakdown=true"

GET /v1/h3/pois/records returns the places themselves, up to per_cell_limit per cell. The default is 100 and the maximum is 1,000. A cell with more matching places than the limit has truncated: true. POST /v1/h3/pois/records takes the same query as a JSON body.

/v1/geohash/pois and /v1/geohash/pois/records do the same for geohashes. See Query by geohash.

What's not included

  • Opening hours, phone numbers and reviews.
  • Live information, such as whether a place is open or busy now.
  • Building outlines or unit-level addresses. Each place has one coordinate. For a park or another area, it is a representative point inside the area.
  • Private or paid data. Every place comes from a source that can be redistributed.
  • Same-day changes. A closed business stays in the data until our sources remove it and the next update runs.