# Company Data Scraper — Domain to Firmographics & Emails (`dev_web_col/company-enrich`) Actor

B2B data enrichment from a domain: employees, industry, HQ, LinkedIn, contact emails and a live hiring signal. CRM enrichment with verified matches.

- **URL**: https://apify.com/dev\_web\_col/company-enrich.md
- **Developed by:** [Diseño Web de Colombia](https://apify.com/dev_web_col) (community)
- **Categories:** Lead generation, Automation, Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 company enricheds

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Company Enrichment API — Domain to Firmographics + Emails

**Paste a list of domains. Get back company records you can put straight into a CRM — and know which ones are wrong.**

### Call it as an API

One domain in, one company row out, in a single HTTP call. This is the fastest
path and the one most people integrate into their own product.

```bash
curl -X POST "https://api.apify.com/v2/acts/dev_web_col~company-enrich/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domain": "stripe.com"}'
```

```python
import requests

r = requests.post(
    "https://api.apify.com/v2/acts/dev_web_col~company-enrich/run-sync-get-dataset-items",
    params={"token": "YOUR_TOKEN"},
    json={"domain": "stripe.com"},
)
company = r.json()[0]
print(company["companyName"], company["employees"], company["isHiring"])
```

```javascript
const res = await fetch(
  "https://api.apify.com/v2/acts/dev_web_col~company-enrich/run-sync-get-dataset-items?token=YOUR_TOKEN",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ domain: "stripe.com" }),
  },
);
const [company] = await res.json();
```

`run-sync-get-dataset-items` waits for the run and returns the rows in the same
response, so there is nothing to poll. Your token is on the
[Integrations page](https://console.apify.com/account/integrations).

Passing `domains` instead of `domain` enriches a whole list in one call — use
that when you are processing a CRM export rather than serving a live lookup.

### What is Company Enrichment?

Company Enrichment turns a plain list of company websites into structured company records: headcount, industry, headquarters, founding year, company type, LinkedIn followers and specialties, plus contact emails and social profiles crawled from the company's own site, plus a live hiring signal.

It is the cheap alternative to Clearbit or Apollo for the one job most people actually need: taking a column of domains out of a CRM and filling in the columns next to it.

### The thing that makes this different: it verifies the match

Most enrichment tools guess the LinkedIn page from the domain — `vanta.com` becomes `linkedin.com/company/vanta`. That is wrong, and silently so. Measured on this exact input:

| Domain | A naive guess returns | This Actor returns |
|---|---|---|
| `vanta.com` | **VANTA - Chauffeurs, 4 employees** | Vanta, 1,965 employees |
| `notion.so` | **a Notion with 38 employees** | Notion, 7,458 employees |

Two independent checks decide it. The company must either **link that LinkedIn page from its own website**, or LinkedIn must **list that same domain**. Every row carries a `linkedinVerified` flag, and the `onlyVerified` option drops everything that failed.

Name matching would not save you here: "VANTA - Chauffeurs" starts with the word vanta, and the wrong Notion is called exactly "Notion". The website link is the only signal that actually separates them.

A wrong company in your CRM costs more than a missing row. This is the only field that tells you which one you got.

### What you can use it for

| What you want | How to ask for it |
|---|---|
| **CRM enrichment** — fill the empty columns next to a domain | `domains: [...]` from a CRM export |
| **Lead qualification** — is this account big enough? | Read `employees`, `employeeRange`, `industry` |
| **B2B data for scoring** — firmographics at scale | `onlyVerified: true` so nothing wrong enters the CRM |
| **Email finder** — contact addresses for a company | `findEmails: true`, read `emails` |
| **Sales triggers** — who is hiring right now | Read `isHiring`, `openJobs` and `jobBoardUrl` |
| **Territory research** — headcount and HQ by account | Read `hqLocation`, `founded`, `companyType` |
| **Live API lookup** — one domain per call from your app | `domain: "stripe.com"` via `run-sync-get-dataset-items` |
| **Investor or portfolio tracking** | Run the same domain list monthly, compare `employees` |

Typical jobs: **CRM enrichment**, **B2B data** for lead scoring, **company data**
lookups, **email finder** for outbound, and hiring-based **sales triggers**.

### What data you get

**Firmographics** — company name, employee count, size band, industry, headquarters, founded year, company type, specialties, LinkedIn followers, LinkedIn URL.

**Contact** — emails found on the company site with own-domain addresses listed first, plus Twitter/X, Facebook and Instagram profiles.

**Hiring signal** — whether the company has an open public job board right now, how many roles are on it, which ATS it runs on, and the direct link.

### Input

Every field is optional unless marked required.

| Field | Type | Description |
|---|---|---|
| `domains` | array, **required** | One company website per line. Paste them straight from your CRM — https://, www. and trailing paths are all stripped for you. |
| `findEmails` | boolean | Crawls the company website for contact emails and social profiles. Also finds the company's own LinkedIn link, which is what makes the match reliable — turning this off makes verification much weaker. |
| `onlyVerified` | boolean | Drops any company whose LinkedIn page could not be confirmed as belonging to that domain. Use it when the data goes straight into a CRM and a wrong match costs you more than a missing row. |
| `proxyConfiguration` | object | Required. LinkedIn returns HTTP 999 to datacenter IPs, Apify's included, so runs without a residential proxy come back empty. |

#### Input sample

```json
{
  "domains": [
    "stripe.com",
    "figma.com",
    "vanta.com"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

### Output sample

One real row from an actual run:

```json
{
  "domain": "stripe.com",
  "companyName": "Stripe",
  "linkedinUrl": "https://www.linkedin.com/company/stripe/",
  "linkedinVerified": true,
  "employees": 17034,
  "employeeRange": "5,001-10,000 employees",
  "industry": "Technology, Information and Internet",
  "hqLocation": "South San Francisco, California",
  "founded": "2010",
  "companyType": "Privately Held",
  "specialties": null,
  "followers": 1605462,
  "linkedinWebsite": "https://stripe.com",
  "emails": [
    "contact@stripe.com"
  ],
  "facebook": "https://www.facebook.com/StripeHQ",
  "instagram": "https://www.instagram.com/stripehq",
  "twitter": "https://twitter.com/stripe",
  "isHiring": true,
  "atsPlatform": "greenhouse",
  "jobBoardUrl": "https://boards.greenhouse.io/stripe",
  "openJobs": 531
}
```

### How often each field comes back

Measured on 20 real company domains in one run:

| Field | Hit rate |
|---|---|
| LinkedIn match **verified** | **88%** |
| At least one email | **65%** |
| An email on the company's own domain | **41%** |
| Hiring signal present | 41% |

Two things that table tells you honestly. First, **41% is the number that matters
for outbound** — a `press@` or an agency address is not the same as
`someone@thecompany.com`, and the rows list own-domain addresses first so you can
filter. Second, that run asked for 20 domains and returned **17**: the time budget
stops enrichment at about 3.5 minutes and delivers what it has. Split large lists.

### Who uses this

- **Sales and RevOps** — enrich an inbound list before it ever reaches a rep
- **Recruiters** — see which target companies are hiring, and on which board
- **Investors and analysts** — headcount and industry across a whole portfolio
- **Agencies** — qualify a prospect list without paying per seat for a data vendor
- **Anyone with a spreadsheet of domains** and no budget for Clearbit

### Honest limits

These are real and none of them is fixable by any tool. They are here so you know what you are buying before you run it.

- **A residential proxy is required.** LinkedIn answers HTTP 999 to datacenter IPs, Apify's included. The default input already asks for one.
- **Not every domain has a LinkedIn page**, and some companies never list their website there. Those rows come back with `linkedinVerified: false` and the LinkedIn fields empty or unconfirmed — by design, rather than guessing.
- **Emails come from the public website only.** No pattern-guessing, no invented `firstname.lastname@`. If the site publishes no address, the field is empty. Expect generic inboxes like info@ or press@, not named contacts.
- **The hiring index covers 1,014 validated companies**, weighted toward tech. `isHiring: false` means "not in the index", not "definitely not hiring".
- **Big lists need batching.** A run stops enriching after about 3.5 minutes and delivers what it has. Split large lists across runs.
- **LinkedIn headcount is self-reported.** It is the company's claim, not payroll truth.

### FAQ

#### What does `linkedinVerified: false` actually mean?

It means the LinkedIn page shown could not be confirmed as belonging to that domain. The data may still be right — but it may be a different company with a similar name. Treat those rows as a lead, not a fact. Set `onlyVerified: true` to drop them entirely.

#### Why do I get generic emails instead of a person's address?

Because this Actor only reports addresses that are actually published on the company site. It never guesses `name@company.com` patterns. Invented addresses bounce, damage your sending domain, and are worse than no data.

#### Does turning off "Find emails" make it faster?

Yes, but it also makes the match less reliable. The site crawl is where the company's own LinkedIn link is found, and that link is the strongest verification signal. Leave it on unless you only need a rough pass.

#### How is the hiring signal built?

From a bundled index of 1,014 companies whose public job boards on Greenhouse, Lever, Ashby and Workable were validated. It ships inside the Actor, so it costs no extra requests and adds no time to your run.

#### Can I feed it company names instead of domains?

Not today — it takes domains, because a domain is unambiguous and a company name is not. That is the same reason the verification step exists.

#### Do I need an account or an API key for the source?

No. This Actor only reads public pages and public endpoints, the same ones any
logged-out visitor sees. You never give it credentials, and nothing is done on
your behalf.

#### Is it legal to scrape this data?

Scraping publicly available information is broadly accepted as legal, and this
Actor only ever touches pages that require no login. That said, what you may do
with the data afterwards is a separate question — personal data in particular is
regulated by GDPR and CCPA. If you plan to process personal data, take advice
first. Apify has a good primer: [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

#### How much will a run cost me?

You are charged per result, so the cost scales with what you actually get back.
The pricing is shown on this page above the input form. Start with a small run to
see real numbers before you scale up.

#### Can I export to Excel, CSV or Google Sheets?

Yes. Every run's dataset can be downloaded as CSV, Excel, JSON, XML or HTML from
the **Storage** tab, and Apify integrates directly with Google Sheets, Airtable,
Zapier, Make and Slack.

#### Can I run this on a schedule?

Yes. Open the **Schedules** tab and pick an interval — hourly, daily, weekly.
Each run only costs you what it returns.

#### Can I call it from my own code instead of the console?

Yes. Every Actor on Apify is also an API endpoint. There are official clients for
[Python](https://docs.apify.com/api/client/python/) and
[JavaScript](https://docs.apify.com/api/client/js/), plus a plain REST API. The
**API** tab on this page shows ready-made snippets with your input already filled in.

#### Something came back empty. Is it broken?

Usually not. An empty result normally means the filters were too narrow, or the
source genuinely has nothing to return for that query. This Actor is written to
finish successfully and tell you what happened rather than fail — check the run
log, it says which step returned nothing.

### Related Actors

- **ATS Job Scraper** — search live jobs across the same company index
- **LinkedIn Jobs Scraper + Direct Apply Links** — jobs with the original apply link
- **Local Business Email & Contact Scraper** — the same email crawling, but starting from a city and a category instead of a domain list. Use that one when you do not know the companies yet.

### Where the data comes from

Public LinkedIn company pages as a logged-out visitor sees them, and the company's own website. No login, no cookies, no personal data about individuals — company records only.

# Actor input Schema

## `domain` (type: `string`):

One domain, for when you call this Actor as an API from your own code — the fastest path, one call in, one row out. For a list, use the field below instead.

## `domains` (type: `array`):

A list of company websites, one per line, for batch runs. Paste them straight from your CRM — https://, www. and trailing paths are all stripped for you.

## `findEmails` (type: `boolean`):

Crawls the company website for contact emails and social profiles. Also finds the company's own LinkedIn link, which is what makes the match reliable — turning this off makes verification much weaker.

## `onlyVerified` (type: `boolean`):

Drops any company whose LinkedIn page could not be confirmed as belonging to that domain. Use it when the data goes straight into a CRM and a wrong match costs you more than a missing row.

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

Required. LinkedIn returns HTTP 999 to datacenter IPs, Apify's included, so runs without a residential proxy come back empty.

## Actor input object example

```json
{
  "domain": "stripe.com",
  "findEmails": true,
  "onlyVerified": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `companies` (type: `string`):

One dataset item per domain.

# 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 = {
    "domain": "stripe.com",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dev_web_col/company-enrich").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 = {
    "domain": "stripe.com",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("dev_web_col/company-enrich").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "domain": "stripe.com",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call dev_web_col/company-enrich --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=dev_web_col/company-enrich",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/HULv6MTmQKDENN2tH/builds/LcEzHWZY1bbheHGQC/openapi.json
