# Local Business Finder (`captain-crawl/local-business-finder`) Actor

- **URL**: https://apify.com/captain-crawl/local-business-finder.md
- **Developed by:** [captain crawl](https://apify.com/captain-crawl) (community)
- **Security:** Limited permissions
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $5.00 / 1,000 results

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

Find local businesses by **type and city** and get their contact details in one table.

Type what you are looking for, for example `clothing shops` in `Los Angeles` or `dentists` in `Chicago`. The actor searches **Google Maps** and returns each business with address, phone, website, rating, review count and opening hours. It then visits each business website and adds the **emails, phone numbers and social media links** found there.

When Google Maps returns nothing or blocks the request, the actor switches to **OpenStreetMap** automatically, so a run still ends with data.

### What you get

One row per business:

| Field | Example |
|---|---|
| `name` | Burning Torch |
| `category`, `categories` | Clothing store |
| `address`, `street`, `city`, `state`, `postcode`, `country` | 1227 Abbot Kinney Blvd, Venice, CA 90291 |
| `latitude`, `longitude` | 33.9914634, -118.468582 |
| `phone`, `phones` | +1 310-399-1920 |
| `website` | https://www.burningtorchinc.com/ |
| `emails` | hello@burningtorchinc.com |
| `socialLinks` | instagram, facebook, twitter, linkedin, youtube, tiktok, pinterest, whatsapp |
| `rating`, `reviewCount`, `priceRange` | 4.6, 58 (Google Maps rows only) |
| `openingHours` | Monday: 11 AM–7 PM; Tuesday: 11 AM–7 PM … |
| `source` | `Google Maps` or `OpenStreetMap` |
| `websiteStatus` | `ok`, `failed`, `none` (no website listed) or `not_checked` |
| `websiteTitle`, `websiteDescription` | Title and meta description of the home page |
| `googleMapsUrl`, `placeId`, `osmUrl` | Links and ids of the listing |
| `searchTerm`, `location` | The search this row belongs to |

### Input

| Field | Description |
|---|---|
| **Business types** | One or more terms: `clothing shops`, `restaurants`, `marketing agencies`, `plumbers` … |
| **Locations** | One or more cities, districts or regions. Every type is searched in every location. |
| **Max results per search** | Limit per type and location pair. |
| **Data source** | Automatic (Google Maps, then OpenStreetMap), Google Maps only, or OpenStreetMap only. |
| **Language** | Language code for Google Maps results. |
| **Collect contacts from websites** | Turn off for a faster run with listing data only. |
| **Pages per website** | Home page plus contact / about pages to check (1 to 5). |
| **Only businesses with a website** | Skip listings that have no website. |
| **Proxy** | Optional. Residential proxies give the best results on Google Maps. |

You can also write a full phrase such as `dentists in Chicago` as the business type and leave Locations empty.

```json
{
    "searchTerms": ["clothing shops", "shoe stores"],
    "locations": ["Los Angeles", "San Diego"],
    "maxResultsPerSearch": 100
}
```

### How it works

1. The location is geocoded to its centre and boundaries.
2. Google Maps is searched with a real browser and each listing is opened for its details.
3. If Google Maps gives no results, the same search runs on OpenStreetMap.
4. Each business website is opened and its home, contact and about pages are scanned for emails, phone numbers and social links.

The run always finishes with a dataset. A business whose website cannot be reached is still returned with its listing data and `websiteStatus: failed`. If a search matches nothing on either source, the dataset contains one row with `status: no_results` and a hint on what to change.

### Good to know

- Google Maps limits automated traffic. Without a proxy, large or repeated runs may get fewer Google results; those searches are then answered from OpenStreetMap. The `source` field shows where each row came from.
- OpenStreetMap rows have no rating or review count. Shops, restaurants, hotels and health services are covered well there; office-based businesses such as agencies are covered less completely.
- Not every listing has a phone number or a website.
- Phone numbers and emails of small businesses can be personal data. Follow the rules that apply to you (GDPR, CCPA, CAN-SPAM) when you use them.

### Credits and licences

- Google Maps search uses the open-source [google-maps-scraper](https://github.com/gosom/google-maps-scraper) engine by Georgios Komninos, MIT License, Copyright (c) 2023 Georgios Komninos.
- OpenStreetMap listings © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors, available under the Open Database License (ODbL). If you publish that data, credit OpenStreetMap.

# Actor input Schema

## `searchTerms` (type: `array`):

What to look for, e.g. "clothing shops", "dentists", "marketing agencies". You can also write "clothing shops in Los Angeles" and leave Locations empty.

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

Cities, districts or regions, e.g. "Los Angeles" or "Camden, London". Every business type is searched in every location.

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

Upper limit of businesses returned for each business type and location pair. The most complete listings come first.

## `dataSource` (type: `string`):

Automatic searches Google Maps first and uses OpenStreetMap when Google returns nothing or blocks the request.

## `language` (type: `string`):

Language code for Google Maps results, e.g. en, de, es, fr.

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

Visits each business website to collect emails, phone numbers and social media links.

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

Home page plus contact / about pages checked on each website.

## `onlyWithWebsite` (type: `boolean`):

Skips businesses that have no website listed.

## `maxConcurrency` (type: `integer`):

Websites visited in parallel.

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

Optional. Used for Google Maps and for visiting business websites. Residential proxies give the best results on Google Maps. If the proxy cannot be set up the run continues without it.

## Actor input object example

```json
{
  "searchTerms": [
    "clothing shops"
  ],
  "locations": [
    "Los Angeles"
  ],
  "maxResultsPerSearch": 50,
  "dataSource": "auto",
  "language": "en",
  "enrichWebsites": true,
  "maxPagesPerWebsite": 3,
  "onlyWithWebsite": false,
  "maxConcurrency": 10
}
```

# Actor output Schema

## `businesses` (type: `string`):

One row per business with contact details.

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

Searches performed, where the results came from and how many websites were checked.

# 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 = {
    "searchTerms": [
        "clothing shops"
    ],
    "locations": [
        "Los Angeles"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("captain-crawl/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 = {
    "searchTerms": ["clothing shops"],
    "locations": ["Los Angeles"],
}

# Run the Actor and wait for it to finish
run = client.actor("captain-crawl/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 '{
  "searchTerms": [
    "clothing shops"
  ],
  "locations": [
    "Los Angeles"
  ]
}' |
apify call captain-crawl/local-business-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,captain-crawl/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/rB8a6B1Z2oUcqvrmc/builds/7yVc5rsElQwBpwOdd/openapi.json
