# Business Spreadsheet Enricher: Emails, Phones & Websites (`thescrapelab/business-spreadsheet-enricher`) Actor

Enrich CSV and Excel company lists with public business emails, phone numbers, verified websites, addresses, social profiles, and source evidence. Directly scrapes official company websites—no enrichment API or subscription required.

- **URL**: https://apify.com/thescrapelab/business-spreadsheet-enricher.md
- **Developed by:** [Inus Grobler](https://apify.com/thescrapelab) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 enriched companies

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Spreadsheet Enricher: Emails, Phones & Websites

Turn a CSV or Excel company list into an evidence-backed business contact dataset. This business data enrichment and company contact scraper finds official websites, then collects public emails, phone numbers, named contacts, addresses, company descriptions, published industries, and social profiles.

No contact database, enrichment provider, search API, subscription, or generated email pattern is used. Every collected value comes directly from a public company website and includes source URLs.

### Quick start

1. Open the **Input** tab.
2. Upload a CSV/XLSX file, or enter a few companies manually.
3. Click **Start**.
4. Download `ENRICHED.xlsx` or `ENRICHED.csv` from the **Output** tab.

For the fastest and most accurate results, include a `website` column. If you only have company names, add city and country where possible to improve matching.

### What you can do

- Complete missing fields in CRM and prospecting spreadsheets.
- Verify company websites before outreach.
- Collect public sales, support, careers, press, and staff contact routes.
- Preserve the original rows while appending enrichment columns.
- Download the completed file as XLSX or CSV.
- Review confidence scores and source evidence before importing results.

### Use cases

- **CRM cleanup:** append official domains and published contact routes before account assignment.
- **Sales operations:** enrich conference, partner, and target-account spreadsheets in bulk.
- **Lead research:** collect evidence-linked public company details without buying an enrichment API subscription.
- **Market mapping:** standardize company websites, locations, industries, and social profiles for analysis.
- **Data quality review:** separate verified, ambiguous, unavailable, and blocked rows before import.

### Input

Choose one input method:

1. Upload a `.csv` or `.xlsx` file in **Upload a CSV or Excel file**.
2. Enter a short JSON list in **Or enter companies manually**.

Each row must contain either a company name or a website. Company name, city, region, country, company number, and website columns are detected automatically. Use **Column mapping** when your spreadsheet uses custom headers.

The simplest spreadsheet looks like this:

```csv
companyName,website,city,country
Apify,https://apify.com,Prague,CZ
Mozilla,https://www.mozilla.org,,US
```

Important settings:

| Setting | What it does | Default |
| --- | --- | --- |
| **Business details to collect** | Select emails, phones, contacts, address, description, industry, and social profiles. | All details |
| **Companies to process** | Limits the number of spreadsheet rows processed. | 1 |
| **Pages per company** | Controls how many useful pages are checked on each website. | 5 |
| **Total request budget** | Prevents unexpectedly large or expensive runs. | 10 |
| **JavaScript rendering** | Optional browser fallback for JavaScript-only sites. | Off |

Example:

```json
{
  "companies": [
    {
      "companyName": "Apify",
      "website": "https://apify.com"
    }
  ]
}
```

The Actor never overwrites a non-empty input cell. It fills or appends enrichment columns and keeps rows in their original order.

### Output

Every input row produces a dataset record with:

- match status and confidence;
- verified official website and domain;
- public emails and phone numbers;
- named contacts exactly as published by the company;
- address, description, and published industry;
- LinkedIn, Facebook, Instagram, X, YouTube, TikTok, and GitHub links;
- source URLs and field-level evidence;
- warnings or errors when a site cannot be accessed safely.

The run also provides:

- `ENRICHED.xlsx` — completed Excel workbook;
- `ENRICHED.csv` — completed CSV export;
- `RUN_SUMMARY.json` — sanitized processing and quality totals.

Example dataset item:

```json
{
  "rowNumber": 2,
  "status": "enriched",
  "matchStatus": "provided_domain_accessible",
  "matchConfidence": 1,
  "inputCompanyName": "Apify",
  "officialWebsite": "https://apify.com/",
  "domain": "apify.com",
  "emails": ["support@apify.com"],
  "phones": [],
  "address": "Na Příkopě 959/27, Prague, 11000, CZ",
  "sourceUrls": ["https://apify.com/", "https://apify.com/about"],
  "warnings": [],
  "errors": []
}
```

### Result statuses

- `enriched`: verified company with one or more requested fields.
- `partial`: verified website, but limited enrichment was available.
- `needs_review`: plausible candidates existed but confidence was insufficient.
- `not_found`: no suitable official website was found.
- `blocked`: robots rules or access controls prevented collection.
- `invalid_input`: the row lacked a usable company name and website.

The Actor deliberately abstains when a match is ambiguous. This protects your spreadsheet from confident-looking but incorrect company data.

### Direct-scraping methodology

Unknown websites are discovered from public HTML search results and verified against company name and location evidence. A website supplied in the input is treated as customer-provided and checked for public accessibility, not independently proven to belong to the named company. The Actor then visits a bounded number of same-site About, Contact, Team, Legal, and Imprint pages. It respects robots rules and does not bypass CAPTCHAs, login walls, paywalls, or access controls.

Named contacts are returned only when visibly published by the company. The Actor does not infer employee details, generate email patterns, or scrape private profiles.

Placeholder addresses, monitoring ingestion tokens, escaped markup fragments, and example-form values are excluded from email output. Social fields contain profile or company-page URLs rather than individual posts, videos, embeds, repositories, or issue forms. To keep spreadsheet cells reviewable, phone output is limited to the first ten distinct published numbers per company and reports a truncation warning when more were found.

### Cost controls

Runs are economical by default: direct HTTP scraping is used, JavaScript browser rendering is off, concurrency is 2, and one row is processed for a cheap first-run sample. Raise the row and request limits together for larger lists. The 1024 MB default completed the measured 10-row workload faster and with fewer compute units than the memory-constrained 512 MB configuration.

Every run also has independent hard ceilings:

- **Total request budget** counts robots checks, discovery requests, redirects, and website pages across the whole run. The default is 10 for the one-company trial. The measured 10-company test used 43 requests, so set about 50 requests when trying a similar batch.
- **Maximum browser renders** limits the more expensive browser fallback. The default is 0; browser use requires explicitly selecting **Automatic fallback** and raising this limit.
- **Maximum processing time** stops new network work after 10 minutes by default.
- **Memory-aware concurrency** limits 512 MB runs to one company at a time and 1024 MB HTTP-only runs to four at once. This prevents transient memory spikes while allowing the default tier to finish bulk work efficiently.
- Apify's Actor timeout remains a final 15-minute platform-level backstop.

When a ceiling is reached, completed data is retained and remaining affected rows are returned as partial results with a budget warning. `RUN_SUMMARY.json` reports the requested and effective concurrency, allocated memory, configured request and browser limits, actual usage, and deadline.

On pay-per-event runs, the Actor also checks the customer's Apify spending limit before starting each new unique company. It stops cleanly when the remaining event budget is exhausted, retains already completed output, and reports `customer_charge_limit_reached` in `RUN_SUMMARY.json`. Only successfully enriched unique companies emit the `enriched-company` event; duplicates and unsuccessful rows remain free.

For the lowest and most predictable usage cost, supply known company websites, keep JavaScript rendering disabled, test a small sample first, and increase limits only when the sample output justifies it.

### Python example

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
actor = client.actor("thescrapelab/business-spreadsheet-enricher")

run = actor.call(run_input={
    "companies": [
        {
            "companyName": "Apify",
            "website": "https://apify.com",
        }
    ]
})

dataset = client.dataset(run["defaultDatasetId"])
for item in dataset.iterate_items():
    print(item["officialWebsite"], item["emails"], item["sourceUrls"])
```

### Pricing

The introductory price is **$1 per 1,000 successfully enriched unique companies** (`$0.001` each), plus Apify platform usage. There is no subscription or monthly minimum.

You are not charged the enrichment event for duplicate rows, blocked pages, invalid input, low-confidence matches, or companies where no useful public details are found. Apify's small standard Actor-start event may also apply once per run.

The low launch price is intended to make real spreadsheet trials inexpensive. Pricing may be reviewed later as reliability, coverage, and operating evidence grow; any active price change follows Apify's customer-notice rules.

### Responsible use and limitations

Use public contact information responsibly and follow applicable privacy, marketing, and communications laws. A public email address is not automatic consent to send unsolicited messages.

Website designs, robots policies, blocking controls, and published data change over time. Some JavaScript-heavy or protected sites may return partial or blocked results. Domain discovery is best effort; supplying a known website produces the most reliable and economical runs. Review `needs_review` rows instead of treating candidate domains as verified matches.

### Support

If a run produces unexpected output, open the Actor's **Issues** tab and include the run URL plus a short description of the affected row. Do not include private spreadsheet data in a public issue.

# Changelog

This Actor's version history is a separate document: https://apify.com/thescrapelab/business-spreadsheet-enricher/changelog.md

# Actor input Schema

## `inputFile` (type: `string`):

Best for spreadsheets. Upload a CSV or XLSX file up to 25 MB. Common company, website, city, region, country, and company-number columns are detected automatically.

## `companies` (type: `array`):

Best for a quick test or a short list. Each company needs a company name or website. Supplying the website is faster and more reliable.

## `sheetName` (type: `string`):

Optional. Leave blank to use the first worksheet in an XLSX file.

## `columnMapping` (type: `object`):

Optional. Use only when automatic column detection does not recognize your headers.

## `fieldsToEnrich` (type: `array`):

Choose the public details to collect from official company websites. The default collects everything available.

## `maxRows` (type: `integer`):

Maximum number of company rows to process in this run. The one-company default gives you a cheap, complete sample; increase it for larger jobs.

## `maxPagesPerCompany` (type: `integer`):

Maximum pages checked on each company website, including useful Contact, About, Team, Legal, and Imprint pages.

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

Advanced. Higher values can be faster but use more memory. The Actor may lower this automatically for stability.

## `maxRequests` (type: `integer`):

Safety limit for all website and discovery requests in the run. The 10-request default keeps the one-company trial inexpensive; raise it with the row limit for larger jobs.

## `maxBrowserRenders` (type: `integer`):

Advanced safety limit for costlier browser fallbacks. Keep 0 for HTTP-only runs. Increase it only when JavaScript rendering is set to Automatic fallback.

## `maxRunMinutes` (type: `integer`):

Stops starting new website work after this many minutes and keeps all completed results.

## `browserRendering` (type: `string`):

Keep Never use a browser for the lowest cost. Automatic fallback can render empty JavaScript-only pages and is limited by Maximum browser renders.

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

Optional. Configure Apify Proxy only when public websites block ordinary datacenter requests.

## Actor input object example

```json
{
  "companies": [
    {
      "companyName": "Apify",
      "website": "https://apify.com",
      "city": "Prague",
      "country": "CZ"
    }
  ],
  "fieldsToEnrich": [
    "emails",
    "phones",
    "namedContacts",
    "address",
    "description",
    "industry",
    "socialProfiles"
  ],
  "maxRows": 1,
  "maxPagesPerCompany": 5,
  "maxConcurrency": 2,
  "maxRequests": 10,
  "maxBrowserRenders": 0,
  "maxRunMinutes": 10,
  "browserRendering": "never"
}
```

# Actor output Schema

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

Structured enrichment results with confidence, evidence, and row-level status.

## `enrichedExcel` (type: `string`):

Download the original rows plus appended enrichment columns as XLSX.

## `enrichedCsv` (type: `string`):

Download the original rows plus appended enrichment columns as CSV.

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

Inspect sanitized counts, timing, truncation, effective concurrency, request/browser budget usage, and proposed chargeable 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 = {
    "companies": [
        {
            "companyName": "Apify",
            "website": "https://apify.com",
            "city": "Prague",
            "country": "CZ"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("thescrapelab/business-spreadsheet-enricher").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 = { "companies": [{
            "companyName": "Apify",
            "website": "https://apify.com",
            "city": "Prague",
            "country": "CZ",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("thescrapelab/business-spreadsheet-enricher").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 '{
  "companies": [
    {
      "companyName": "Apify",
      "website": "https://apify.com",
      "city": "Prague",
      "country": "CZ"
    }
  ]
}' |
apify call thescrapelab/business-spreadsheet-enricher --silent --output-dataset

```

## MCP server setup

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

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/Qs335qkffhN01CFj6/builds/ZQXDTL0klD8pm2vEb/openapi.json
