# Business Enrichment - Company Profiles and Contacts (`leadproof/business-enrichment`) Actor

Enrich business names or websites with public contact details, source-backed activity descriptions and identity confidence. Supports businesses without websites and optional email verification.

- **URL**: https://apify.com/leadproof/business-enrichment.md
- **Developed by:** [Lead Proof](https://apify.com/leadproof) (community)
- **Categories:** Lead generation, Business
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 business matcheds

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

## Business Enrichment - Company Profiles and Contacts

Turn a list of business names (or website URLs) into source-backed company records: official website, published emails and phone numbers, and a description of what the business does. Built for supplier lists and incomplete CRM records, including small businesses that have no website in your data.

Every fact in the output carries the URL it came from. Nothing is generated or guessed: if a fact is not published on a page that matches the business, the field stays empty.

### What you get

- **Identity match** with a transparent confidence level and the reasons behind it (name in page title, city, country, matching phone).
- **Official website**, only when it is confirmed: supplied in your input, or a branded domain that also shows your phone number.
- **Emails and phones** published by the business, first-party sources first. Directory publishers' and article authors' mailboxes are filtered out.
- **Business description**: an excerpt of published activity or background text, never a generated summary.
- **Optional email verification** through LeadProof Bulk Email Verifier.
- **One output row per input row**, including businesses that could not be found, so your export lines up with your source list.
- **Pay only for matches.** Unresolved rows are free. A match is an identified business, not a guaranteed email (see Pricing).

### Input

```json
{
  "businesses": [
    {"id": "supplier-1", "name": "Eon Energy", "country": "Kenya", "phone": "+254202210028"},
    {"id": "supplier-2", "name": "Kavirondo Chemist", "country": "Kenya", "city": "Homa Bay"},
    {"id": "supplier-3", "website": "bidcoafrica.com", "country": "Kenya"}
  ],
  "verifyEmails": false
}
```

Each row needs a `name` or a `website`. Name searches also need a `country`, per row or through the top-level `country` default. Optional fields: `id`, `city`, `phone`, `email`, `address`, `category`. Keep phone numbers as strings so leading zeroes survive.

- **City** narrows the search and supports the identity check. If the first search returns no readable page, the Actor retries once without the city.
- **Phone** is the strongest identity signal: a page showing your phone number next to the business name is a high-confidence match.
- **Email, address and category** are kept as context in `input`. They are never presented as discovered facts.

Instead of `businesses` you can pass `csvUrl`: a public or signed UTF-8 CSV (up to 2 MB, 100 rows) with the same column names. Up to 100 businesses per run (`maxBusinesses`). Duplicate rows are processed separately so each keeps its own `id`.

### Output

Flat columns for spreadsheets: `inputId`, `businessName`, `status`, `matchConfidence`, `websiteUrl`, `businessDescription`, `emailAddresses`, `phoneNumbers`, `warnings`. Use the **Download CSV** link on the run page for exactly these columns.

The JSON adds the evidence behind each value:

- `website`, `description`, `emails`, `phones`, `addresses`, `socialProfiles`, `productsAndServices`, `businessTypes`: each fact with `sourceUrl`, `confidence` and `method`.
- `emails[].contactScope`: `city_named_mailbox` when the mailbox is named after your row's city (for example a branch mailbox), otherwise `company_or_listing`.
- `emails[].verificationStatus` and `isDeliverable` when verification is on.
- `sources` (every page considered, with match reasons) and `candidates` (pages that did not pass the identity check, for manual review).
- `input`: your original row.

#### Statuses

| Status | Meaning | Charged |
| --- | --- | --- |
| `matched` | At least one readable page carries the business name plus the city, country or phone. | Yes, one `business-match` |
| `needs_review` | Candidate pages mention the name or phone, but none passed the identity check. | No |
| `not_found` | Search returned nothing that mentions the business. | No |
| `error` | The search dependency failed. This says nothing about the business. | No |

#### Confidence and scope

`matchConfidence` is a transparent rule, **not a calibrated probability**:

- **high**: supplied website, or page title with the name plus your phone number.
- **medium**: a distinctive name in the page title plus your city or country. Review medium matches before outreach.

A company match does not guarantee the right branch or a named person. Large companies publish head-office and other branch contacts; the Actor ranks a mailbox named after your city first and adds the warning `contacts_may_include_head_office_or_other_branches`. A country-only match can confuse two businesses with the same name. Generic names (for example "General Supplies") are never matched on location alone.

#### Verification semantics

With `verifyEmails: true`, each found email is checked. `isDeliverable` is `true` only for an `ok` verdict and `false` only for `invalid`. `catch_all`, `unknown` and provider errors stay `null`: they do not confirm deliverability. With verification off, no verification runs and every email is `not_requested`.

### Pricing

**$0.01 per matched business** (`business-match`). Rows with `needs_review`, `not_found` or `error` are free. The only other event is the standard Actor start ($0.00005).

**A match is not a guaranteed email.** `business-match` is charged when the business is identified on a public page. A matched row can have an email, a phone, a website and a description, or only some of them: many small businesses publish a phone number but no email. Check `emailAddresses` and `warnings` per row.

**Dependency runs are billed in addition to the $0.01.** This Actor calls other Actors on your account, and each bills you separately:

| Dependency | When it runs | Billed by it |
| --- | --- | --- |
| [RAG Web Browser](https://apify.com/apify/rag-web-browser) (Apify) | One search per business, a second only when the first returns no readable page | Platform usage: Google SERP proxy plus compute, typically under $0.01 per search |
| [Website Email Finder](https://apify.com/leadproof/website-email-finder) (LeadProof) | Only for a confirmed website, when search pages did not fill the email quota | Its Store price per email found |
| [Bulk Email Verifier](https://apify.com/leadproof/bulk-email-verifier) (LeadProof) | Only with `verifyEmails: true` | Its Store price per email checked |

Your maximum charge for the run applies to `business-match` events only. The Actor stops starting new searches once the remaining limit cannot cover every business in progress. Each dependency run has its own cap of $0.20. Start with a few rows to see real costs for your list.

#### Example total cost

Scaled from our pilot on a list of small Kenyan suppliers, verification off, per 100 input rows:

| Item | Billed by | Approximate cost |
| --- | --- | --- |
| About 40 matched businesses x $0.01 | This Actor | $0.41 |
| Searches, including retries | RAG Web Browser (platform usage) | $0.55 |
| About 11 emails found on websites x $0.005 | Website Email Finder | $0.06 |
| **Total** | | **about $1.02** |

With `verifyEmails: true`, add about $0.0049 per email found (about $0.16 for this list). Search usage follows your Apify plan's platform prices, and your total depends on your match rate and how many emails businesses publish.

### Coverage limits

- Only public pages returned by the search are read. There is no login and no dedicated Facebook, LinkedIn or Google Maps lookup; public pages from those sites count only when the search returns them readable. Blocked or JavaScript-only pages stay unresolved.
- Small local businesses often have no web presence at all. Expect many `not_found` rows on lists of sole traders.
- Search results vary between runs, so rerunning an unresolved row can find something new.
- Directories can be stale or mix several businesses. On directory pages, only company-branded emails or the email you supplied are accepted, which deliberately skips some Gmail or Yahoo addresses to avoid publisher contamination.
- Addresses and products/services are filled only from structured business data on the page, so they are often empty.
- No revenue, employee counts, decision makers or WhatsApp availability.

### Runtime

The run uses its own `APIFY_TOKEN` only to start the dependencies and read their results. It is never sent to scraped websites. Three businesses are processed at a time, which fits the memory limit of a free Apify account including the dependency runs. Results and dependency run IDs are checkpointed, so a restarted run resumes without repeating finished work or charges. Budget about 20-25 minutes per 100 businesses.

### Development

Python 3.13, Apify SDK 4.0.0. `python -m unittest discover -s test` covers identity rules, contact contamination, description filters, input limits, SSRF-safe URLs, verification semantics, billing and resume behavior. `network.py` and the email rules in `extract.py` are shared with Website Email Finder; keep security fixes synchronized.

***

*Built by [LeadProof](https://leadproof.co) - verified local-business lead lists, built to order.*

# Actor input Schema

## `businesses` (type: `array`):

Objects with name or website, plus optional id, country, city, phone, email, address and category. Keep phones as strings. Country is required for name-only searches. Only matched businesses are charged; a match does not guarantee an email.

## `csvUrl` (type: `string`):

Public or signed UTF-8 CSV, up to 2 MB and 100 rows. Columns: name,website,id,country,city,phone,email,address,category. Supply businesses, CSV, or both.

## `country` (type: `string`):

Country name or code used when a row has no country.

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

Check each found email with LeadProof Bulk Email Verifier, billed by that Actor per email checked. Off means no verification runs. Catch-all and unknown results do not confirm deliverability.

## `includeWebsiteEmails` (type: `boolean`):

Scan up to 3 pages of a confirmed website with LeadProof Website Email Finder, billed by that Actor per email found. Skipped when search pages already filled the email quota. Off still extracts emails from search pages.

## `maxBusinesses` (type: `integer`):

Hard limit applied to API and Console input.

## `maxSearchResults` (type: `integer`):

Hard limit applied to API and Console input.

## `maxEmailsPerBusiness` (type: `integer`):

Hard limit applied to API and Console input.

## Actor input object example

```json
{
  "businesses": [
    {
      "name": "Eon Energy",
      "country": "Kenya",
      "phone": "+254202210028"
    }
  ],
  "verifyEmails": false,
  "includeWebsiteEmails": true,
  "maxBusinesses": 20,
  "maxSearchResults": 4,
  "maxEmailsPerBusiness": 5
}
```

# Actor output Schema

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

Full JSON with evidence, sources and warnings.

## `csv` (type: `string`):

Flat contact columns for spreadsheet import.

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

Counts by status, stop reason and verification mode.

# 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 = {
    "businesses": [
        {
            "name": "Eon Energy",
            "country": "Kenya",
            "phone": "+254202210028"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("leadproof/business-enrichment").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 = { "businesses": [{
            "name": "Eon Energy",
            "country": "Kenya",
            "phone": "+254202210028",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("leadproof/business-enrichment").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 '{
  "businesses": [
    {
      "name": "Eon Energy",
      "country": "Kenya",
      "phone": "+254202210028"
    }
  ]
}' |
apify call leadproof/business-enrichment --silent --output-dataset

```

## MCP server setup

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

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/8GN8Bx8zO9dMGanIf/builds/jQs2TngbpdYQTenSp/openapi.json
