OpenStreetMap POI Extractor: Cafes, Shops, Hotels by City
Pricing
from $0.50 / 1,000 poi saveds
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 days ago
Last modified
Categories
Security
Limited permissionsShare
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.

Table of contents
- What it does
- How it works
- Quick start
- Use cases
- Input
- OSM tag cheat sheet
- Output
- Pricing
- API, export and integrations
- Limits
- Troubleshooting
- FAQ
- Data licence
- Related Actors
- Support
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.
openingHoursis the schedule that mappers wrote down.
How it works
- It finds your area. A place name becomes the official OSM boundary of that place. A bounding box is used as it is.
- 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.
- 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.
Quick start
-
Open the Input tab. Area already has
Pattaya(box 1 in the picture). Type your own city or a bounding box. -
Keep
amenity=cafein OSM tags, or add your own tag (box 2). -
Keep Max POIs at
20for a first test (box 3). This is the form prefill; the runtime default is100. -
Click Start at the bottom of the form.
-
Open the Output tab and check the POIs table: name, type, coordinates, address, website and phone (box 5).
Expected result: about 20 rows of cafes with name, type, coordinates and, when mappers added them, address, phone and opening hours.
Use cases
| Job | Input | Key fields | Next step |
|---|---|---|---|
| Lead list of local businesses | area = city, tags = amenity=restaurant | name, phone, website, address | Export CSV to your CRM or spreadsheet |
| Store and competitor map | area = region, tags = shop=supermarket | brand, lat, lng, address | Load into a GIS tool or a map library |
| Travel guide data | area = city, tags = tourism=hotel, tourism=attraction | name, type, website, openingHours | Feed a travel app or a content plan |
| Accessibility check | area = district, tags = amenity=restaurant | wheelchair, name, lat, lng | Filter by wheelchair in a spreadsheet |
Input
Input fields
| Field | Name in the form | Type | Required | Default | Allowed values | What it does |
|---|---|---|---|---|---|---|
area | Area (city or bounding box) | string | yes | none (form example: Pattaya) | A place name up to 200 characters, or south,west,north,east in decimal degrees, up to 5° per side | Sets where to search. A place name uses the OSM boundary of that place. |
tags | OSM tags | array of strings | yes | none (form example: amenity=cafe) | 1–20 tags: key=value, key=*, key or key~regex, up to 120 characters each, with no quotes or backslashes | Sets which places to return. |
match | Combine tags | string | no | any | any, all | any: a place needs at least one tag. all: a place needs every tag. |
maxItems | Max POIs | integer | no | 100 (form example: 20) | 1–10000 | The largest number of place rows. Each place row is charged. |
includeTags | Include raw OSM tags | boolean | no | true | true, false | Adds 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 MitteorLisbon, 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 returnsmaxItemsplaces in the order the Overpass server sends them (grouped by map position, not by name or importance) and marks the run summary astruncated.
Settings that work together
| When | Fields | Effect | Example |
|---|---|---|---|
Several tags, match = any | tags, match | Places with any of the tags. | amenity=cafe and amenity=restaurant return cafes and restaurants. |
Several tags, match = all | tags, match | Only places with every tag. | amenity=restaurant and cuisine=thai return Thai restaurants. |
| A low Maximum total charge on the run | maxItems, run options | The Actor stops at the charge limit and reports a partial run. | A limit that pays for 5 rows returns 5 rows. |
maxItems above 500 | maxItems | The 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.
OSM tag cheat sheet
| You want | Tag |
|---|---|
| Cafes | amenity=cafe |
| Restaurants | amenity=restaurant |
| Bars and pubs | amenity~^(bar|pub)$ |
| Fast food | amenity=fast_food |
| Pharmacies | amenity=pharmacy |
| Fuel stations | amenity=fuel |
| EV charging | amenity=charging_station |
| Banks and ATMs | amenity~^(bank|atm)$ |
| Supermarkets | shop=supermarket |
| Any shop | shop=* |
| Hotels | tourism=hotel |
| Attractions | tourism=attraction |
| Gyms | leisure=fitness_centre |
| Dentists | amenity=dentist |
| Offices | office=* |
| One brand | brand=Starbucks (with match = all and a place tag) |
| Name contains a word | name~[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.
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
| Field | Meaning | Type | Can be empty |
|---|---|---|---|
status | complete for a place, not_found or error for a free status row | string | no |
statusReason | Plain reason for a not_found or error row | string | yes, null for places |
errorCode | Code for an error row, for example INVALID_INPUT or SOURCE_TIMEOUT | string | yes |
osmId | OSM element id with type, for example node/123. Stable across runs. | string | no for places |
osmType | node (a point), way (a building or outline) or relation | string | no for places |
osmUrl | Link to the place on openstreetmap.org | string | no for places |
name | Name as mapped, in the local language | string | yes, some places have no name |
nameEn | English name, when mapped | string | yes |
category | Key of the matched tag, for example amenity | string | no for places |
type | Value of the matched tag, for example cafe | string | no for places |
matchedTag | The matched tag, for example amenity=cafe | string | no for places |
lat, lng | Latitude and longitude. For buildings and outlines, the centre. | number | no for places |
address | Object with street, houseNumber, postcode, city, suburb, country and full | object | yes, when no address is mapped |
website | Business website as an http(s) link | string | yes |
phone | Business phone as mapped | string | yes |
email | Business email as mapped | string | yes |
openingHours | Opening hours in OSM format, for example Mo-Fr 08:00-18:00 | string | yes |
cuisine | Food type, for example thai;seafood | string | yes |
brand | Brand or chain name | string | yes |
operator | Operating company | string | yes |
wheelchair | yes, limited or no | string | yes |
tags | All OSM tags of the place | object | null when includeTags is off |
area | Your area input | string | no |
areaName | The place name that the geocoder found, or the bounding box | string | yes |
areaOsmId | OSM boundary used for the search | string | null for a bounding box |
attribution | © OpenStreetMap contributors | string | no |
license | ODbL 1.0 | string | no |
licenseUrl | https://www.openstreetmap.org/copyright | string | no |
source | overpass | string | no |
sourceEndpoint | Overpass server that answered | string | yes |
dataTimestamp | Time of the OSM data on that server, UTC | string | yes |
fetchedAt | Time the Actor read the data, UTC | string | no |
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
| Situation | Where to look | Result shape | Run status | Charged | What to do |
|---|---|---|---|---|---|
| Places found | Dataset, POIs view | One row per place, status = complete | COMPLETE | Start fee + each place | Open, export or filter the rows |
| No place has your tags in the area | Dataset + RUN_SUMMARY | One free row, status = not_found, reason in statusReason | NOT_FOUND | Start fee only | Try a wider tag or check the area |
| The area name is not found | Dataset + RUN_SUMMARY | One free not_found row | NOT_FOUND | Start fee only | Fix the spelling or add the country |
| The charge limit stops the run early | Dataset + RUN_SUMMARY | The rows that fit the limit; partialReason = CHARGE_LIMIT | PARTIAL | Start fee + stored places | Raise Maximum total charge |
| The charge limit is too low for any place | RUN_SUMMARY | No rows | CAPPED | Start fee only | Raise Maximum total charge |
| Invalid input, for example a bad tag | Dataset + RUN_SUMMARY | One free error row with errorCode = INVALID_INPUT | INVALID_INPUT, run fails | Start fee only | Fix the input field named in statusReason |
| OSM servers fail or time out before any place is saved | Dataset + RUN_SUMMARY | One free error row with errorCode, for example SOURCE_TIMEOUT | ERROR | Start fee only | Wait a few minutes and run again |
| OSM servers fail after some places are saved | RUN_SUMMARY and the run status message | The places already saved, no error row; the reason is in RUN_SUMMARY (partialReason = SOURCE_ERROR, errorCode) and in the run status message | PARTIAL | Start fee + stored places | Run 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 job | Input path | Output entity | Key fields | Next integration |
|---|---|---|---|---|
| Local lead list | area, tags = amenity=restaurant | Place row | name, phone, website, address.full | CSV export to a CRM or spreadsheet |
| Store and competitor map | area, tags = shop=supermarket | Place row | brand, lat, lng, osmId | GIS tool or map library |
| Travel guide data | area, tags = tourism=hotel | Place row | name, type, website, openingHours | Travel app or content plan |
| Repeat checks of one area | the same input on a schedule | Place row | osmId, dataTimestamp | Compare runs by osmId |
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.
| Event | Free plan | Bronze | Silver | Gold 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=completeare charged.not_foundanderrorrows 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 returned | Free plan | Gold 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.
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_SUMMARYfrom 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:completefor a place.not_foundanderrorrows 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.
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. maxItemsis 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.
dataTimestampshows 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.
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.
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.
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.
Related Actors
- Website Contact Scraper — add emails and social links from the
websiteof each place. - Phone Number Validator & Formatter — check the
phonevalues and turn them into one E.164 format. - Bulk Email Verifier — check the
emailvalues before outreach. - Website Tech Stack Detector — see which CMS or shop software each place website uses.
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.

