OpenStreetMap POI Extractor: Cafes, Shops, Hotels by City avatar

OpenStreetMap POI Extractor: Cafes, Shops, Hotels by City

Pricing

from $0.50 / 1,000 poi saveds

Go to Apify Store
OpenStreetMap POI Extractor: Cafes, Shops, Hotels by City

OpenStreetMap POI Extractor: Cafes, Shops, Hotels by City

Extract points of interest from OpenStreetMap by city or bounding box and OSM tags (amenity=cafe, shop=supermarket, tourism=hotel). Name, coordinates, address, website, opening hours, all tags. No login, ODbL attribution included.

Pricing

from $0.50 / 1,000 poi saveds

Rating

0.0

(0)

Developer

Cliqto Media

Cliqto Media

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

10 days ago

Last modified

Security

Limited permissions

Share

Get points of interest from OpenStreetMap for a city or a map rectangle. Add a place name and one or more map tags such as amenity=cafe, and the Actor returns one row per place with name, coordinates, address, website, phone and opening hours.

Use it for local lead lists, travel guides, store-location research and map projects. The smallest useful run is one city and one tag, for example cafes in Pattaya. It takes about 35–60 seconds and costs about $0.019 for 20 places on the Free plan. You need no login and no API key.

OpenStreetMap POI Extractor: add a city and a map tag, get places with coordinates, address, hours, website and phone, export to JSON or CSV

Table of contents

What it does

The Actor reads OpenStreetMap (OSM), the free world map that volunteers edit. You give it an area and one or more map tags. It returns the places in that area that have those tags.

Each row has the OSM id and link, name, type, latitude and longitude, address, website, phone, email, opening hours, cuisine, brand, operator and wheelchair access. It can also include all raw OSM tags of the place. Every row carries the OpenStreetMap credit and the data timestamp.

Good fit

  • Lists of cafes, restaurants, hotels or shops in one city, with coordinates and contact fields.
  • Store-location and competitor maps: all supermarkets, pharmacies or fuel stations in a region.
  • Travel and tourism data: hotels, attractions, viewpoints and museums for a guide or an app.
  • Any other map object with an OSM tag, from bicycle parking to charging stations.

Not a fit

  • Reviews, ratings, photos or prices. OpenStreetMap does not store them.
  • A full business directory. Some places are missing from the map. Many places have no phone or website.
  • Many cities in one run. Run the Actor once per area, or use a bounding box.
  • Live opening status. openingHours is the schedule that mappers wrote down.

👉 Try it now: open the input form

How it works

  1. It finds your area. A place name becomes the official OSM boundary of that place. A bounding box is used as it is.
  2. It asks the public Overpass API for every place inside the area that has your tags. If one server is busy, it tries the next one.
  3. It saves one clean row per place in the Dataset and a short run report, RUN_SUMMARY, in the key-value store. You can export the rows as JSON, CSV or Excel.

👉 Try it now: open the input form

Quick start

Input form: Area set to Pattaya (box 1), OSM tags set to amenity=cafe (box 2), and Max POIs set to 20 (box 3)

  1. Open the Input tab. Area already has Pattaya (box 1 in the picture). Type your own city or a bounding box.

  2. Keep amenity=cafe in OSM tags, or add your own tag (box 2).

  3. Keep Max POIs at 20 for a first test (box 3). This is the form prefill; the runtime default is 100.

  4. Click Start at the bottom of the form.

  5. Open the Output tab and check the POIs table: name, type, coordinates, address, website and phone (box 5).

    Output table: the POIs view with name, type, coordinates, address, website and phone of each cafe

Expected result: about 20 rows of cafes with name, type, coordinates and, when mappers added them, address, phone and opening hours.

👉 Try it now: open the input form

Use cases

JobInputKey fieldsNext step
Lead list of local businessesarea = city, tags = amenity=restaurantname, phone, website, addressExport CSV to your CRM or spreadsheet
Store and competitor maparea = region, tags = shop=supermarketbrand, lat, lng, addressLoad into a GIS tool or a map library
Travel guide dataarea = city, tags = tourism=hotel, tourism=attractionname, type, website, openingHoursFeed a travel app or a content plan
Accessibility checkarea = district, tags = amenity=restaurantwheelchair, name, lat, lngFilter by wheelchair in a spreadsheet

