# Business Contact Finder: Owners, Emails & Phones (`varnmill/business-contact-finder`) Actor

Turn business websites or a Google Maps search into decision-maker leads: owner and staff names with titles, MX-verified emails, phones, social profiles, founding year and more. No login, no cookies, public web only.

- **URL**: https://apify.com/varnmill/business-contact-finder.md
- **Developed by:** [Jay Novello](https://apify.com/varnmill) (community)
- **Categories:** Lead generation, Business, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 leads

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Business Contact Finder: Owners, Emails & Phones

Owner and staff names with job titles, MX-verified email addresses, phone numbers and social profiles for any business, taken from its own public website or from a Google Maps search, with no login, no cookies and no purchased database.

**Try it:** Click Start with the default input: 3 businesses enriched in 39 s for about $0.009 (run on October 21, 2026). The $0.50 maximum charge shown on the run is a spending cap, not a fee.

Default input: three roofing-company websites. Real rows from that run:

| Business | Owner | Verified email | Phone | People found |
|---|---|---|---|---|
| Steadfast Roofing | Jon Starry | info@steadfastroofingfl.com | (813) 851-2276 | Jon Starry (Owner & CEO), Eldora Starry (Production Manager), 12 more |
| SCM Roofing | Scott Marone | scheduling@scmroofingfl.com | (855) 726-7663 | Scott Marone (Owner & Founder) |
| Rainier Roofing | Sameer Asmar | info@rainierbuilders.com | (813) 452-8248 | Sameer Asmar (President of Rainier Roofing), Joe Aponte (General Manager) |

Each row also carries every email with its type and MX status, all phones, socials (Facebook, Instagram, LinkedIn, YouTube and more), founding year, city and state, and the pages crawled. Paste your own website list, a Google Maps search such as `plumbers in Austin, TX`, or the dataset ID of a Google Maps Scraper run.

**Pricing:** $2.00 per 1,000 businesses enriched (primary event) plus $1.00 per 1,000 MX-verified emails; a typical lead with two emails costs $0.004. Unreachable sites, sites that disallow crawling, unverified emails and failed runs are not charged.

![Real output from a Google Maps search for roofing contractors in Tampa, FL](https://raw.githubusercontent.com/Jammer1333/apify-actor-assets/main/business-contact-finder/sample.png)

**Who it is for:** agencies and freelancers building local outreach lists, recruiters, sales teams prospecting small and mid-size businesses, and anyone who already has a list of companies and needs the decision maker and a working email.

### More real examples

**Google Maps search** (October 2026). Input: `roofing contractors in Tampa, FL`, 10 places. Result: 9 businesses enriched, 5 owners identified by name, total run cost $0.004. Every row below came straight out of the dataset.

| Business | Owner | Verified email | People found |
|---|---|---|---|
| Steadfast Roofing | Jon Starry | info@steadfastroofingfl.com | Jon Starry (Owner & CEO), Eldora Starry (Production Manager), Sara Adams (Office Assistant), 2 more |
| SCM Roofing, LLC | Scott Marone | scheduling@scmroofingfl.com | Scott Marone (Owner & Founder) |
| Rainier Roofing LLC | Sameer Asmar | info@rainierbuilders.com | Sameer Asmar (President), Joe Aponte (General Manager) |
| Red Fox Roofers of Tampa | Camilo Calle | | Camilo Calle (co-owner) |
| Tampa Roof Repair LLC | Erik Emery | | Erik Emery (Owner & Operator) |

A second test on a mixed list of 12 plumbers, accountants and service businesses reached 11 sites, found 23 verified emails, and matched personal emails to 6 named partners at one accounting firm.

![People view: one row per person with title and matched email](https://raw.githubusercontent.com/Jammer1333/apify-actor-assets/main/business-contact-finder/people.png)

### What you get for every business

| Field | Example |
|---|---|
| `businessName` | Steadfast Roofing |
| `ownerName` | Jon Starry |
| `people[]` | `{ "name": "Eldora Starry", "title": "Production Manager", "email": null }` |
| `emails[]` | `{ "email": "info@steadfastroofingfl.com", "type": "generic", "onDomain": true, "domainStatus": "mx", "verified": true }` |
| `phones[]` | `(813) 726-4590` |
| `socials` | facebook, linkedin, instagram, x, youtube, tiktok, yelp, pinterest |
| `foundedYear` | 2012 |
| `city`, `state`, `postalCode`, `addresses[]` | Tampa, FL, 33602 |
| `siteBuilder`, `https`, `pagesCrawled`, `status` | wordpress, true, 6 pages, ok |

When the input comes from Google Maps you also keep `placeId`, `address`, `categories`, `rating`, `reviewsCount`, `mapsPhone` and `mapsUrl`, so the output works as a complete lead record on its own.

Three dataset views are built in: **Overview** (one row per business), **Emails** (one row per address) and **People** (one row per person). Export any of them as CSV, Excel or JSON.

### Three ways to feed it

1. **Google Maps search** such as `plumbers in Austin, TX`. The Actor runs the Google Maps Scraper (`compass/crawler-google-places`) for you, billed to your account at that Actor's own rate (about $4 per 1,000 places on the free tier, less on paid plans), and enriches every result that has a website.
2. **Websites**: paste URLs or bare domains, one per line.
3. **A dataset**: the ID of any run whose items have a `website`, `websiteUri`, `url` or `domain` field. Original fields are kept, so you can enrich a Google Maps Scraper run you already paid for.

### How the enrichment works

- Fetches the homepage and up to *N* contact, about, team and leadership pages (default 6) with per-host throttling.
- Honours `robots.txt` by default and never logs in anywhere.
- Finds emails in links, text and common obfuscations (`name [at] domain [dot] com`), drops placeholders and tracker junk, then checks the domain's MX record. Emails whose domain has an MX record are marked `verified: true`; addresses on domains with only an A record are kept but flagged unverified and never charged. Domains with no DNS at all are dropped.
- Extracts people from schema.org markup, team cards and `Name, Title` lines, and matches personal emails to names (`jsmith@`, `john.smith@`).
- Picks the owner from titles such as Owner, President, Founder, Principal and Managing Partner.

### Pricing

Pay per event. You are charged only for value delivered:

| Event | Price | When |
|---|---|---|
| Business enriched | $2.00 per 1,000 | One charge per website that was reached and crawled |
| Verified email | $1.00 per 1,000 | One charge per email whose domain has an MX record (`verified: true`). Unverified addresses are free |

Unreachable sites, sites that disallow crawling, and businesses with no email (when you turn on *Only output businesses with at least one email*) are written to the dataset for transparency and are **not charged**. Platform usage is included. Set a maximum total charge on the run to cap spend.

### Accuracy and limits

Measured on the test runs above, October 2026:

- 20 of 22 input websites were reached (the two misses were a site returning 429 to all crawlers and a JavaScript-only redirect page).
- An owner name was found for 5 of 9 roofing contractors and 3 of 11 mixed service businesses. Owner detection depends on the site naming its owner somewhere; many very small sites do not.
- Emails are verified at the domain level only (the domain publishes an MX record). Mailbox-level verification is not performed, so a small share of addresses on valid domains may still bounce. Only MX-verified emails are charged.
- People extraction is heuristic. Expect an occasional non-person on unusual page layouts; every person carries a `source_url` so you can check in one click.
- Datacenter proxies are the default and work for most business websites. Switch the proxy configuration to residential for sites behind aggressive bot protection.

### Tips

- Turn on *Only output businesses with at least one email* when you only want mailable leads.
- Raise *Max pages per website* to 10 or more for firms with large team directories (law firms, medical groups, brokerages).
- Chain it: run the Google Maps Scraper with your own filters, then pass its dataset ID here.
- Use the **People** view and export to CSV for a ready-to-import outreach list.

### FAQ

**Does it use Apollo, LinkedIn or any contact database?** No. Every field comes from the business's own public website or from the Google Maps result you asked for.

**Will it get me banned or blocked?** It reads public pages the way a browser does, throttles per host and respects robots.txt. Nothing is posted and no account is used.

**Why is an email marked `generic`?** Addresses such as info@, office@ or sales@ are flagged generic; named addresses are `personal`. Both are included so you can choose.

**Can I enrich my own list of 5,000 domains?** Yes. Paste them as websites, or upload them to a dataset and pass its ID. Raise *Max businesses to enrich* accordingly.

**Where do I report a problem?** Use the **Issues** tab on this page. Problems are usually answered the same day.

### Legal

This Actor reads publicly available web pages, respects `robots.txt` and stores nothing beyond your run's dataset. You are responsible for complying with applicable laws (CAN-SPAM, GDPR, CCPA and similar) when contacting the people it finds.

### Changelog

- **0.1 (October 2026)**: first public release. Google Maps, website and dataset inputs; MX email verification; people and owner extraction; three dataset views.

# Actor input Schema

## `googleMapsQueries` (type: `array`):

Search phrases exactly as you would type them into Google Maps, e.g. "plumbers in Austin, TX". Each search is resolved with the Google Maps Scraper (compass/crawler-google-places), billed to your account at its own rate, and every business with a website is then enriched. Leave empty if you supply websites or a dataset instead. The default input uses websites only, so clicking Start does not call the Google Maps Scraper.

## `maxPlacesPerQuery` (type: `integer`):

Upper limit of Google Maps results fetched for each search phrase.

## `websites` (type: `array`):

Website URLs or bare domains to enrich directly, one per line. Use this when you already have a list of companies.

## `inputDatasetId` (type: `string`):

ID of an existing dataset (for example the output of a Google Maps Scraper run). Every item that has a website field (website, websiteUri, url or domain) is enriched and the original fields are kept.

## `maxPagesPerSite` (type: `integer`):

Homepage plus the most relevant contact, about and team pages. More pages find more people but cost more time.

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

Skip sites and paths that disallow crawling in robots.txt.

## `verifyEmails` (type: `boolean`):

Drop emails whose domain cannot receive mail (no MX or A record). Recommended.

## `onlyBusinessesWithEmail` (type: `boolean`):

Skip businesses where no email was found. You are not charged for skipped businesses.

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

Stop after this many businesses have been processed across all inputs.

## `concurrency` (type: `integer`):

How many websites to crawl at the same time.

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

Proxies to use for website requests. Datacenter proxies are enough for most business websites.

## Actor input object example

```json
{
  "maxPlacesPerQuery": 10,
  "websites": [
    "https://steadfastroofingfl.com",
    "https://scmroofingfl.com",
    "https://rainierbuilders.com"
  ],
  "maxPagesPerSite": 6,
  "respectRobotsTxt": true,
  "verifyEmails": true,
  "onlyBusinessesWithEmail": false,
  "maxResults": 10,
  "concurrency": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `contacts` (type: `string`):

All enriched businesses as JSON. Append ?format=csv for a spreadsheet, or ?view=overview for the summary columns.

## `contactsCsv` (type: `string`):

The same results as a CSV download.

# 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 = {
    "maxPlacesPerQuery": 10,
    "websites": [
        "https://steadfastroofingfl.com",
        "https://scmroofingfl.com",
        "https://rainierbuilders.com"
    ],
    "maxResults": 10,
    "concurrency": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("varnmill/business-contact-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 = {
    "maxPlacesPerQuery": 10,
    "websites": [
        "https://steadfastroofingfl.com",
        "https://scmroofingfl.com",
        "https://rainierbuilders.com",
    ],
    "maxResults": 10,
    "concurrency": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("varnmill/business-contact-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 '{
  "maxPlacesPerQuery": 10,
  "websites": [
    "https://steadfastroofingfl.com",
    "https://scmroofingfl.com",
    "https://rainierbuilders.com"
  ],
  "maxResults": 10,
  "concurrency": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call varnmill/business-contact-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,varnmill/business-contact-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/WXgrdcfS5c4vCN1Lj/builds/VZwKpOSS3zPsxgjCi/openapi.json
