# OpenStreetMap Scraper · POI & Places by City, Category, Radius (`thequietstack/openstreetmap-extract`) Actor

Places from OpenStreetMap (Overpass) as one clean table: name, category, address, coordinates, website, phone, opening hours, OSM link. Pick a city, bounding box or radius plus 34 category presets or your own tag filter. Field completeness report. Not found areas are never charged.

- **URL**: https://apify.com/thequietstack/openstreetmap-extract.md
- **Developed by:** [TheQuietStack](https://apify.com/thequietstack) (community)
- **Categories:** Lead generation, Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 extracted places

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## OpenStreetMap Scraper · POI & Places by City, Category, Radius

**OpenStreetMap scraper for POI and places:** name, address, coordinates, website, phone and opening hours by city, radius or bounding box, no API key.

**OpenStreetMap POI Scraper** exports places from **OpenStreetMap** as one clean table: cafes in Kiel, dentists in Leeds, EV chargers within 5 km of a point, every bicycle shop in a bounding box. Name, category, full address, coordinates, website, phone, email, opening hours, brand, a link to the OSM object and the timestamp of the data, for every row. An open-data alternative to Google Maps scraping, with the license already in every row.

### What you get

- **Area by name, box or radius.** `"Kiel, Germany"` is resolved with Nominatim; or pass `south,west,north,east`; or a center point plus radius.
- **34 category presets** (restaurants, cafes, dentists, doctors, pharmacies, hotels, ev\_chargers, supermarkets, gyms, lawyers, ...) **plus your own OpenStreetMap tag filters** (`shop=bicycle`, `cuisine~pizza`, `["amenity"="cafe"]["internet_access"="wlan"]`).
- **"Only places that have"** a website, phone, email, opening hours or address: filtered on the server, before anything is charged.
- **Field completeness report** in the run summary: exactly how many of the rows have a website, phone, opening hours ... so you know what OSM covers in your area before you build on it.
- **License built in**: every row carries `license` and `attribution` (ODbL 1.0), so the data stays usable in your product.
- **Honest failures.** A place name that does not exist fails the run with *Area not found*. An overloaded or failing Overpass server fails the run with the server's message. Neither is ever reported as "0 results", and neither is charged.
- **Data source is open data**, no login, no API key, no bot protection, no proxy needed.

### Use cases

- **Local lead lists**: dentists, lawyers, gyms or hairdressers in a city, with website and phone, filtered to places that have them.
- **Store locators and maps**: every EV charger, pharmacy or bike shop in a region with coordinates and opening hours.
- **Site selection and market research**: count competitors around a point with `radiusMeters`.
- **Data enrichment**: OSM IDs and links to join with your own records; `includeRawTags` for every tag.
- **Training and analytics datasets** under an open license (ODbL share-alike applies, see below).

### Input

| Field | What it does |
|---|---|
| `areas` | Place names, e.g. `["Kiel, Germany", "Lübeck, Germany"]`. Be specific: add the country. |
| `boundingBox` | Alternative: `"54.30,10.10,54.35,10.16"` (south,west,north,east). |
| `latitude`, `longitude`, `radiusMeters` | Alternative: search around a point (radius up to 50 km). |
| `categories` | Presets, see the table below. |
| `customFilters` | Your own tag filters, one per line. Lines are OR, conditions inside one line are AND. |
| `requireFields` | `name`, `website`, `phone`, `email`, `openingHours`, `address`. |
| `maxResults` | Hard limit on rows written and charged (default 100). |
| `maxCostUsd` | Optional own spending cap, computed from the live per-row price of the run. |
| `includeRawTags` | Adds every OSM tag of the place as `tags` (default on). |
| `overpassUrl`, `fallbackOverpassUrls`, `overpassTimeoutSec`, `nominatimUrl` | Which public servers to use. |

Example:

```json
{
  "areas": ["Kiel, Germany"],
  "categories": ["cafes"],
  "requireFields": ["website"],
  "maxResults": 20
}
```

#### Category presets

| Preset | OpenStreetMap tags (OR) |
|---|---|
| restaurants | amenity=restaurant |
| cafes | amenity=cafe |
| bars\_pubs | amenity=bar, amenity=pub |
| fast\_food | amenity=fast\_food |
| bakeries | shop=bakery |
| supermarkets | shop=supermarket |
| convenience\_stores | shop=convenience |
| hotels | tourism=hotel, tourism=motel |
| lodging | tourism=hotel, motel, guest\_house, hostel, apartment |
| dentists | amenity=dentist, healthcare=dentist |
| doctors | amenity=doctors, healthcare=doctor |
| pharmacies | amenity=pharmacy |
| hospitals | amenity=hospital |
| veterinarians | amenity=veterinary |
| hairdressers | shop=hairdresser |
| beauty\_salons | shop=beauty |
| gyms | leisure=fitness\_centre |
| ev\_chargers | amenity=charging\_station |
| fuel\_stations | amenity=fuel |
| car\_repair | shop=car\_repair |
| car\_dealers | shop=car |
| bicycle\_shops | shop=bicycle |
| banks | amenity=bank |
| atms | amenity=atm |
| lawyers | office=lawyer |
| accountants | office=accountant, office=tax\_advisor |
| estate\_agents | office=estate\_agent |
| schools | amenity=school |
| kindergartens | amenity=kindergarten |
| parking | amenity=parking |
| toilets | amenity=toilets |
| playgrounds | leisure=playground |
| museums | tourism=museum |
| attractions | tourism=attraction |

### Output

One row per OpenStreetMap object (node, way or relation; ways and relations get their center point). Real row from a run on the Apify platform on 01.10.2026 (Kiel, cafes, 5 rows), `tags` omitted:

```json
{
  "name": "Mum und Dad",
  "category": "cafes",
  "matchedFilter": "amenity=cafe",
  "categories": [
    "cafes"
  ],
  "street": "Ziegelteich",
  "houseNumber": "14",
  "postcode": "24103",
  "city": "Kiel",
  "country": "DE",
  "addressFull": "Ziegelteich 14, 24103 Kiel, DE",
  "latitude": 54.3191408,
  "longitude": 10.1319589,
  "website": "https://www.mumdadkiel.de/",
  "phone": null,
  "email": "info@mumdadkiel.de",
  "openingHours": "Mo-Th 12:00-24:00, Fr 12:00-01:00, Sa 10:00-01:00",
  "brand": "MUM&DAD",
  "operator": null,
  "cuisine": "snacks",
  "wheelchair": "limited",
  "osmType": "node",
  "osmId": 192202688,
  "osmUrl": "https://www.openstreetmap.org/node/192202688",
  "osmVersion": 24,
  "lastEditedAt": "2026-09-01T11:39:20Z",
  "dataTimestamp": "2026-10-01T09:42:51Z",
  "area": "Kiel, Schleswig-Holstein, Deutschland",
  "scrapedAt": "2026-10-01T09:43:27.543Z",
  "license": "ODbL-1.0",
  "attribution": "© OpenStreetMap contributors (ODbL 1.0, https://www.openstreetmap.org/copyright)"
}
```

Field completeness of that same run (`SUMMARY.fieldCompleteness`, excerpt): name 5/5, website 5/5, addressFull 4/5,
openingHours 4/5, brand 3/5, phone 1/5, email 1/5. OSM coverage differs by city and category; this report shows it
before you rely on a field.

`website` and `phone` also read the `contact:*` variants. OSM contributor user names are not exported.

The **run summary** (key-value store record `SUMMARY`) lists the resolved area per input (with the OSM object Nominatim matched), the exact Overpass query sent, the data timestamp, field completeness (`filled` / `total` per field, plus a percentage rounded to one decimal), areas not found, failures, duplicates skipped and the billing counts.

### How much does it cost?

Pay per event. Platform usage is included; you pay only for places written to the dataset.

| Event | Charged when | Price |
|---|---|---|
| `place-extracted` (Extracted place) | One row written to the dataset. | $0.002 |
| Actor start | once per run | $0.00005 |

Examples:

- **1,000 places** = **$2**. **10,000 places** = **$20**.
- All cafes with a website in one mid-sized city, say 150 rows = **$0.30**.
- An area name that does not exist, a failed query or an area with 0 matches = **$0** (only the Actor start).

Never charged: areas not found, failed or timed-out queries, areas with 0 matches, duplicate objects across overlapping areas, invalid input. `maxResults`, your own `maxCostUsd` and the platform's *Max cost per run* are all respected; the run stops cleanly before any of them is exceeded.

### Limits and good behaviour

- Uses the **public** Overpass API (default `overpass-api.de`, fallback `maps.mail.ru` mirror) and the **public** Nominatim geocoder. Both are run by volunteers / sponsors. The Actor sends one Overpass query per area, sequentially, with an identifying User-Agent; on HTTP 429/504 it backs off (15 s, 45 s) and then tries the fallback instance. Nominatim is called at most once per second and results are cached for 30 days in a named key-value store (`openstreetmap-extract-geocode-cache`).
- Very large areas (a whole country with a broad category) can exceed the server's memory or time limit. You get the server's error message, not an empty result. Split the area, narrow the category, or raise `overpassTimeoutSec`. For heavy, repeated use, run your own Overpass instance and set `overpassUrl`.
- `maxResults` is passed to Overpass as an output limit, so a small run is a small query.
- OpenStreetMap coverage varies by country and category. The completeness report shows what you got; it does not fill gaps. There are no reviews, ratings or photos in OSM.

### Use it via API, integrations and AI agents

```bash
curl -X POST "https://api.apify.com/v2/acts/thequietstack~openstreetmap-extract/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"areas":["Leeds, United Kingdom"],"categories":["dentists"],"requireFields":["website"],"maxResults":50}'
```

Python (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("thequietstack/openstreetmap-extract").call(run_input={
    "latitude": 52.52, "longitude": 13.405, "radiusMeters": 5000,
    "categories": ["ev_chargers"], "maxResults": 500,
})
for place in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(place["name"], place["latitude"], place["longitude"], place["osmUrl"])
```

- **Export**: JSON, CSV, Excel, XML or HTML from the dataset tab or API (`?format=csv`).
- **Integrations**: Google Sheets, Make, Zapier, n8n, webhooks via the integrations tab.
- **AI agents (MCP)**: `https://mcp.apify.com?tools=thequietstack/openstreetmap-extract`.

### FAQ

**Do I need an API key?** No. The Actor uses the public Overpass API and Nominatim; no login, no key.

**How does this compare to Google Maps scraping?** OSM has no reviews, ratings or photos, and coverage varies by
country. In return the data is openly licensed (ODbL), carries an exact edit timestamp, and is cheap per row. The
completeness report tells you how many rows have a website, phone or opening hours in your area.

**Can I use the data commercially?** Yes, under the ODbL: attribute "© OpenStreetMap contributors" and, if you publish
a derived database, share it under the ODbL. Details below.

**How fresh is the data?** Overpass is usually minutes behind the live OSM database. Every row carries `dataTimestamp`
(state of the Overpass database) and `lastEditedAt` (last edit of that place).

**Can I scrape a whole country?** Large areas with a broad category can exceed the public server's limits. You then get
the server's error, not an empty result. Split the area, narrow the category, or set your own `overpassUrl`.

**Why does a place have no address?** OSM contributors did not map one. The row is still exported (unless you use
`requireFields: ["address"]`) and counted in the completeness report.

**What does "Area not found" cost?** Nothing. Not-found areas, failed queries and empty areas are never charged.

### Data license: OpenStreetMap, ODbL 1.0

**The data is © OpenStreetMap contributors and licensed under the [Open Database License (ODbL) 1.0](https://opendatacommons.org/licenses/odbl/1-0/).** See https://www.openstreetmap.org/copyright.

If you use the output you must:

1. **Attribute**: show "© OpenStreetMap contributors" with a link to https://www.openstreetmap.org/copyright wherever the data or anything produced from it is shown (map, app, website, report, dataset).
2. **Share alike**: if you publicly use a database derived from this data (for example, you merge it with your own data and publish or offer it), that derived database must be made available under the ODbL. Produced works such as a printed map or a chart may use any license, but must still carry the attribution.
3. Keep the license notice with the data. Every row carries `license` and `attribution` fields for this reason.

Nominatim and Overpass usage policies: https://operations.osmfoundation.org/policies/nominatim/ and https://wiki.openstreetmap.org/wiki/Overpass\_API#Public\_Overpass\_API\_instances.

The **Actor code** is separate from the data and is licensed under ISC. Running this Actor does not grant you any rights to the data beyond the ODbL.

### Legal

This Actor reads only openly licensed data from public, documented APIs, without login and without circumventing any protection. You are responsible for how you use the data: ODbL obligations above, and data-protection law where rows describe sole traders or contain personal contact details (for example GDPR when you use phone numbers or emails for marketing). Not affiliated with or endorsed by the OpenStreetMap Foundation.

# Actor input Schema

## `areas` (type: `array`):

City, district, region or country names, resolved with OpenStreetMap Nominatim (max 1 lookup per second, cached). Be specific: "Kiel, Germany" beats "Kiel". A name that does not exist fails the run with "Area not found" - it is never reported as 0 results.

## `boundingBox` (type: `string`):

Alternative to a place name: "south,west,north,east" in decimal degrees, e.g. "54.30,10.10,54.35,10.16".

## `latitude` (type: `string`):

Alternative: search around a point. Decimal degrees, e.g. 54.3233.

## `longitude` (type: `string`):

Decimal degrees, e.g. 10.1228.

## `radiusMeters` (type: `integer`):

Radius around the center point. Only used with latitude/longitude.

## `categories` (type: `array`):

Ready-made category presets (each maps to OpenStreetMap tags, listed in the README).

## `customFilters` (type: `array`):

Your own OpenStreetMap tag filters, one per line (OR between lines, AND inside one line). Examples: shop=bicycle · cuisine~pizza · \["amenity"="cafe"]\["internet\_access"="wlan"] · \["name"~"bio",i]. Find tags at wiki.openstreetmap.org/wiki/Map\_features.

## `requireFields` (type: `array`):

Filtered on the server before anything is charged: e.g. only places with a website and a phone number.

## `maxResults` (type: `integer`):

Hard limit on rows written (and charged) in this run.

## `maxCostUsd` (type: `string`):

Optional own spending cap. Uses the live per-row price of this run; the run stops cleanly before going over. The platform "Max cost per run" limit is respected as well.

## `includeRawTags` (type: `boolean`):

Adds a "tags" object with every OpenStreetMap tag of the place (cuisine, diet, wheelchair, brand:wikidata, ...).

## `overpassUrl` (type: `string`):

Public Overpass instance to query. Respect the instance's usage policy; heavy users should run their own.

## `fallbackOverpassUrls` (type: `array`):

Tried in order when the main instance is busy (HTTP 429/504). Default: maps.mail.ru Overpass mirror. Pass an empty list to disable.

## `overpassTimeoutSec` (type: `integer`):

Server-side query timeout. Large areas need more; public instances may refuse very long timeouts when busy.

## `nominatimUrl` (type: `string`):

Geocoder for area names. The public instance allows max 1 request per second.

## Actor input object example

```json
{
  "areas": [
    "Kiel, Germany"
  ],
  "radiusMeters": 1000,
  "categories": [
    "cafes"
  ],
  "maxResults": 20,
  "includeRawTags": true,
  "overpassUrl": "https://overpass-api.de/api/interpreter",
  "overpassTimeoutSec": 120,
  "nominatimUrl": "https://nominatim.openstreetmap.org"
}
```

# Actor output Schema

## `places` (type: `string`):

One row per OpenStreetMap object (node, way or relation) with ODbL attribution.

## `summary` (type: `string`):

Resolved areas, Overpass query per area, field completeness counts, data timestamp, areas not found, failures, billing counts.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "areas": [
        "Kiel, Germany"
    ],
    "categories": [
        "cafes"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("thequietstack/openstreetmap-extract").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "areas": ["Kiel, Germany"],
    "categories": ["cafes"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("thequietstack/openstreetmap-extract").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "areas": [
    "Kiel, Germany"
  ],
  "categories": [
    "cafes"
  ],
  "maxResults": 20
}' |
apify call thequietstack/openstreetmap-extract --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thequietstack/openstreetmap-extract"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/HHSlwYbYfTOqk66A2/builds/FLmnTbePewycO3FfJ/openapi.json