👉 Try it now: open the input form

Input

Input fields

FieldName in the formTypeRequiredDefaultAllowed valuesWhat it does
areaArea (city or bounding box)stringyesnone (form example: Pattaya)A place name up to 200 characters, or south,west,north,east in decimal degrees, up to 5° per sideSets where to search. A place name uses the OSM boundary of that place.
tagsOSM tagsarray of stringsyesnone (form example: amenity=cafe)1–20 tags: key=value, key=*, key or key~regex, up to 120 characters each, with no quotes or backslashesSets which places to return.
matchCombine tagsstringnoanyany, allany: a place needs at least one tag. all: a place needs every tag.
maxItemsMax POIsintegerno100 (form example: 20)1–10000The largest number of place rows. Each place row is charged.
includeTagsInclude raw OSM tagsbooleannotruetrue, falseAdds the tags object with every OSM tag of the place. Turn it off for a smaller, flat table.

Notes:

  • A place name can be a city, a district or a region, for example Berlin Mitte or Lisbon, Portugal. Add the country when the name exists in many countries.
  • Duplicate tags are removed. Tag keys and values are case-sensitive, as in OSM.
  • When the area has more places than maxItems, the Actor returns maxItems places in the order the Overpass server sends them (grouped by map position, not by name or importance) and marks the run summary as truncated.

Settings that work together

WhenFieldsEffectExample
Several tags, match = anytags, matchPlaces with any of the tags.amenity=cafe and amenity=restaurant return cafes and restaurants.
Several tags, match = alltags, matchOnly places with every tag.amenity=restaurant and cuisine=thai return Thai restaurants.
A low Maximum total charge on the runmaxItems, run optionsThe Actor stops at the charge limit and reports a partial run.A limit that pays for 5 rows returns 5 rows.
maxItems above 500maxItemsThe run uses 256 MB memory instead of 128 MB. The price does not change.maxItems = 5000

Input examples

Cafes in one city

{
"area": "Pattaya",
"tags": ["amenity=cafe"],
"maxItems": 20
}

Expected result: 20 cafe rows. The run summary says that more cafes exist in the area.

Thai restaurants

{
"area": "Berlin",
"tags": ["amenity=restaurant", "cuisine=thai"],
"match": "all",
"maxItems": 500
}

Expected result: restaurants that have both tags, up to 500 rows.

Many kinds of places in a bounding box

{
"area": "12.90,100.85,12.98,100.93",
"tags": ["tourism=hotel", "tourism=guest_house", "shop=*"],
"maxItems": 1000,
"includeTags": false
}

Expected result: hotels, guest houses and all shops inside the rectangle, as a flat table without the tags object.

👉 Try it now: open the input form

OSM tag cheat sheet

You wantTag
Cafesamenity=cafe
Restaurantsamenity=restaurant
Bars and pubsamenity~^(bar|pub)$
Fast foodamenity=fast_food
Pharmaciesamenity=pharmacy
Fuel stationsamenity=fuel
EV chargingamenity=charging_station
Banks and ATMsamenity~^(bank|atm)$
Supermarketsshop=supermarket
Any shopshop=*
Hotelstourism=hotel
Attractionstourism=attraction
Gymsleisure=fitness_centre
Dentistsamenity=dentist
Officesoffice=*
One brandbrand=Starbucks (with match = all and a place tag)
Name contains a wordname~[Cc]offee (with match = all and a place tag; the match is case-sensitive)

The full list of tags is on the OSM wiki page Map features. In the input form you write | without the backslash. The backslash is only needed inside this table.

👉 Try it now: open the input form

Output

The Dataset has one row per place. The POIs view in the Output tab shows the main fields as a table. The run summary is a separate record, RUN_SUMMARY, in the key-value store.

Output fields

