# Local Business Leads Scraper - Phone, Website & Address (`antishock/local-business-leads-scraper`) Actor

Extract local business leads in any city worldwide: name, phone, website, street address, postcode, opening hours and coordinates. 34 categories from restaurants and dentists to law offices and car repair. Filter to businesses that have contact details. No API key, no proxy.

- **URL**: https://apify.com/antishock/local-business-leads-scraper.md
- **Developed by:** [Ryan Zinburg](https://apify.com/antishock) (community)
- **Categories:** Lead generation, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 result exporteds

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 Leads Scraper - Phone, Website & Address by City

Build a **local business lead list for any city in the world**: restaurants, dentists, hotels, hairdressers, law offices, car repair shops and 28 more categories. Every record carries the address, and where the data exists also the phone number, website, email and opening hours.

Type a city name, pick a category, get a table you can call from. No API key, no proxy, no anti-bot blocking, and no per-request Google pricing.

Data comes from **OpenStreetMap**, which matters for two reasons: it is licensed for commercial use (see the licence note below), and in Europe its coverage of small businesses is often better than commercial map directories. Coverage of contact details is dense in Germany, Austria, Switzerland, France and the Netherlands and thinner in parts of the US and Asia. Turn on `requireContact` to see immediately what a given city actually yields.

### What you get per business

| Field | Example |
|---|---|
| `name` | Zahnärztliche Gemeinschaftspraxis Dr. Macke |
| `category` | dentist |
| `phone` | +49 721 686969 |
| `website` | http://www.karlsruhe-zahnarzt.de/ |
| `email` | praxis@example.de |
| `street`, `houseNumber`, `postcode`, `city`, `country` | Kaiserstraße 12, 76133 Karlsruhe, DE |
| `openingHours` | Mo-Fr 08:00-18:00; Sa 09:00-12:00 |
| `brand`, `operator` | chain and operating company |
| `cuisine` | italian, german, sushi |
| `wheelchair` | yes, limited, no |
| `latitude`, `longitude` | 49.0383618, 8.4368371 |
| `osmUrl` | link to the OpenStreetMap record |

#### With contact enrichment enabled

| Field | Example |
|---|---|
| `emails` | \["praxis@karlsruhe-zahnarzt.de"] |
| `emailSource` | `openstreetmap` or `website` |
| `emailDomainMatch` | whether the first email sits on the company's own domain |
| `phonesFromWebsite` | \["+49 721 686969"] |
| `socialLinks` | facebook, instagram, linkedin, twitter, youtube |
| `contactPageUrl` | the imprint or contact page the address came from |
| `enrichment` | ok, no-email-found, unreachable, no-website, failed |

### Search filters

- **location** - any place name, e.g. `Karlsruhe, Germany`, `Austin, Texas`, `Milan`. Resolved automatically to its bounding box
- **boundingBox** - alternatively give exact coordinates as `south,west,north,east`
- **category** - one of 34 business types: restaurant, cafe, bar, fast\_food, hotel, dentist, doctor, clinic, pharmacy, veterinary, hairdresser, beauty, bakery, butcher, supermarket, florist, optician, clothes, furniture, electronics, hardware, car\_repair, car\_dealer, fuel, bank, gym, lawyer, accountant, insurance, estate\_agent, company\_office, craft, school, kindergarten
- **osmFilter** - any other OpenStreetMap tag, e.g. `shop=bicycle`, `office=architect`, `amenity=nightclub`
- **requireContact** - keep only businesses that have a phone, website or email
- **enrichContacts** - visit each business website and read the imprint or contact page for email addresses, phone numbers and social profiles
- **requireEmail** - keep only records where an email was actually found
- **maxSitesToEnrich** - upper bound on how many websites are opened
- **proxyConfiguration** - proxy used when visiting business websites
- **maxResults** - up to 10 000 businesses per run

### Why the enrichment matters

The OpenStreetMap record almost always has the phone number and the website, but rarely an email address. Most outreach needs the email.

With `enrichContacts` enabled the actor opens each business website, follows the link to the imprint, contact or about page and reads the address from there. In Germany, Austria and Switzerland this works particularly well, because an Impressum with contact details is legally required on every commercial site.

The result is one run that produces the business list **and** the contact details, instead of buying a list somewhere and enriching it in a second tool.

### Example input

```json
{
  "location": "Karlsruhe, Germany",
  "category": "dentist",
  "requireContact": true,
  "enrichContacts": true,
  "requireEmail": true,
  "maxSitesToEnrich": 200,
  "maxResults": 500
}
```

### Use cases

- **Local lead generation** - build outreach lists of businesses in a city and category, with phone and website
- **Agency prospecting** - find businesses in a niche that have no website yet, a classic web design pitch
- **Market and competitor mapping** - count and locate every competitor in a catchment area
- **Franchise and site selection** - measure density of a category per district before opening
- **Delivery and field sales routing** - export coordinates and opening hours for route planning
- **Data enrichment** - add coordinates, opening hours and category to an existing business list

### Attribution and licence

OpenStreetMap data is published under the **Open Database License (ODbL)**. You may use it commercially, including for lead generation. If you publish or redistribute the data or a derived database, you must credit "© OpenStreetMap contributors" and keep the derived database under the same licence. Internal use such as sales outreach has no attribution requirement.

### Notes

- Ways and relations (buildings, large sites) are returned with their centre coordinates.
- Records without a name are skipped, since they are not usable as leads.
- The data comes from Overpass, community-run infrastructure that is sometimes overloaded. The actor rotates through four independent servers and only fails once every one of them is unavailable.
- Enrichment opens up to three pages per business and runs five sites in parallel, so a run with 200 websites takes a few minutes. `enrichment` tells you per record what happened, so a missing email is never silent.
- Company websites often link to chambers, associations and agencies in the footer. Addresses on the company's own domain are sorted first and flagged with `emailDomainMatch`, so a generic association address never ends up first in your outreach list.
- Email addresses found on a company website are business contact data. Using them for cold outreach is regulated in the EU and elsewhere; check what applies to you before sending.

# Actor input Schema

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

City, region or country, e.g. Karlsruhe, Germany or Austin, Texas.

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

Type of business to extract.

## `osmFilter` (type: `string`):

Overrides the category. Any OpenStreetMap tag, e.g. shop=bicycle or office=architect.

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

Alternative to location: south,west,north,east in decimal degrees.

## `requireContact` (type: `boolean`):

Keep only records that have a phone number, website or email address.

## `enrichContacts` (type: `boolean`):

Visit each business website and read the imprint or contact page to collect email addresses, phone numbers and social profiles. Slower, but this is what turns the list into an outreach list.

## `requireEmail` (type: `boolean`):

Keep only records where an email address was found. Needs the enrichment above.

## `maxSitesToEnrich` (type: `integer`):

Upper bound on how many business websites are opened during enrichment.

## `proxyConfiguration` (type: `object`):

Proxy used when visiting business websites. Residential proxies reach more sites.

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

How many businesses to save.

## Actor input object example

```json
{
  "location": "Karlsruhe, Germany",
  "category": "dentist",
  "requireContact": true,
  "enrichContacts": true,
  "requireEmail": false,
  "maxSitesToEnrich": 30,
  "proxyConfiguration": {},
  "maxResults": 200
}
```

# Actor output Schema

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

Scraped records in the default dataset.

# 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": "Karlsruhe, Germany",
    "category": "dentist",
    "requireContact": true,
    "enrichContacts": true,
    "maxSitesToEnrich": 30,
    "maxResults": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("antishock/local-business-leads-scraper").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": "Karlsruhe, Germany",
    "category": "dentist",
    "requireContact": True,
    "enrichContacts": True,
    "maxSitesToEnrich": 30,
    "maxResults": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("antishock/local-business-leads-scraper").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": "Karlsruhe, Germany",
  "category": "dentist",
  "requireContact": true,
  "enrichContacts": true,
  "maxSitesToEnrich": 30,
  "maxResults": 200
}' |
apify call antishock/local-business-leads-scraper --silent --output-dataset

```

## MCP server setup

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

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/hlN3gz9FG9i0kO6Vh/builds/jxvh8kGsOkpAALBFO/openapi.json
