# OSM POI Extractor (`dataio/osm-poi-extractor`) Actor

Extract local businesses and places from OpenStreetMap by place name and category — with phone, email, website, address and coordinates. Open data, no login, no proxies.

- **URL**: https://apify.com/dataio/osm-poi-extractor.md
- **Developed by:** [Tom Awake](https://apify.com/dataio) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## OSM POI Extractor

Extract local businesses and places from OpenStreetMap **by place name and
category** — with phone numbers, emails, websites, opening hours, addresses
and coordinates.

**No login. No cookies. No proxies. No API keys. Nothing to break.**

Type `Bordeaux, France` and `restaurants`. That is the whole configuration.

### A legal alternative to scraping Google Maps

Scraping Google Maps breaks Google's terms of service. This Actor reads
**OpenStreetMap**, an open database, through the public Overpass and
Nominatim APIs — access that is offered, documented and free. There is no
anti-bot system to defeat, no account to get suspended, and no proxy bill.

The data is licensed **ODbL 1.0**: you may use it commercially, including in
a lead list or a product, provided you attribute OpenStreetMap. Every row
carries an `attribution` field so the requirement travels with the data. If
you redistribute a *derived database*, ODbL's share-alike terms apply to
that database — worth knowing before you resell the raw export.

### What makes this one different

**It takes a place name, not a bounding box.** Overpass needs
`(44.81,-0.63,44.91,-0.53)` and `amenity=restaurant`. That is why most
OpenStreetMap tools go unused. Here you type a town and pick from 82
categories.

**It queries the real administrative area, not a rectangle.** Asking for
"Gironde" by bounding box means querying the Atlantic Ocean: measured, 64
near-empty requests and over ten minutes. This Actor resolves the
département's actual boundary and returns 488 pharmacies in about four
seconds.

**It returns the most complete records first.** Overpass emits points before
buildings, so a capped run would hand you an arbitrary, biased slice. Rows
are ranked by how contactable they are before the limit applies. Measured on
Bordeaux restaurants, asking for 120 out of 835:

| | Naive order | This Actor |
|---|---|---|
| With a phone number | 47 % | **100 %** |
| With a website | 47 % | **99 %** |
| With a street address | — | **97 %** |

**It includes places mapped as buildings.** Many POIs are areas, not points.
Querying nodes only silently loses them — 37 of 835 Bordeaux restaurants.

### Output

One row per place, deduplicated.

| Field | Example |
|---|---|
| `name` | La Tupiña |
| `category` | restaurants |
| `phone` | +33 5 56915637 |
| `email` | contact@latupina.com |
| `website` | http://www.latupina.com/ |
| `address` | 6 Rue Porte de la Monnaie |
| `street`, `housenumber`, `postcode`, `city`, `country` | split for mail merge |
| `latitude`, `longitude` | 44.8335, -0.5658 |
| `openingHours` | `Mo-Su, PH 11:00-23:00` |
| `cuisine`, `brand`, `operator`, `wheelchair` | when mapped |
| `siret`, `siren` | French business identifiers, when mapped |
| `osmType`, `osmId`, `osmUrl` | provenance, to verify any row |
| `attribution` | © OpenStreetMap contributors, ODbL 1.0 |

French places often carry `ref:FR:SIRET` and `ref:FR:SIREN`, which lets you
join the export straight to the French company register. Measured on
Bordeaux restaurants: 84 of the first 120 rows carried a SIRET.

### Input

```json
{
  "location": "Bordeaux, France",
  "category": "restaurants",
  "maxItems": 500,
  "requirePhone": false
}
```

| Field | Default | Notes |
|---|---|---|
| `location` | `Bordeaux, France` | Town, city, district, département, region or country |
| `category` | `restaurants` | 82 presets — see below |
| `maxItems` | `500` | Up to 50,000 |
| `requirePhone` | `false` | Keep only places with a phone number |
| `requireWebsite` | `false` | Keep only places with a website |
| `boundingBox` | — | `[south, west, north, east]`, overrides `location` |
| `center` + `radiusMeters` | — | Circle around a point |
| `customTags` | — | `["amenity=restaurant"]` for tags with no preset |

#### Categories

**Food and drink** — restaurants, fast-food, cafes, bars, nightclubs,
bakeries, caterers

**Accommodation** — hotels, guest-houses, campsites

**Health** — pharmacies, doctors, dentists, hospitals, clinics,
veterinarians, opticians, physiotherapists

**Retail** — supermarkets, convenience-stores, clothing-stores,
shoe-stores, furniture-stores, hardware-stores, florists, bookshops,
jewellers, electronics-stores, bicycle-shops, pet-shops, butchers,
greengrocers, wine-shops, tobacco-shops

**Personal services** — hairdressers, beauty-salons, tattoo-parlours,
dry-cleaners, gyms, spas

**Automotive** — car-dealers, car-repair, car-rental, petrol-stations,
car-wash, driving-schools

**Professional services** — real-estate-agents, lawyers, accountants,
insurance-offices, banks, architects, travel-agencies,
employment-agencies, advertising-agencies, it-companies, coworking-spaces

**Trades** — plumbers, electricians, carpenters, painters, roofers,
locksmiths, builders

**Education** — schools, kindergartens, universities, language-schools

**Leisure** — museums, attractions, theatres, cinemas, sports-centres,
swimming-pools, golf-courses, playgrounds

**Logistics and infrastructure** — warehouses, post-offices,
parcel-lockers, ev-charging, parking, atms

Anything else: use `customTags` with a tag from
[taginfo.openstreetmap.org](https://taginfo.openstreetmap.org).

### Use cases

- **Local lead lists** — pick a category and a city, tick "only places with
  a phone number", export a call list.
- **Market and site analysis** — count competitors per town, map their
  density, compare coverage between areas.
- **Store locators and directories** — seed a directory with real
  coordinates and opening hours.
- **Enriching a CRM** — match on name and postcode, fill in missing phone
  numbers and websites.
- **Field logistics** — route planning from real coordinates.

### Coverage, honestly

OpenStreetMap is volunteer-mapped, so completeness varies by country and by
category. Measured on 966 Bordeaux restaurants: 98.8 % had a name, 74.3 % a
street address, 48.9 % opening hours, 41.5 % a website, 41.1 % a phone
number, 12.2 % an email address.

Dense European cities are mapped well. Rural areas and some categories are
thinner. This Actor reports what is in the database — it does not invent
missing fields, and it will not pretend a village has ten dentists.

### Behaviour and limits

- Several Overpass mirrors are used in turn; a loaded or failing mirror is
  skipped automatically.
- Very large areas with no administrative boundary fall back to a tiled
  scan, deduplicated across tiles.
- Places with no name are skipped: a nameless point is not a business.
- Nominatim's usage policy is respected — one geocoding request per run.

# Actor input Schema

## `location` (type: `string`):

A town, city, district or region — for example "Bordeaux, France", "Brooklyn, New York" or "Bavaria". Resolved to an area automatically. Leave empty only if you supply a bounding box or a centre point below.

## `category` (type: `string`):

82 ready-made categories. For anything not listed, use Custom OSM tags below.

## `maxItems` (type: `integer`):

Stops once this many places have been collected. Large areas are split into tiles and queried automatically.

## `requirePhone` (type: `boolean`):

Useful for call lists. Roughly 40% of places carry a phone number, so this shrinks the result substantially.

## `requireWebsite` (type: `boolean`):

Roughly 40% of places carry a website.

## `boundingBox` (type: `array`):

Exact area as \[south, west, north, east] in decimal degrees. Overrides Place when set.

## `center` (type: `object`):

Used with Radius, as {"lat": 44.84, "lon": -0.58}.

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

Used with Centre point.

## `customTags` (type: `array`):

Raw OpenStreetMap tag filters, as \["amenity=restaurant", "shop=bakery"]. Overrides the category when set. Use the OSM wiki or taginfo.openstreetmap.org to find tags.

## Actor input object example

```json
{
  "location": "Bordeaux, France",
  "category": "restaurants",
  "maxItems": 500,
  "requirePhone": false,
  "requireWebsite": false
}
```

# Actor output Schema

## `results` (type: `string`):

Every row produced by the run, in JSON.

# 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 = {
    "location": "Bordeaux, France",
    "maxItems": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataio/osm-poi-extractor").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 = {
    "location": "Bordeaux, France",
    "maxItems": 500,
}

# Run the Actor and wait for it to finish
run = client.actor("dataio/osm-poi-extractor").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 '{
  "location": "Bordeaux, France",
  "maxItems": 500
}' |
apify call dataio/osm-poi-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dataio/osm-poi-extractor"
        }
    }
}
```

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/aIbdp59E1d9SdqUY4/builds/Hp5atyhGBT2fQHChG/openapi.json