FieldMeaningTypeCan be empty
statuscomplete for a place, not_found or error for a free status rowstringno
statusReasonPlain reason for a not_found or error rowstringyes, null for places
errorCodeCode for an error row, for example INVALID_INPUT or SOURCE_TIMEOUTstringyes
osmIdOSM element id with type, for example node/123. Stable across runs.stringno for places
osmTypenode (a point), way (a building or outline) or relationstringno for places
osmUrlLink to the place on openstreetmap.orgstringno for places
nameName as mapped, in the local languagestringyes, some places have no name
nameEnEnglish name, when mappedstringyes
categoryKey of the matched tag, for example amenitystringno for places
typeValue of the matched tag, for example cafestringno for places
matchedTagThe matched tag, for example amenity=cafestringno for places
lat, lngLatitude and longitude. For buildings and outlines, the centre.numberno for places
addressObject with street, houseNumber, postcode, city, suburb, country and fullobjectyes, when no address is mapped
websiteBusiness website as an http(s) linkstringyes
phoneBusiness phone as mappedstringyes
emailBusiness email as mappedstringyes
openingHoursOpening hours in OSM format, for example Mo-Fr 08:00-18:00stringyes
cuisineFood type, for example thai;seafoodstringyes
brandBrand or chain namestringyes
operatorOperating companystringyes
wheelchairyes, limited or nostringyes
tagsAll OSM tags of the placeobjectnull when includeTags is off
areaYour area inputstringno
areaNameThe place name that the geocoder found, or the bounding boxstringyes
areaOsmIdOSM boundary used for the searchstringnull for a bounding box
attribution© OpenStreetMap contributorsstringno
licenseODbL 1.0stringno
licenseUrlhttps://www.openstreetmap.org/copyrightstringno
sourceoverpassstringno
sourceEndpointOverpass server that answeredstringyes
dataTimestampTime of the OSM data on that server, UTCstringyes
fetchedAtTime the Actor read the data, UTCstringno

null means the map has no value for that field. It does not mean zero or "no".

Output example

One place from the cafes in Pattaya example. The tags object is shortened.

{
"status": "complete",
"statusReason": null,
"errorCode": null,
"osmId": "way/1455905589",
"osmType": "way",
"osmUrl": "https://www.openstreetmap.org/way/1455905589",
"name": "Espresso Love",
"nameEn": null,
"category": "amenity",
"type": "cafe",
"matchedTag": "amenity=cafe",
"lat": 12.9049444,
"lng": 100.869448,
"address": {
"street": "Thappraya Rd",
"houseNumber": "315",
"postcode": "20150",
"city": null,
"suburb": null,
"country": null,
"full": "Thappraya Rd 315, 20150"
},
"website": null,
"phone": "+66 81 982 4727",
"email": null,
"openingHours": "Mo-Su 06:00-19:00",
"cuisine": "coffee_shop",
"brand": null,
"operator": null,
"wheelchair": null,
"tags": {
"amenity": "cafe",
"cuisine": "coffee_shop",
"name": "Espresso Love",
"opening_hours": "Mo-Su 06:00-19:00",
"outdoor_seating": "no"
},
"area": "Pattaya",
"areaName": "Pattaya City, Nong Pla Lai, Bang Lamung District, Chon Buri Province, Thailand",
"areaOsmId": "relation/12645794",
"attribution": "© OpenStreetMap contributors",
"license": "ODbL 1.0",
"licenseUrl": "https://www.openstreetmap.org/copyright",
"source": "overpass",
"sourceEndpoint": "https://overpass-api.de/api/interpreter",
"dataTimestamp": "2026-10-17T08:32:06Z",
"fetchedAt": "2026-10-17T08:33:11.550Z"
}

In this run, most of the 20 cafes had a name and coordinates. Only a few had a phone, a website or opening hours. That is normal for OSM data.

Run summary

RUN_SUMMARY has the run status, the area that was found, counts of places and free rows, truncated (true when the area has more places than maxItems), the OSM data timestamp and request statistics.

Output entities and surfaces

