# Local Business Lead Finder & Enricher (`otdjones/businessfinder`) Actor

Find and enrich local business leads with contact details, websites, ratings, reviews, lead scoring and opportunity signals. Get CRM-ready B2B prospect data for sales, outreach and market research.

- **URL**: https://apify.com/otdjones/businessfinder.md
- **Developed by:** [Oliver Jones](https://apify.com/otdjones) (community)
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

## Businessfinder — qualified local business opportunities

Businessfinder finds local businesses that have a specific, observable problem your service can solve. It combines Google Maps discovery with conservative website inspection, public contact extraction, email-domain checks, transparent lead scoring, and CRM-ready output.

Instead of returning a raw directory, every qualified row answers three questions:

1. **Can I contact this business?** Public email, phone, UK-mobile classification, source URL, and MX status.
2. **Why might it need my service?** Website, local SEO, or reputation evidence gathered from public pages and Maps data.
3. **What should I say?** A concise opportunity summary and suggested pitch angle.

### Best fit

The first version is designed for UK web-design, local SEO, and reputation-management agencies that prospect local businesses. It can also produce general B2B prospect lists.

### What it does

1. Searches up to 20 business types in one town, city, county, or country.
2. Places a hard limit on each Maps search to control upstream cost.
3. Deduplicates results and removes closed or below-threshold businesses.
4. Checks a small number of public, same-domain website pages without executing JavaScript.
5. Finds public business emails, phones, UK mobiles, and social profiles.
6. Checks whether an email domain publishes a usable MX record. This is a domain check, not a guarantee that a particular mailbox exists.
7. Detects evidence such as no website, no HTTPS, missing mobile viewport, no contact form, no booking CTA, weak page metadata, outdated copyright, low review count, or a rating below 4.0.
8. Returns qualified leads only by default and explains every score.
9. Optionally remembers delivered businesses so a scheduled task returns only new leads.
10. Produces a JSON summary and readable Markdown run report.

Businessfinder delegates Maps discovery to Apify's maintained `compass/crawler-google-places` Actor. Its paid contact add-on is off by default because Businessfinder performs its own public website refinement.

### Opportunity presets

- `website_redesign` — finds visible website and conversion problems.
- `local_seo` — finds weak page metadata, low review volume, and other local visibility opportunities.
- `reputation` — prioritises low ratings and low review counts.
- `general_sales` — prioritises contactability for broader B2B prospecting.

### Input example

```json
{
    "searchTerms": ["roofers", "landscape gardeners"],
    "location": "Manchester, United Kingdom",
    "maxResultsPerSearch": 25,
    "servicePreset": "website_redesign",
    "minimumLeadScore": 45,
    "contactRequirement": "email_or_phone",
    "onlyNewBusinesses": true,
    "enrichment": {
        "useMapsContactAddon": false,
        "crawlBusinessWebsite": true,
        "verifyEmailDomains": true,
        "maxWebsitePages": 4,
        "includeMobileNumbers": true
    },
    "outreach": {
        "mode": "off"
    }
}
```

Set `includeUnqualified` to `true` only when tuning a preset or score threshold. Normal customer runs should leave it off.

### Output

The default table and CSV contain flat fields including:

- business name, category, address, city, postcode, website, Maps URL, rating, and reviews;
- best email, email source URL, MX status, phone, UK mobile, and social profiles;
- lead score, opportunity score, primary issue, full opportunity summary, suggested pitch angle, and evidence labels;
- outreach status.

Nested `business`, `contacts`, `website_signals`, `opportunity`, `qualification`, `outreach`, and `provenance` objects preserve the complete audit trail. `RUN_SUMMARY` contains machine-readable metrics and `RUN_REPORT` contains a readable quality report.

### Repeat discovery

Enable `onlyNewBusinesses` when saving an Apify task or schedule. Businessfinder stores up to 50,000 delivered business keys per search scope and omits them on later runs with the same location, search terms, and opportunity preset.

### Pay per qualified lead

Qualified dataset rows are emitted under the Apify pay-per-event name `qualified-lead`; diagnostic rows use `unqualified-candidate`, which must remain free. Before publishing paid pricing, benchmark several categories and configure those events in the Actor's Monetization settings. Do not set a price until the real upstream and compute cost per 1,000 qualified leads has been measured.

### Outreach modes

- `off` — lead discovery and qualification only.
- `prepare` — adds a personalised draft to eligible rows but sends nothing.
- `send` — a restricted single-mailbox proof of concept using Gmail OAuth.

Draft templates support `{{business_name}}`, `{{business_category}}`, `{{business_city}}`, `{{opportunity_summary}}`, `{{pitch_angle}}`, `{{sender_name}}`, `{{sender_postal_address}}`, and `{{unsubscribe_url}}`.

Sending is deliberately not the main public product. A customer-facing sender needs per-user Google/Microsoft OAuth, encrypted token storage, tenant isolation, bounce handling, suppression, unsubscribe processing, and delivery monitoring. See [the multi-customer design](docs/OUTREACH_SERVICE.md).

### Local development

```bash
npm ci
npm run check
npm start
```

Local Actor runs require an Apify token because discovery calls another Actor.

### Security and data limits

Website refinement accepts only HTTP(S), blocks credentials, local hostnames, private/reserved network addresses, non-standard ports, oversized pages, non-HTML content, and long redirect chains. It visits no more than eight same-domain pages and does not execute page JavaScript.

Public contact information can be stale. An MX result confirms only that a domain advertises mail handling, not that the specific address will accept mail. UK-mobile classification is structural and does not prove ownership. Operators remain responsible for UK GDPR, PECR, suppression, opt-out, and their own outreach decisions; see [the privacy and outreach checklist](docs/PRIVACY_AND_OUTREACH.md).

Businessfinder is not affiliated with Google, Apify, or Gmail. Upstream Actor usage is billed separately under Apify's live listing.

# Actor input Schema

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

Use distinct business types, for example accountants and commercial cleaners.

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

A town, city, county, or country. Keep the business type out of this field.

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

A hard cost and runtime limit for every search term.

## `minimumRating` (type: `number`):

Optional post-search filter from 0 to 5.

## `minimumReviews` (type: `integer`):

Exclude businesses with fewer Google reviews than this number.

## `servicePreset` (type: `string`):

Select the problem you sell a solution for. Businessfinder will explain the evidence behind every opportunity.

## `minimumLeadScore` (type: `integer`):

Only leads meeting this combined contactability and opportunity score are returned by default.

## `contactRequirement` (type: `string`):

Choose the minimum contact information a qualified lead must contain.

## `onlyNewBusinesses` (type: `boolean`):

Remember qualified businesses for this search and omit them from later scheduled runs.

## `includeUnqualified` (type: `boolean`):

Diagnostic option. Leave off for a clean list containing only qualified opportunities.

## `enrichment` (type: `object`):

Only public business contact pages are read. Personal employee lead enrichment is not requested.

## `outreach` (type: `object`):

Off by default. Prepare creates drafts in the dataset. Send also requires mailbox secrets and an explicit lawful-basis confirmation.

## Actor input object example

```json
{
  "searchTerms": [
    "independent garden centre",
    "landscape gardener"
  ],
  "location": "Bristol, United Kingdom",
  "maxResultsPerSearch": 25,
  "minimumRating": 0,
  "minimumReviews": 0,
  "servicePreset": "website_redesign",
  "minimumLeadScore": 45,
  "contactRequirement": "email_or_phone",
  "onlyNewBusinesses": false,
  "includeUnqualified": false,
  "enrichment": {
    "useMapsContactAddon": false,
    "crawlBusinessWebsite": true,
    "verifyEmailDomains": true,
    "maxWebsitePages": 4,
    "includeMobileNumbers": true
  },
  "outreach": {
    "mode": "off"
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

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

No description

## `runReport` (type: `string`):

No description

# 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": [
        "independent garden centre",
        "landscape gardener"
    ],
    "location": "Bristol, United Kingdom"
};

// Run the Actor and wait for it to finish
const run = await client.actor("otdjones/businessfinder").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": [
        "independent garden centre",
        "landscape gardener",
    ],
    "location": "Bristol, United Kingdom",
}

# Run the Actor and wait for it to finish
run = client.actor("otdjones/businessfinder").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": [
    "independent garden centre",
    "landscape gardener"
  ],
  "location": "Bristol, United Kingdom"
}' |
apify call otdjones/businessfinder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,otdjones/businessfinder"
        }
    }
}
```

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/nJnxfpI3r38GTBzg7/builds/D7qekAHQeMX3VR9uk/openapi.json
