# OpenStreetMap Local Business Directory (`jetlaunch/osm-local-business-directory`) Actor

Build a local business directory from OpenStreetMap data: restaurants, cafes, dentists, shops and more, with addresses, coordinates and tagged contact data.

- **URL**: https://apify.com/jetlaunch/osm-local-business-directory.md
- **Developed by:** [Chris O'Byrne](https://apify.com/jetlaunch) (community)
- **Categories:** Lead generation, Business, Open source
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 business records

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 Local Business Directory

Build a clean local business directory from OpenStreetMap data. Give a place name (or a bounding box) and pick categories such as restaurants, cafes, dentists, hardware stores, gyms or hotels. You get one flat, CSV-friendly record per business with its address, coordinates, category, cuisine and other useful tags, plus website, phone and opening hours when they are tagged as business contact data. Every record carries the OpenStreetMap attribution and license details.

The data comes from OpenStreetMap through the Overpass API. No login, API key or proxy is needed. This Actor is not affiliated with or endorsed by the OpenStreetMap Foundation, the Overpass API operators or any other third party named here.

### Who it is for

- Marketers and sales teams building a prospect list for a town or district
- Local SEO and directory builders who need a starting list of businesses
- Researchers and analysts mapping where certain kinds of business are located
- Developers who want a normalized OSM point-of-interest feed without writing Overpass queries

### What it does

1. Looks up the administrative boundary of your place in OpenStreetMap, or uses your bounding box.
2. Runs one Overpass query for the categories you selected.
3. Drops unnamed features, anything tagged as a private residence, and any feature whose name looks like a private individual's home.
4. Normalizes each feature into a flat record and removes duplicates.
5. Returns up to your maximum, charging per business returned.

### Input

| Field | Meaning |
| --- | --- |
| Place name | For example `Rochester, Minnesota`. Add a region or country to avoid mix-ups between towns with the same name. |
| Bounding box | Optional. `south,west,north,east`, for example `43.95,-92.55,44.08,-92.38`. Replaces the place name. Maximum 16 square degrees. |
| Business categories | Presets: restaurants, cafes, fast food, bars and pubs, bakeries, dentists, doctors and clinics, pharmacies, veterinarians, hardware stores, supermarkets and grocery, gyms, hotels and lodging, hair and beauty, car repair, car dealers, banks, gas stations, lawyers, accountants, real estate agents, insurance agencies, clothing and shoe stores, bookstores, florists, laundry and dry cleaning. |
| Extra OSM tags | Optional `key=value` pairs such as `shop=books` or `craft=brewery`. Allowed keys: amenity, shop, craft, office, tourism, leisure, healthcare. |
| Name contains | Optional filter on the business name. |
| Maximum results | Default 25, up to 2,000. |
| Remove duplicates | On by default. |
| Include website, phone and opening hours | On by default. |
| Include all OSM tags | Off by default. Adds a `tags` column with the feature's other tags as JSON text. |
| Overpass endpoint | Advanced. Leave empty for the default public instance, or enter your own or a paid Overpass server. |

Example input (this is also the default prefill):

```json
{
    "place": "Rochester, Minnesota",
    "categories": ["cafes"],
    "maxResults": 25
}
```

### Output

One record per business. All values are plain strings, numbers, booleans or null, so the dataset exports cleanly to CSV and Excel.

```json
{
    "osmType": "node",
    "osmId": 123456789,
    "osmUrl": "https://www.openstreetmap.org/node/123456789",
    "name": "Example Coffee",
    "category": "Cafe",
    "categoryKey": "amenity=cafe",
    "cuisine": "coffee_shop",
    "houseNumber": "123",
    "street": "Main St",
    "city": "Rochester",
    "state": "MN",
    "postcode": "55901",
    "fullAddress": "123 Main St, Rochester, MN 55901",
    "latitude": 44.0121,
    "longitude": -92.4802,
    "website": "http://example.com/",
    "phone": "+1 507 555 0100",
    "openingHours": "Mo-Fr 07:00-17:00",
    "lastEditedAt": "2025-06-14T17:22:05Z",
    "dataSource": "OpenStreetMap via Overpass API",
    "attribution": "© OpenStreetMap contributors",
    "license": "ODbL 1.0",
    "licenseUrl": "https://opendatacommons.org/licenses/odbl/1-0/",
    "osmDataTimestamp": "2026-10-21T23:20:00Z",
    "queryArea": "Rochester, Minnesota (OSM relation 184106)"
}
```

The example values are illustrative. Real records are returned from live OpenStreetMap data, and fields that are not tagged for a feature are null. For ways and relations (buildings and areas), latitude and longitude are the feature's center point. A run also saves a `SUMMARY` record in the key-value store with counts of fetched, excluded and duplicate features.

### Pricing

Pay per event. You pay only for businesses returned, never for failed runs or empty results.

| Event | Price |
| --- | --- |
| Actor start | $0.00005 per run (set by Apify) |
| Business record (`poi-record`) | $0.002 per business returned |

For example, 1,000 businesses cost about $2.00. Apify platform usage is included in these prices. A 25-business run uses well under one cent of platform compute.

### Data source, license and what you must do with it

All data is copyright OpenStreetMap contributors and available under the Open Database License (ODbL) 1.0. Details: https://www.openstreetmap.org/copyright

- Attribution: wherever you publish or show this data, credit "© OpenStreetMap contributors". Every record carries this credit in its `attribution` field.
- Share-alike: if you publicly use a database that you derived from this data (for example a combined directory you publish or sell access to), you must offer that derived database under the ODbL as well, and keep the attribution. Producing a work from the data, such as a map image or a report, is treated differently from publishing a derived database; read the license if you are unsure.
- Keep the license and attribution fields with the data when you pass it on.

This summary is not legal advice. The license text is the authority.

### Fair use of the public Overpass server

The Actor uses the NextGIS Overpass service (listed by its operator as free with no request limits) and falls back to the public instance `overpass.private.coffee` (whose operators say it is open for commercial use with no registration and ask for advance notice of large operations such as more than ten requests per second) if NextGIS cannot answer. Each run sends at most a handful of requests, one at a time, at least five seconds apart, with a descriptive User-Agent that includes the contact address chris@jetlaunch.net. After HTTP 429, 406, 502, 503 or 504 it waits at least 30 seconds and retries a few times, then fails clearly without charging. It does not use Nominatim, which forbids bulk geocoding. It does not call the main overpass-api.de instance, because that instance asks commercial and app traffic to use self-hosted or paid servers. You can point the Actor at your own or a paid Overpass server in the advanced field.

### Privacy

Features tagged as dwellings (house, apartments, detached and similar building types), residential features, features with private access, and features whose name looks like a private individual's home (for example "Smith Residence") are excluded. Contact fields are returned only for named business features that pass these checks, exactly as tagged in OpenStreetMap. No email addresses or mapper user names are returned.

### FAQ

**Why are some businesses I know missing?** OpenStreetMap is edited by volunteers. A business may be unmapped, unnamed, or mapped with a different tag. Try extra tags, or check the `SUMMARY` record to see what was excluded.

**Why is a phone number or website empty?** Only about some of the features carry that tag. The Actor never looks anything up outside OpenStreetMap.

**Why was a business in a house excluded?** Features whose building tag marks a dwelling are excluded on purpose to protect private residences. This can also drop a few genuine businesses located in houses.

**How fresh is the data?** The `osmDataTimestamp` field shows the age of the server's copy, usually within minutes of the live map. `lastEditedAt` shows when each feature was last edited.

**Can I search a whole state or country?** No. The area limit keeps queries small enough for the public server. Use cities, counties or districts. For planet-scale needs, use an OSM extract such as Geofabrik instead.

### Limitations

- Runs usually take about 2 to 5 minutes. The free public server queues requests, and the Actor sends only one or two per run, so most of the run time is waiting.
- The public server's copy of the map is not always current. In testing, copies dated June and July 2026 were served in October 2026. Check `osmDataTimestamp` on each record.
- Coverage and tagging quality vary by region.
- Results are not ranked. When the maximum is lower than the number of matches, you get the first ones the server returns, in map-tile order.
- Public Overpass servers can be busy. If the server cannot answer, the run fails with a clear message and nothing is charged.
- Business hours and contact details are as tagged by volunteers and may be out of date.

### Support

Questions, bug reports or feature requests: use the Issues tab of this Actor.

# Actor input Schema

## `place` (type: `string`):

A city, town, county or district, optionally followed by a region or country to avoid mix-ups, for example Rochester, Minnesota or Leeds, United Kingdom. The Actor looks up the administrative boundary in OpenStreetMap. Ignored when a bounding box is given.

## `bbox` (type: `string`):

Instead of a place name, give four numbers: south, west, north, east, for example 43.95,-92.55,44.08,-92.38. The area may not exceed 16 square degrees. When set, it replaces the place name.

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

One or more presets. A business matches when it fits any selected category.

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

Additional business tags as key=value, for example shop=books or craft=brewery. Several values for one key may be joined with a pipe: shop=books|games. Allowed keys: amenity, shop, craft, office, tourism, leisure, healthcare.

## `nameFilter` (type: `string`):

Keep only businesses whose name contains this text. Not case sensitive.

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

Stop after this many businesses. You are charged per business returned.

## `dedupe` (type: `boolean`):

Drop repeated OSM features and near-duplicates (same name and category within 75 meters, such as a shop mapped as both a point and a building outline).

## `includeContactData` (type: `boolean`):

Business contact data exactly as tagged in OpenStreetMap. Residential features are always excluded.

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

Adds a tags column with every other tag of the feature as a JSON string.

## `overpassEndpoint` (type: `string`):

Leave empty to use the default public instance. To use your own or a paid Overpass server, enter its https interpreter URL. The OSM main instance overpass-api.de is not accepted because its operators ask commercial and app traffic to use self-hosted or paid servers.

## Actor input object example

```json
{
  "place": "Rochester, Minnesota",
  "categories": [
    "cafes"
  ],
  "maxResults": 25,
  "dedupe": true,
  "includeContactData": true,
  "includeRawTags": false
}
```

# Actor output Schema

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

Businesses with category, address, coordinates, tagged contact data and license metadata.

# 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 = {
    "place": "Rochester, Minnesota",
    "categories": [
        "cafes"
    ],
    "maxResults": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("jetlaunch/osm-local-business-directory").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 = {
    "place": "Rochester, Minnesota",
    "categories": ["cafes"],
    "maxResults": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("jetlaunch/osm-local-business-directory").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 '{
  "place": "Rochester, Minnesota",
  "categories": [
    "cafes"
  ],
  "maxResults": 25
}' |
apify call jetlaunch/osm-local-business-directory --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jetlaunch/osm-local-business-directory"
        }
    }
}
```

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/ElcLMRm0vJKZXEPEq/builds/S86HR51v9Zc5utqhW/openapi.json