SituationWhere to lookResult shapeRun statusChargedWhat to do
Places foundDataset, POIs viewOne row per place, status = completeCOMPLETEStart fee + each placeOpen, export or filter the rows
No place has your tags in the areaDataset + RUN_SUMMARYOne free row, status = not_found, reason in statusReasonNOT_FOUNDStart fee onlyTry a wider tag or check the area
The area name is not foundDataset + RUN_SUMMARYOne free not_found rowNOT_FOUNDStart fee onlyFix the spelling or add the country
The charge limit stops the run earlyDataset + RUN_SUMMARYThe rows that fit the limit; partialReason = CHARGE_LIMITPARTIALStart fee + stored placesRaise Maximum total charge
The charge limit is too low for any placeRUN_SUMMARYNo rowsCAPPEDStart fee onlyRaise Maximum total charge
Invalid input, for example a bad tagDataset + RUN_SUMMARYOne free error row with errorCode = INVALID_INPUTINVALID_INPUT, run failsStart fee onlyFix the input field named in statusReason
OSM servers fail or time out before any place is savedDataset + RUN_SUMMARYOne free error row with errorCode, for example SOURCE_TIMEOUTERRORStart fee onlyWait a few minutes and run again
OSM servers fail after some places are savedRUN_SUMMARY and the run status messageThe places already saved, no error row; the reason is in RUN_SUMMARY (partialReason = SOURCE_ERROR, errorCode) and in the run status messagePARTIALStart fee + stored placesRun again for the full list

A source failure is never shown as an empty result. Check status and errorCode in the row and in RUN_SUMMARY.

Use case → fields

Buyer jobInput pathOutput entityKey fieldsNext integration
Local lead listarea, tags = amenity=restaurantPlace rowname, phone, website, address.fullCSV export to a CRM or spreadsheet
Store and competitor maparea, tags = shop=supermarketPlace rowbrand, lat, lng, osmIdGIS tool or map library
Travel guide dataarea, tags = tourism=hotelPlace rowname, type, website, openingHoursTravel app or content plan
Repeat checks of one areathe same input on a schedulePlace rowosmId, dataTimestampCompare runs by osmId

👉 Try it now: open the input form

Pricing

Pricing unit

You pay per event. There is no monthly fee. The unit is one place row: a row with status = complete in the Dataset.

EventFree planBronzeSilverGold and higher
Actor start (per GB of run memory)$0.0045$0.0045$0.0045$0.0045
Place row (POI)$0.00072$0.000648$0.000576$0.000504
  • The start fee is the same on every plan. Apify charges it automatically when the run starts. It grows with run memory: one start event per GB, rounded up. This Actor uses 128 MB, or 256 MB above 500 rows, so every run has one start event.
  • Only place rows with status = complete are charged. not_found and error rows are free, so a valid run that finds nothing, a filter with no matches and a failed run cost only the start fee.
  • Platform usage (compute and storage) is included in these prices. You pay only the events above.

Cost examples

Places returnedFree planGold plan
1$0.0052$0.0050
10$0.0117$0.0095
100$0.0765$0.0549
1000$0.7245$0.5085

A run that finds nothing costs only the start fee. You can set Maximum total charge in the run options. The limit covers everything: the start fee is counted first, and the Actor saves only as many places as the rest of the limit pays for. It never charges more than the limit.

👉 Try it now: open the input form

API, export and integrations

  • API: start the Actor with the same input JSON through the Apify API. Read rows from the default Dataset, and RUN_SUMMARY from the default key-value store.
  • Export: download the Dataset as JSON, CSV, Excel, XML or HTML from the Output tab or the API.
  • Scheduling: save an input as a task and run it on an Apify schedule, for example once a week for a fresh list.
  • Integrations: send the Dataset to Google Sheets, Zapier, Make or a webhook with Apify integrations.

Use with AI agents (MCP)

AI agents can run this Actor through the Apify MCP server. The server URL with only this Actor:

https://mcp.apify.com?tools=cliqtomedia/openstreetmap-poi-extractor

Smallest useful input:

{
"area": "Pattaya",
"tags": ["amenity=cafe"],
"maxItems": 20
}

