How to get OSM Places by category for a region

Places API results on the map by Geoapify

Have you ever needed to get a list of all the places in a particular city or region that fit a specific category, like restaurants, bookstores, or museums? Or maybe you need a list of all the cafes in a specific area?

With the Places API from Geoapify, you can easily query data from OpenStreetMap (OSM), which is the primary data source for the places. The Places API lets you get places by category, and you can filter the results by region (suburb, postcode, city, etc.).

This tutorial will show you how to get OSM places by category within a certain city, postcode, suburb, or administrative boundary.

Jump to our Live Demo to see how it works.

Using Places API to query the data

The Places API is a RESTful web service that lets you query for places by category within a geographical area, using a radius, bounding box, or place_id (which specifies a particular region) parameter:

  • circle: With this filter, you can specify a radius (in meters) and a center point (latitude and longitude). The API will return all places within the specified radius of the center point.

    Example: filter=circle:-87.770231,41.878968,5000

  • rectangle (bounding box): This filter lets you specify a rectangular area. The API will return all places within the specified bounding box.

    Example: filter=rect:-89.097540,39.668983,-88.399274,40.383412

  • place (city, postcode, suburb, etc): This filter lets you specify a particular region, using a place_id. The API will return all places within the specified region.

    Example: filter=place:51f07665660fc4024059dc0a96dfac6c...

For example, you can get all the cafes within a certain radius of a particular point, or all the restaurants in Paris or London.

The Places API supports a wide variety of place categories. You can find the complete list of categories on the Geoapify Places API docs.

How to get a place_id for an administrative boundary

To query by region, you need a place_id. A place_id is a unique identifier for a particular place, and you can get one with the Geoapify APIs - Geocoding API, Places API, and Place Details API.

For example, the Geocoding API lets you search a place by name (like "Paris, France") and returns a place_id with the results. To use the Geocoding API, you need to sign up for a Geoapify account and get an API key.

Below is the code you need to get a region data including a place_id for "Paris, France":

function getCityData(city, apiKey) {
  Return
fetch(`https://api.geoapify.com/v1/geocode/search?text=${encodeURIComponent(city)}&format=json&apiKey=${myAPIKey}`).then(data => data.json()).then(results => {
    const cityData = results.results.length ? results.results[0] : null;
    return cityData;
  });
}

console.log(getCityData("Paris, France", YOUR_API_KEY));

In the above code, we are using the fetch() method to make a GET request to the Geoapify Geocoding API. We are also passing in our Geoapify API key.

The Geocoding API will return a JSON object with the data for the specified place. The object will contain a place_id field, which is what we need.

Getting the Places

Now you can use the Places API to get a list of places in the region specified by place_id.

The Places API uses the following endpoint to get places by region:

https://api.geoapify.com/v2/places?categories=${category}&filter=place:${cityId}&limit=${limit}&apiKey=${apiKey}&offset=${offset}
  • cityId: This is the place_id. You will use the place_id that you got from the Geocoding API.

  • category: The place category from the supported list of categories. You can set one or multiple categories here.

  • apiKey: Your Geoapify API key.

  • limit: The maximal number of places returned in the request.

  • offset: The offset is the number of results to skip to get to the following page. First, you get places from 1 to 20, then from 21-to 40, etc.

As the API return only a limited number of places with one request, we will call the API recursively. We use the offset parameter to get the following places. Below is the code to get the data:

function getAllPlaces(category, cityId, apiKey) {
  return getPlacesRecursively([], 0, category, cityId, apiKey);
}

// one Places API request returns maximum of 500 objects, so it may require making several API calls to get all the places
function getPlacesRecursively(foundPlaces, offset, category, cityId, apiKey) {
  // Set the maximal number of Places returned within one API call. Note more significant limit number will require a more significant processing time.
  const limit = 20;
  return fetch(`https://api.geoapify.com/v2/places?categories=${category}&filter=place:${cityId}&limit=${limit}&apiKey=${apiKey}&offset=${offset}`).then(data => data.json()).then(places => {
    // Geoapify Places API returns a GeoJSON FeatureCollection object as a result. Let's collect only properties.
    foundPlaces.push(...places.features.map(feature => feature.properties));

    console.log(`${foundPlaces.length} places found`);

    if (places.features.length === 0 || stopped) {
      return foundPlaces;
    } else {
      // get the next page
      return getPlacesRecursively(foundPlaces, offset + limit, category, cityId, apiKey);
    }
  });
}

console.log(getAllPlaces("commercial.books", cityData.place_id, YOUR_API_KEY));

Downloading the Places to a JSON file

Once you have the data, you can save it to a file. The following code will create a JSON file:

// Download results
const a = document.createElement('a');
const blob = new Blob([JSON.stringify(results)], {'type':'application\/json'});
a.href = window.URL.createObjectURL(blob);
a.download = `${placeCategory}-${city}-${(new Date()).toUTCString()}.json`;
a.click();

Here is an example of data you get:

    • "Librairie Le Monde libertaire"
    • "145"
    • "Rue Amelot"
    • "Quartier de la Folie-Méricourt"
    • "11th Arrondissement"
    • "Paris"
    • "Paris"
    • "Ile-de-France"
    • "Metropolitan France"
    • "75011"
    • "France"
    • "fr"
    • 2.3663328
    • 48.8650907
    • "Librairie Le Monde libertaire, 145 Rue Amelot, 75011 Paris, France"
    • "Librairie Le Monde libertaire"
    • "145 Rue Amelot, 75011 Paris, France"
    • [] 2 items
      • [] 6 items
        • {} 5 keys
          • "51ea9d1be43fee024059afb2c34abb6e4840f00103f901b3d1f18a0000000092031d4c6962726169726965204c65204d6f6e6465206c696265727461697265"

        Each place contains the address, the categories it belongs to, and the raw OSM object.

        Live Demo

        Here is a JSFiddle that shows how the Places API works. You can set a city name and change its category to download OpenStreetMap places for the city.

        In this article, we've shown how you can use the Geoapify Places API to find places by category for a particular region, city, or postcode.

        You can download up to 60,000 places for free every day with our Free pricing plan. You can also test the API without registering with the API playground.

        What's next