# Local Business Finder: OpenStreetMap places, phones, websites (`djk12/osm-local-business-finder`) Actor

Find restaurants, dentists, plumbers, hotels and 70 other business types in any city from OpenStreetMap. Address, phone, website, opening hours, coordinates. A cheap Google Maps alternative. No login, no keys.

- **URL**: https://apify.com/djk12/osm-local-business-finder.md
- **Developed by:** [Dylan Knaup](https://apify.com/djk12) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.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?

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

## Local Business Finder: OpenStreetMap places, phones and websites

Find businesses and places in any city, from **OpenStreetMap**: restaurants, dentists, plumbers, hotels, gyms, lawyers and 70 more types. You get name, address, phone, website, opening hours, coordinates and a completeness score. No login, no API key, no proxies, no browser.

A low-cost alternative when you need local business lists and do not need reviews or star ratings.

### Why use this Actor

- **Open data, clean rights.** Results come from OpenStreetMap through its public Nominatim and Overpass interfaces. No scraping of protected pages. Data (c) OpenStreetMap contributors, ODbL 1.0: keep the credit (every row carries it).
- **Best listings first.** Each place gets a `completeness` score (name, address, phone, website, hours). With a result limit you receive the most complete listings, not random ones.
- **Filters that save money.** `requirePhone` and `requireWebsite` are applied on the server, so you only pay for places you can use.
- **Pay only for results.** Pay per event: a tiny start fee plus a price per place in your dataset.
- **Private by design.** Only business listing fields are returned. E-mail addresses, mobile numbers and the names of OpenStreetMap editors are never read.

### Honest limits

OpenStreetMap is community-maintained. In big cities of the US and Europe many shops, restaurants and clinics have address and website; phone numbers and opening hours are less common. Smaller regions have fewer listings. There are no reviews, ratings, photos or e-mail addresses. Check the `completeness` field and run a small test for your city and category first.

### How to use it

1. **What to find**: business types, one per line: `dentist`, `restaurant`, `plumber`, `hotel`, `gym`, `hairdresser`, `lawyer`, `car repair`, `pharmacy`, `supermarket` and more. Any OpenStreetMap tag also works: `shop=bakery`, `craft=*`.
2. **Where**: cities or districts written like a map search, for example `Boulder, Colorado` or `Leeds, UK`. Or give latitude, longitude and a radius.
3. Optionally filter (phone, website, name) and set limits. Click **Start**.

Each category is searched in each place. Use cities or smaller areas: very large areas (a whole country) can be refused by the free OpenStreetMap servers, and the Actor then reports the search as failed in `RUN_SUMMARY` without charging for it.

#### Input example

```json
{
    "categories": ["dentist", "plumber"],
    "locations": ["Boulder, Colorado", "Denver, Colorado"],
    "requirePhone": true,
    "maxResultsPerSearch": 50
}
```

#### Categories

restaurant, cafe, bar, pub, fast food, bakery, butcher, supermarket, convenience store, florist, dentist, doctor, clinic, hospital, pharmacy, veterinarian, optician, physiotherapist, hotel, hostel, motel, guest house, campsite, gym, yoga studio, spa, hairdresser, barber, beauty salon, tattoo, car repair, car dealer, car wash, gas station, ev charging station, plumber, electrician, carpenter, roofer, painter, locksmith, lawyer, accountant, real estate agent, insurance, architect, bank, atm, school, kindergarten, library, laundry, clothes, shoes, jewelry, bookstore, electronics, furniture, hardware store, pet shop, bicycle shop, mobile phone shop, cinema, museum, parking, post office.

### Output

One row per place:

```json
{
    "id": "node/123456",
    "name": "Example Dental",
    "category": "dentist",
    "address": "Pearl Street 1000, 80302 Boulder, CO",
    "latitude": 40.0176,
    "longitude": -105.2797,
    "phone": "+1 303 555 0100",
    "website": "https://example.com",
    "openingHours": "Mo-Fr 08:00-17:00",
    "completeness": 1.0,
    "osmUrl": "https://www.openstreetmap.org/node/123456",
    "attribution": "OpenStreetMap contributors, ODbL",
    "fetchedAt": "2026-11-02T12:00:00Z"
}
```

(Values above are illustrative; real rows are in the Output tab of a run.) A `RUN_SUMMARY` record lists every search, the area it resolved to, how many places were found and returned, and any failure.

### Pricing

Pay per event: a very small fee for starting the run and a price per place that lands in your dataset. Searches that fail or find nothing are free. Set a **maximum cost per run** to cap spending: the Actor stops when it is reached. See the **Pricing** tab for current prices.

### Use it from code

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("<YOUR_USERNAME>/osm-local-business-finder").call(run_input={
    "categories": ["dentist"], "locations": ["Boulder, Colorado"], "requirePhone": True,
})
for place in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(place["name"], place["phone"], place["website"])
```

### FAQ

**Is it a Google Maps scraper?** No. It does not touch Google. It reads OpenStreetMap, which is open data.

**Why is a run slow sometimes?** The free public OpenStreetMap servers are shared. A search usually takes 10 to 60 seconds and the Actor switches servers and retries when one is busy.

**A city was not found or the wrong place was used.** Add the state or country (`Paris, Texas`). The area actually used is shown in `searchLocation`.

**Can I get e-mail addresses?** No, by design.

**Is it legal?** The data is licensed under ODbL: use it freely, credit OpenStreetMap contributors, and share-alike applies to derived databases. You are responsible for your use, including marketing and privacy rules in your country.

### Support

Wrong results for a city or a missing category? Open an issue in the **Issues** tab with the city and category.

# Changelog

This Actor's version history is a separate document: https://apify.com/djk12/osm-local-business-finder/changelog.md

# Actor input Schema

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

Business types, one per entry, for example <code>restaurant</code>, <code>dentist</code>, <code>plumber</code>, <code>hotel</code>, <code>gym</code>, <code>hairdresser</code>, <code>lawyer</code>, <code>car repair</code>. You can also use an OpenStreetMap tag such as <code>shop=bakery</code> or <code>craft=\*</code>.

## `locations` (type: `array`):

Cities, districts or regions, written the way you would search a map: <code>Boulder, Colorado</code>, <code>Berlin</code>, <code>Leeds, UK</code>. Use cities or smaller areas; a whole country is too large for the free OpenStreetMap servers.

## `latitude` (type: `number`):

Search around a point instead of, or in addition to, the places above. Needs longitude too.

## `longitude` (type: `number`):

See latitude.

## `radiusKm` (type: `number`):

Used with latitude and longitude.

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

Skip listings that have no phone number in OpenStreetMap.

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

Skip listings that have no website in OpenStreetMap.

## `nameContains` (type: `array`):

Keep only places whose name contains one of these words (not case sensitive), for example a brand like <code>Starbucks</code>.

## `maxResultsPerSearch` (type: `integer`):

The most complete listings (name, address, phone, website, hours) come first.

## `maxResultsTotal` (type: `integer`):

Hard stop for the whole run. Also set a maximum cost per run in the run options if you want a cost cap.

## Actor input object example

```json
{
  "categories": [
    "dentist"
  ],
  "locations": [
    "Boulder, Colorado"
  ],
  "radiusKm": 5,
  "requirePhone": false,
  "requireWebsite": false,
  "maxResultsPerSearch": 20,
  "maxResultsTotal": 20
}
```

# Actor output Schema

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

One row per business or place.

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

Per search: which area was used, how many places were found and returned, and any failure.

# 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 = {
    "categories": [
        "dentist"
    ],
    "locations": [
        "Boulder, Colorado"
    ],
    "maxResultsPerSearch": 20,
    "maxResultsTotal": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("djk12/osm-local-business-finder").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 = {
    "categories": ["dentist"],
    "locations": ["Boulder, Colorado"],
    "maxResultsPerSearch": 20,
    "maxResultsTotal": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("djk12/osm-local-business-finder").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 '{
  "categories": [
    "dentist"
  ],
  "locations": [
    "Boulder, Colorado"
  ],
  "maxResultsPerSearch": 20,
  "maxResultsTotal": 20
}' |
apify call djk12/osm-local-business-finder --silent --output-dataset

```

## MCP server setup

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

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/M1aIMa6ZdasyXX6na/builds/xtXloxT5vPUemX5xt/openapi.json