Key output fields:

  • name: the place name as mapped.
  • lat, lng: coordinates of the place.
  • address.full: the address in one line, when mapped.
  • phone, website, openingHours: contact data and hours, when mapped.
  • status: complete for a place. not_found and error rows explain an empty or failed run.

A typical run with 20 rows takes 35–60 seconds and costs $0.019 on the Free plan. One run covers one area. maxItems is 10000 at most.

👉 Try it now: open the input form

Limits

  • One area per run. A bounding box can be 5° per side at most. In a dense city, keep the box to about 2° per side: a 2° box around Paris returned 1000 restaurants in 57 seconds. A larger box only makes the servers slower; it does not add more rows than maxItems.
  • maxItems is 10000 at most. A run with 10000 rows in Paris took about 90 seconds.
  • 1–20 tags per run, up to 120 characters each. Tag values cannot contain quotes or backslashes.
  • A place name can be 200 characters at most.
  • The Actor waits for OSM servers for about 150 seconds at most. After that it stops and reports a source error.
  • Data depends on OSM volunteers. Coverage is different from region to region.
  • The data is as fresh as the Overpass server. dataTimestamp shows the time, usually a few minutes old.
  • The Actor uses public OSM servers. It follows the Nominatim usage policy and does not send heavy parallel requests.

👉 Try it now: open the input form

Troubleshooting

The run returned one not_found row

The area has no place with your tags, or the place name was not found. Check statusReason. Try a wider tag such as shop=*, check the spelling of the city, or add the country, for example Valencia, Spain.

The place is in the wrong country

Many place names exist more than once. Check areaName in the row. Add the region or country to area, or use a bounding box.

The run stopped with SOURCE_TIMEOUT, SOURCE_UNAVAILABLE, RATE_LIMITED or DEADLINE_REACHED

The public OSM servers were busy, refused more requests, or did not answer within about 150 seconds. The Actor already tried other Overpass servers. Wait a few minutes and run it again. A smaller area or a lower maxItems also helps.

The run stopped with SOURCE_BAD_REQUEST

The Overpass server rejected the query. This usually means a key~regex tag with a broken regular expression. Check the tag named in statusReason, fix the expression and run again.

The run stopped with SOURCE_INCOMPLETE or SOURCE_SCHEMA_CHANGED

SOURCE_INCOMPLETE: the Overpass servers that answered had no data for this area yet. SOURCE_SCHEMA_CHANGED: a server sent an answer the Actor could not read, or one larger than its safety limit. Run again later. If it repeats, open an issue on the Issues tab with the run link.

Fewer rows than maxItems

If status is PARTIAL and the summary says CHARGE_LIMIT, your Maximum total charge was too low. Raise it. If the run is COMPLETE, the area simply has fewer matching places.

Many fields are null

OSM has only what volunteers mapped. Use tags to see every value that exists for the place.

👉 Try it now: open the input form

FAQ

Do I need an OpenStreetMap account or an API key?

No. The Actor uses public OSM services. You need only an Apify account.

Can I use the data in my product?

Yes, under the ODbL licence. You must credit OpenStreetMap contributors. If you share a changed database, it must stay under ODbL. Read the OSM copyright page.

Is the list complete?

No map is complete. The Actor returns every place that OSM has with your tags in the area, up to maxItems. truncated in RUN_SUMMARY tells you when more places exist.

Will two runs return the same places?

Yes, if the map did not change. osmId stays the same, so you can compare runs by osmId.

Does the Actor collect personal data?

It returns only public map data about places, not data about people. The phone, email and website fields hold public business contacts that mappers added to the map, for example the phone of a cafe. Some small businesses are run by one person, so check your local rules before you use these contacts for marketing.

👉 Try it now: open the input form

Data licence

Map data © OpenStreetMap contributors, available under the Open Database License (ODbL) 1.0. Every row has attribution, license and licenseUrl. Keep the credit when you publish the data.

👉 Try it now: open the input form

👉 Try it now: open the input form

Support

Use the Issues tab of this Actor for bugs, questions and ideas. Add the run ID, your input and the errorCode or statusReason. Do not send API tokens or other private data.

👉 Try it now: open the input form