# Local Business Leads Scraper - Phones, Emails, Websites (`offgrid-labs/local-business-leads`) Actor

Local business leads by category and city: business leads from Overture Maps open data (88 million places, 2,000+ categories) - a Google Maps scraper alternative with no scraping. Name, address, phone, website, socials; optional email and tech-stack enrichment. $0.50 per 1,000 leads.

- **URL**: https://apify.com/offgrid-labs/local-business-leads.md
- **Developed by:** [OFFGRID LABS](https://apify.com/offgrid-labs) (community)
- **Categories:** Lead generation, Business, Travel
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 business leads

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: businesses by category and city, with phones, websites and emails

**TL;DR:** Local Business Leads Scraper returns businesses of any type in any city or radius - dentists in Austin, plumbers in Manchester, restaurants in Lyon - with **name, category, full address, coordinates, phone, website, email and social links** in one CSV-friendly row. Data comes from **[Overture Maps](https://overturemaps.org) open data (88 million places, 2,000+ categories, monthly releases)**, so there is no Google Maps scraping, no browser and no proxy. Optional **website enrichment** visits each business's own site and adds business emails, social profiles, CMS/tech stack, SEO basics and a list of website gaps for outreach. You pay **$0.50 per 1,000 leads** and **$1.00 per 1,000 enriched websites**.

Built for lead-generation agencies, local SEO and web-design agencies, B2B sales teams, franchise and market researchers, and AI agents that need a "find businesses of type X in place Y" tool.

### What data you get

One dataset row per business. Fields the source does not have are `null`, never guessed.

| Field | What it is | Example |
|---|---|---|
| `name`, `brand` | Business name, chain/brand if any | `Smile 360` |
| `category`, `categories` | Primary category and hierarchy (Overture taxonomy) | `dental clinic`; `health care > outpatient care facility > dental clinic` |
| `address`, `city`, `postcode`, `region`, `country`, `fullAddress` | Postal address, split and single-line | `1509 S Lamar Blvd #675, Austin, TX 78704, US` |
| `latitude`, `longitude`, `distanceKm` | Coordinates; distance from your centre point (radius searches) | `30.25198`, `-97.763924`, `2.14` |
| `phone`, `phones` | Main phone in E.164 where the country is known, plus all numbers | `+1512...` |
| `website`, `websites` | Website(s) listed for the place | `http://www.smile360atx.com/` |
| `email`, `emails` | Role/business mailboxes only (listing; plus website when enrichment is on) - personal addresses are never returned | `info@smile360atx.com` |
| `facebook`, `instagram`, `linkedin`, `twitter`, `youtube`, `tiktok` | Social profile URLs | `https://www.instagram.com/smile360atx` |
| `operatingStatus`, `confidence` | Open/closed and Overture's 0-1 existence confidence | `open`, `0.92` |
| `overtureId` | Stable place ID (GERS) for dedupe across runs and CRM sync | `a7537c99-172a-...` |
| `searchLocation`, `searchCategory` | Which of your inputs found it | `Austin, TX`, `dentist` |

**With website enrichment** (`enrichWebsites: true`), each lead whose site can be read also gets:

| Field | What it is |
|---|---|
| `emails`, `phones`, social fields | Extra contacts found on the homepage and contact/about pages (robots.txt respected) |
| `cms`, `ecommercePlatform`, `technologies`, `hosting` | Website builder / CMS (WordPress, Wix, Squarespace...), shop platform, detected technologies |
| `analytics`, `adPixels`, `marketingTools`, `bookingPlatform` | Google Analytics, Meta Pixel, HubSpot, live chat, Calendly / Zocdoc / OpenTable... |
| `websiteTitle`, `websiteDescription`, `hasStructuredData`, `hasMobileViewport` | SEO basics |
| `hasHttps`, `missingSecurityHeaders`, `websiteResponseMs` | Security and speed basics |
| `websiteGaps` | Outreach-ready list: `No HTTPS`, `No meta description`, `No analytics`, `No online booking widget`, `Not mobile-friendly`... |
| `enrichedAt`, `websiteStatus` | When it was analysed, or why not (`HTTP 403`, `disallowed by robots.txt`, `no website listed`) |

### Use cases

- **Lead generation agencies**: build prospect lists for any niche and city in seconds and export CSV to your CRM or email tool.
- **Web design and local SEO agencies**: `requireWebsite` + enrichment shows which businesses have no HTTPS, no analytics, no booking widget or no meta description (`websiteGaps`) - the pitch is in the data.
- **B2B sales (software for dentists, salons, restaurants, contractors)**: territory lists with phone, website and the tools they already use (`technologies`, `bookingPlatform`).
- **Market and location research**: count competitors per area, map chains (`brand`), find white spots by radius.
- **AI agents**: one tool call answers "list 50 HVAC companies within 10 km of Denver with a website".

### How to scrape local business leads

1. **Locations**: one per line - a city or area (`Austin, TX`, `Camden, London`), a centre point `lat,lon`, or a bounding box `minLon,minLat,maxLon,maxLat`.
2. **Business categories**: plain words such as `dentist`, `plumber`, `restaurant`, `lawyer`, `hair salon`, `roofing`, `real estate agent`. Leave empty for every business in the area.
3. Optional: **Radius (km)** around the location centre, **Name contains**, **Only leads with a phone / website**, **Minimum place confidence**.
4. Optional: turn on **Enrich from the business website** for emails, socials, tech stack and website gaps.
5. Set **Max leads per run** and click **Start**. Download CSV, Excel or JSON, or read the dataset via API.

### Input example

```json
{
  "locations": ["Austin, TX", "Denver, CO"],
  "categories": ["dentist", "orthodontist"],
  "radiusKm": 15,
  "maxResults": 500,
  "requireWebsite": true,
  "enrichWebsites": true,
  "maxPagesPerWebsite": 2
}
```

### Output example

A real row from our test run (phone shortened; some enrichment fields omitted for length):

```json
{
  "name": "Smile 360",
  "category": "dental clinic",
  "categories": [
    "health care",
    "outpatient care facility",
    "dental clinic"
  ],
  "brand": null,
  "address": "1509 S Lamar Blvd #675",
  "city": "Austin",
  "postcode": "78704",
  "region": "TX",
  "country": "US",
  "fullAddress": "1509 S Lamar Blvd #675, Austin, TX 78704, US",
  "latitude": 30.25198,
  "longitude": -97.763924,
  "distanceKm": 2.14,
  "phone": "+1512xxxxxxx",
  "website": "http://www.smile360atx.com/",
  "email": "info@smile360atx.com",
  "facebook": "https://www.facebook.com/473581829321616",
  "instagram": "https://www.instagram.com/smile360atx",
  "operatingStatus": "open",
  "confidence": 0.92,
  "overtureId": "a7537c99-172a-45ef-9b19-95aed739fb9b",
  "searchLocation": "Austin, TX",
  "searchCategory": "dentist",
  "enrichedAt": "2026-11-03T08:09:43.086Z",
  "websiteStatus": "HTTP 200",
  "websiteResponseMs": 238,
  "cms": null,
  "analytics": [],
  "adPixels": [],
  "bookingPlatform": null,
  "technologies": [
    "Nginx",
    "Adobe Fonts",
    "core-js"
  ],
  "websiteTitle": "Smile 360: Dentist Austin, TX - Zilker",
  "hasHttps": true,
  "missingSecurityHeaders": [
    "CSP",
    "Referrer-Policy"
  ],
  "websiteGaps": [
    "No analytics",
    "No ad/retargeting pixel",
    "No online booking widget",
    "No chat, forms or marketing automation tool",
    "No email address on site"
  ],
  "overtureRelease": "2026-10-21.0",
  "scrapedAt": "2026-11-03T08:09:43.431Z"
}
```

A per-location summary (leads, enriched sites, places scanned, seconds, retries, charged events) is saved in the key-value store record `RUN_SUMMARY`. The dataset has three views: **Leads**, **Website enrichment** and **All fields**.

### How much does it cost to scrape local business leads?

Pay per event. Filters (category, radius, phone/website required, confidence) run before saving, so you only pay for leads you keep. Compute is included; the price is the same on every Apify plan.

| Event | Charged when | Price |
|---|---|---|
| `lead` | One business saved to the dataset | **$0.50 per 1,000** ($0.0005 each) |
| `website-enrichment` | Opt-in; the lead's website was read and analysed | **$1.00 per 1,000** ($0.001 each) |
| `apify-actor-start` | Once per run (Apify's standard start fee) | $0.00005 |

**Worked examples**

- **1,000 leads** without enrichment = 1,000 × $0.0005 = **$0.50**.
- **1,000 leads with website enrichment** where 700 sites are readable = $0.50 + 700 × $0.001 = **$1.20**. Sites that are down, blocked by robots.txt or missing are not charged.
- Every restaurant inside the Chicago city box (12,064 leads in our test) = **$6.03**.

### Speed

Measured on our test runs (2026-11-03): 10 dentists within 3 km of Austin with website enrichment in **12 s**; **12,064** restaurants inside the Chicago city box in **29 s** without enrichment. The actor reads only the parts of the Overture GeoParquet files that overlap your area (HTTP range requests on the official AWS Open Data bucket, retried with backoff), so run time grows with the number of places in your area, not with the size of the world dataset. Enrichment adds about 1-3 s per website, 10 sites in parallel.

### How it compares

| | This actor | Scraping Google Maps | Raw Overture / OSM files | Lead-data subscriptions |
|---|---|---|---|---|
| Source | Overture Maps open data (Meta, Microsoft, Foursquare, AllThePlaces and more) | Google's consumer site | Same open data | Mixed, often undisclosed |
| Terms | Open licenses (CDLA-Permissive-2.0, Apache-2.0, CC0) | Against Google's terms of service | Open licenses | Vendor contract |
| Setup | Type a city and a category | Proxies, browsers, retries | DuckDB/Spark + GIS skills | Sales call |
| Speed | Seconds per city | Minutes per search, rate-limited | Fast once set up | Instant |
| Website emails, tech stack, gaps | Optional add-on in the same run | Usually separate tools | No | Sometimes |
| Ratings and reviews | No | Yes | No | Sometimes |
| Cost | $0.50 per 1,000 leads | Varies | Free + your time | Subscription |

### FAQ

#### How do I get a list of businesses in a city by category?

Put the city in **Locations** and the business type in **Business categories** (for example `Austin, TX` + `plumber`), then run. Each row is one business with address, phone, website and socials; export as CSV.

#### Is this a Google Maps scraper alternative?

Yes, for business listings: it covers the same core fields (name, category, address, coordinates, phone, website) for 88 million places worldwide, from open data instead of Google. It does not return Google ratings or reviews.

#### Where does the data come from, and is it legal to use?

From the official [Overture Maps Foundation](https://overturemaps.org) monthly release on AWS Open Data, located through Overture's STAC catalog. The places theme combines data from Meta and Microsoft (CDLA Permissive 2.0), Foursquare (Apache 2.0), AllThePlaces (CC0) and others; all allow commercial reuse with attribution ("© Overture Maps Foundation and contributors"). City names are geocoded with OpenStreetMap Nominatim (one request per location, within its usage policy). Website enrichment fetches only the business's own public pages and respects robots.txt.

#### How do I find emails for local businesses?

Turn on **Enrich from the business website**. The actor reads the homepage and up to N contact/about pages and returns business mailboxes (info@, office@, bookings@ or addresses named after the business). Personal-looking addresses (firstname.lastname@...) are never returned - they are only counted in `personalEmailsHidden`. Business contact data is still subject to GDPR, CAN-SPAM and PECR when you use it.

#### Which countries and categories are covered?

Worldwide - Overture has places in every country, densest in North America and Europe. Categories follow the Overture taxonomy (2,000+ types). Plain words work (`dentist`, `hvac`, `lawyer`, `gym`, `real estate agent`); common synonyms are mapped for you.

#### How fresh is the data?

Overture publishes a new release every month; the actor auto-detects the latest one (field `overtureRelease`). Pin a release with **Overture release** for reproducible exports.

#### Why does a business have no phone or website?

Not every place has them in the open data. Use **Only leads with a phone** / **Only leads with a website** to skip those (skipped places are not charged), or raise **Minimum place confidence**.

#### How do I avoid duplicates across runs?

Rows are de-duplicated within a run (same name + phone/address). Across runs, use `overtureId`, which is stable between Overture releases.

#### Can an AI agent use this local business API?

Yes. Through the Apify MCP server an agent can find it with `search-actors`, read the input schema and call it, for example "50 roofing companies within 20 km of Tampa with a website, enriched". See **Integrations**.

#### Something wrong?

Open an issue on the Issues tab; we aim to respond within 24 hours.

### Integrations

- **API**: `POST https://api.apify.com/v2/acts/offgrid-labs~local-business-leads/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>` with the input JSON returns the leads directly (runs under 5 minutes; otherwise start a run and read its dataset).
- **Python / JavaScript**: `pip install apify-client` / `npm install apify-client`, then `client.actor("offgrid-labs/local-business-leads").call(run_input={...})`.
- **No-code**: Apify integrations for **Make**, **Zapier** and **n8n**, Google Sheets, CRM webhooks, Slack, and Schedules (for example a weekly list for your territory).
- **MCP / AI agents**: add `https://mcp.apify.com?tools=offgrid-labs/local-business-leads` to your MCP client.

### Changelog

- **2026-11-03** · Policy: the role/business-mailbox filter now also applies to emails from the Overture listing itself. Removed the personal-email opt-in. Only role/business mailboxes are ever returned; personal-looking addresses are counted in `personalEmailsHidden` only.
- **2026-11-03** · First release: Overture Maps places (latest release auto-detected), city / coordinate / bounding-box / radius search, 2,000+ categories with synonyms, phone / website / confidence filters, dedupe, optional website enrichment (business emails, socials, tech stack, SEO and security basics, website gaps), `RUN_SUMMARY`.

### Related actors

| Actor | What it does |
|---|---|
| Contact Details Scraper | Emails, phones and social links from a list of websites you already have |
| Tech Stack Detector | Full technology profile (CMS, ecommerce, analytics, pixels, CDN) of any website |
| US Building Permits Scraper | New construction projects and contractors from 12 US cities |

Attribution: places data © Overture Maps Foundation and contributors (Meta, Microsoft, Foursquare, AllThePlaces and others; licenses listed at docs.overturemaps.org/attribution); geocoding © OpenStreetMap contributors (ODbL).

***

```
// OFFGRID LABS ─────────────────────────────────────────────
// status: monitored 24/7 by a self-hosted agent swarm
// health checks: every 6h · auto-repair + rollback on failure
// sources: official public APIs and open data only
// support: open an issue — human-reviewed response within 24h
// ───────────────────────────────────────────────────────────
```

# Actor input Schema

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

One search area per line. Accepts a place name ("Austin, TX", "Camden, London", "Lyon, France" - geocoded with OpenStreetMap Nominatim), a centre point "lat,lon" ("30.2672,-97.7431", searched with radiusKm, default 2 km) or a bounding box "minLon,minLat,maxLon,maxLat". Max 50 per run. Example: \["Austin, TX", "Denver, CO"]

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

What kind of businesses to return, in plain words: dentist, plumber, restaurant, lawyer, hair salon, roofing, real estate agent, gym, hvac, accountant... Matched against the Overture category taxonomy (2,000+ categories, synonyms built in). Empty = every business in the area. Example: \["dentist", "orthodontist"]

## `radiusKm` (type: `integer`):

0 = use the whole city/area boundary box returned by the geocoder. Above 0 = only businesses within this many kilometres of the location's centre (adds distanceKm to every row). Coordinates without a radius use 2 km. Example: 5

## `nameKeywords` (type: `array`):

Keep only businesses whose name contains one of these words (case-insensitive). Example: \["family", "smile"]

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

Stop after this many leads (one lead = one dataset row = one charged lead event). 0 = no limit (hard cap 100,000). Example: 500

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

Skip (and do not charge for) businesses without a published phone number.

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

Skip (and do not charge for) businesses without a website. Turn on together with website enrichment for agency prospect lists.

## `minConfidence` (type: `number`):

Overture's existence-confidence score per place. 0.5 (default) drops most stale or duplicate listings; 0.8 = only well-corroborated places; 0 = everything. Example: 0.7

## `includeClosed` (type: `boolean`):

By default only places with operating status "open" are returned.

## `enrichWebsites` (type: `boolean`):

Visit each lead's own website (robots.txt respected) and add business emails, extra phones, Facebook/Instagram/LinkedIn/X/YouTube/TikTok links, CMS and tech stack, analytics and ad pixels, booking widget, SEO title/description, HTTPS and security headers, and a list of website gaps. Charged per lead only when the site could be read.

## `maxPagesPerWebsite` (type: `integer`):

Homepage plus up to N-1 contact/about pages per website (more pages = more emails found, slower). Default 2. Example: 3

## `respectRobotsTxt` (type: `boolean`):

Skip websites whose robots.txt disallows the page (not charged). Recommended: keep on.

## `overtureRelease` (type: `string`):

Leave empty to use the latest monthly Overture Maps release (auto-detected). Pin a release for reproducible exports. Example: 2026-10-21.0

## Actor input object example

```json
{
  "locations": [
    "Austin, TX"
  ],
  "categories": [
    "dentist"
  ],
  "radiusKm": 3,
  "nameKeywords": [],
  "maxResults": 10,
  "requirePhone": false,
  "requireWebsite": false,
  "minConfidence": 0.5,
  "includeClosed": false,
  "enrichWebsites": true,
  "maxPagesPerWebsite": 2,
  "respectRobotsTxt": true,
  "overtureRelease": ""
}
```

# Actor output Schema

## `leads` (type: `string`):

One row per business: name, category, address, phone, website, email, socials, confidence.

## `enrichment` (type: `string`):

One row per business with tech stack, SEO/security basics and website gaps (enrichWebsites runs).

## `allFields` (type: `string`):

Every field of every lead.

## `runSummary` (type: `string`):

RUN_SUMMARY record: leads saved and websites enriched per location, Overture release, seconds, retries, charged events.

# 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 = {
    "locations": [
        "Austin, TX"
    ],
    "categories": [
        "dentist"
    ],
    "radiusKm": 3,
    "maxResults": 10,
    "enrichWebsites": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("offgrid-labs/local-business-leads").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 = {
    "locations": ["Austin, TX"],
    "categories": ["dentist"],
    "radiusKm": 3,
    "maxResults": 10,
    "enrichWebsites": True,
}

# Run the Actor and wait for it to finish
run = client.actor("offgrid-labs/local-business-leads").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 '{
  "locations": [
    "Austin, TX"
  ],
  "categories": [
    "dentist"
  ],
  "radiusKm": 3,
  "maxResults": 10,
  "enrichWebsites": true
}' |
apify call offgrid-labs/local-business-leads --silent --output-dataset

```

## MCP server setup

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

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/yoqZpKHpbJ08zLYrT/builds/svmHmZwhmIDE9gHAI/openapi.json
