# Company Enrichment API – Domain to Tech & Contacts (`renzomacar/company-enrichment`) Actor

Turn a domain into a full company profile: tech stack, NAICS, size, socials & decision-maker emails with confidence scores and live MX checks — read from the company's live site. $20/1k results, no subscription, no API key. A Clearbit, Apollo, Clay & ZoomInfo alternative.

- **URL**: https://apify.com/renzomacar/company-enrichment.md
- **Developed by:** [Renzo Madueno](https://apify.com/renzomacar) (community)
- **Categories:** Lead generation, Business
- **Stats:** 71 total users, 11 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $18.60 / 1,000 enriched companies

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

## Company Enrichment API — Turn a Domain Into Firmographics, Tech Stack & Verified Emails

**What it does:** give it a company domain (e.g. `stripe.com`) and it returns a full structured profile — industry + NAICS/SIC, employee band, revenue band, categorized tech stack, LinkedIn/Crunchbase, role emails, and decision-makers — read live from the company's own website.
**Who it's for:** SDRs, RevOps, founders, and data teams who need fresh B2B company data without buying a Clearbit, Apollo, Clay, or ZoomInfo seat.
**What it costs:** **$0.02 per enriched company ($20 per 1,000)** plus $0.01 per run. No subscription, no API key, no seats — and records below your confidence threshold are **not charged**.

### What company enrichment returns here

Company enrichment means taking an identifier you already have — a domain — and filling in everything else about that company. One domain in, one structured record out, grouped like this:

| Group | Fields you get back |
|---|---|
| **Identity** | `companyName`, `description`, `logo`, `foundedYear`, `companyType`, `country`, `address` |
| **Firmographics** | `industry`, `naicsCode`, `sicCode`, `employeesEstimate` (band + source), `revenueBand` |
| **Technographics** | `technographics` (categorized: Frontend, Analytics, Payments, CRM, Hosting…) and the flat `techStack` |
| **Contacts** | `emails[]` with type (role vs personal), `emailPattern`, `phones`, `decisionMakers[]` with name, title and email |
| **Deliverability** | `domainHasMx`, `mxProvider`, per-email `confidence`, `emailInferred` |
| **Social** | `linkedinUrl`, `crunchbaseUrl`, plus `socialLinks` (Twitter/X, Facebook, Instagram, YouTube) |
| **Quality** | `confidenceScore` (0–1) on the record, a confidence on every email and decision-maker, and `pagesCrawled` |

Anything not published on the site comes back `null` — never a guess. See the full example record below.

### 🎥 Video walkthrough

https://youtu.be/qm5vUED0p1o

### Why live enrichment beats a static database

Database vendors sell you records they collected months ago; B2B contact data decays at roughly 2% per month as people change jobs and companies pivot. This Actor doesn't have a database to go stale — every run crawls the company's live site (homepage, about, team, leadership, contact pages) and extracts what is published **right now**, with:

- a **0–1 confidence score** on every email, every decision-maker, and the record overall;
- a **live DNS MX check** on the domain (`domainHasMx`, `mxProvider` — e.g. "Google Workspace") so you know the mailbox infrastructure exists before you send;
- an honest `emailInferred` flag — a pattern email (`first.last@`) is only constructed when **two or more real emails confirm the pattern**, and it is always labeled as inferred;
- **null instead of a guess** when a signal isn't on the site.

### Company Enrichment vs Clearbit vs Apollo vs Clay vs ZoomInfo

An honest comparison — these are strong platforms; the difference is the model you pay for and where the data comes from:

| | **This Actor** | **Clearbit (HubSpot Breeze)** | **Apollo** | **Clay** | **ZoomInfo** |
|---|---|---|---|---|---|
| Data source | Live crawl of the company's site | Static database + ML | Static database | Aggregates other providers' credits | Static database |
| Pricing model | **Pay per result: $20/1k** | Credit packs via HubSpot | Per-seat plans (from ~$49/user/mo) | Monthly credit plans (from ~$134/mo) | Annual contract, typically five figures |
| Subscription required | **No** | Yes | Yes (beyond free tier) | Yes | Yes |
| Seat licenses | **None** | Per HubSpot seat | Per user | Per workspace | Per user |
| Confidence score per field | **Yes, 0–1** | Partial | Partial | Depends on source | No |
| Live MX verification | **Yes** | Partial | Partial | Via credits | Partial |
| Pay $0 for weak records | **Yes (`minConfidence`)** | No — credits burn either way | No | No — credits burn either way | No |
| Minimum commitment | **$0.03 (one domain)** | Credit pack | Monthly plan | Monthly plan | Annual contract |

*(Competitor pricing is list pricing at the time of writing; check each vendor for current terms.)*

**When to still use them:** if you need a 200M-contact search engine to *discover* companies you don't know yet, a database product fits. If you already have the domains and need them **enriched accurately today**, that's this Actor.

### How to enrich a list of company domains

1. Paste **domains** (or full URLs), one per line.
2. Optionally tune `maxPagesPerDomain` (crawl depth, default 5) and `minConfidence` (the bar below which you are not charged, default 0.3).
3. Run — export JSON, CSV, or Excel, or push to your stack via Apify integrations.

```json
{ "domains": ["stripe.com", "notion.so"], "maxPagesPerDomain": 5, "minConfidence": 0.5 }
```

Real output shape (one row per domain):

```json
{
  "domain": "stripe.com",
  "companyName": "Stripe",
  "industry": "Financial Services & Fintech",
  "naicsCode": "522320",
  "employeesEstimate": { "band": "1001-5000", "source": "site-copy" },
  "technographics": { "Frontend": ["Next.js"], "Payments": ["Stripe"] },
  "linkedinUrl": "https://www.linkedin.com/company/stripe/",
  "emailPattern": "first.last",
  "emails": [{ "email": "press@stripe.com", "type": "role", "onDomain": true, "confidence": 0.7 }],
  "decisionMakers": [{ "name": "Jane Smith", "title": "VP Sales", "email": "jane.smith@stripe.com", "emailInferred": false, "confidence": 0.82 }],
  "domainHasMx": true,
  "mxProvider": "Google Workspace",
  "confidenceScore": 0.85
}
```

Every field's provenance is documented: `companyName`/`description` from Schema.org & OpenGraph, industry classified from site content, employee band from JSON-LD → "team of N" copy → named team count, founded year from schema → copy → © range, logo from schema → og:image → favicon.

### Finding the CEO, founders and other decision makers on a domain

`decisionMakers` returns named people found on the domain — typically from an about, team or leadership page — with the role text as published.

**How often it fills: roughly one domain in five.** A company that publishes a leadership page gets you names; most sites do not have one, and the Actor returns an empty array rather than a guess. Treat this column as a bonus that sharpens a subset of the list, not as the reason to run the enrichment.

When it is empty, `linkedinUrl` is the reliable fallback: it comes back for essentially every domain that has a company page, and it is one click from the people you were looking for.

### Working out a company's email pattern before you guess an address

`emailPattern` is reserved for domains that expose enough addresses to infer a shape such as `{first}.{last}@`.

**In practice it is almost always empty**, because inferring a pattern needs several personal addresses on the same domain and most sites publish one role address or none. Do not build a workflow that depends on this column.

What does work: the `emails` array returns the addresses actually published on the site — about two domains in five carry at least one. A run over your list, filtered to rows where `emails` is non-empty, is a real send list. Rows where it is empty are form-only companies, which is a different play, not a failed lookup.

### Checking deliverability before a domain enters a cold email campaign

Every record includes a live DNS lookup of the domain's MX records, done at run time, not read from a cache:

- **`domainHasMx`** — `true` when the domain publishes mail exchangers at all. `false` means nothing can receive mail there: a parked domain, a redirect, or a brand that routes mail through a different domain. Those are the rows to drop before you import a list, and they are why part of a purchased list bounces on day one.
- **`mxProvider`** — who runs the mailbox, classified from the MX hostnames: `Google Workspace`, `Microsoft 365`, `Zoho`, `Proofpoint`, `Mimecast`, `GoDaddy`, `Amazon SES`, `Transactional ESP`, or `Other`. `Proofpoint` and `Mimecast` mean a security gateway is filtering inbound mail, which is useful to know before you decide on send volume and copy.

This is an infrastructure check on the **domain**, not an SMTP probe of each mailbox — it tells you mail can be received there and by whom, without touching the recipient's server.

### Employee count and revenue band from a website alone

Both `employeesEstimate` and `revenueBand` are best-effort and depend on the company publishing the figure.

**Measured on real domains: `employeesEstimate` fills for about one in five, `revenueBand` almost never.** A website rarely states its own headcount or revenue, and this Actor reads the website rather than buying a third-party database, so when the site is silent the field is empty. That is the honest trade of live enrichment: everything returned is sourced from the company right now, and nothing is invented to fill a column.

If firmographic size is the field your scoring model runs on, size it from what does come back reliably instead: `industry` plus `naicsCode`, the length of `technographics`, and whether the domain runs enterprise infrastructure.

### Finding a company's industry and NAICS code from its domain

`industry`, `naicsCode` and `sicCode` come back for every domain that resolves — these are the columns that survive a full list, so they are the ones to build segmentation on.

The pairing is what makes them useful. `industry` is the readable label you put in a CRM field; `naicsCode` and `sicCode` are the standard codes your RevOps team already filters on, and they let you match against government and industry datasets that are keyed by code rather than by name.

For a list that arrived as bare domains with no context, running this and grouping by `naicsCode` is usually the fastest way to see what you actually have before deciding who to contact.

### Company technology and stack detection by domain

Two columns describe what a company runs: `techStack` is the readable list (`["HubSpot", "Cloudflare", "React"]`) and `technographics` is the structured view of the same detection.

Both fill for essentially every live domain, which makes them the most dependable filter in the output. Concretely:

- selling a migration — filter for the platform you migrate away from
- selling an integration — filter for the tool it plugs into
- qualifying by budget — a domain running several paid SaaS products is spending money, which is a better signal than a headcount guess

Because the detection runs against the site live, it reflects what the company uses today rather than what a database recorded months ago.

### Checking a domain can receive email before importing it

`domainHasMx` and `mxProvider` answer a question most enrichment skips: can this domain accept mail at all?

`domainHasMx` is a live DNS check. A domain with no MX record cannot receive email, so every address you might send there will bounce — and a bounce on a cold domain costs sender reputation, not just one lead. `mxProvider` names who handles the mail (Google Workspace, Microsoft 365, and so on), which is itself a segmentation signal.

Filtering an import on `domainHasMx` before it reaches your sequencer is a cheap way to protect the sending domain you spent months warming.

### Reading the confidence score

Every row carries `confidenceScore`, which reflects how much the Actor could actually verify on that domain rather than how complete the row looks.

Use it as a sort, not a filter: work the high-confidence rows first, and treat the low ones as leads that need a human glance. A low score usually means the site was thin — a single-page site, a parked domain, or a redirect — and no enrichment tool will do better on it.

### What teams use this for

- **Outbound prospecting** — domain in, decision-maker + MX-verified email out.
- **CRM append** — backfill industry, NAICS, size, and tech stack on existing accounts.
- **ICP & TAM building** — segment by size, industry, country, or technographics ("everyone on Shopify + Klaviyo").
- **Technographic & competitive intel** — the categorized tool list a company actually runs.
- **Investor and recruiter research** — size, founding year, and who to contact, in one row.

### What a company actually does, from the URL alone

Sometimes the only thing you need is the sentence: what is this company, in plain language, without opening the site yourself.

`description` is the field for that, and it is the one column that never comes back empty. Measured across a sample of twelve live domains it filled **12 out of 12**, while `foundedYear` filled six and `revenueBand` two. It is written from what the company says about itself on its own pages, so it reads like a human summary rather than a category label:

```
stripe.com   → "Stripe powers online and in-person payment processing and
                financial solutions for businesses of all sizes."
```

Pair it with `industry` and `naicsCode` (both fill for about 8 in 10 domains) when you need something sortable, and with `logo` (12/12) when the list is going into a UI a human will scan.

This is the fastest way to turn a column of bare URLs into something you can read down and triage in a minute, before deciding which rows deserve a real look.

### Founding year, address and country from the company's own site

Straight answer first, because this one is partial by nature. Measured on the same twelve domains:

| Field | Filled |
|---|---|
| `foundedYear` | 6 / 12 |
| `address` | 3 / 12 |
| `country` | 1 / 12 |

A company only has a founding year or a street address in your data if it published one — an "About" page that says "founded in 2011", or a footer with a real postal address. Plenty of SaaS companies publish neither. This Actor reads the live site instead of buying a third-party database, so when the site is silent the field comes back `null` rather than a guess.

```
zapier.com   → foundedYear 2011, address "548 Market St. #62411, San Francisco, CA"
stripe.com   → foundedYear null, address null
```

So: useful for enriching a list where roughly half the rows gaining a founding year is a win, and the wrong tool if every row must have one. If you need location on every record, `phones` country codes and the `address` that does come back are better starting points than `country`, which is the thinnest field of the three.

### Enriching a one-off list of 50 domains without a subscription

There is no seat, no contract and no monthly minimum here. Pricing is per run plus per company actually resolved:

- **$0.01** to start a run
- **$0.02** per company resolved above your confidence threshold

A one-off list of 50 domains is **$1.01**. A hundred is $2.01. You pay for what came back — a domain that fails to resolve, or that lands under the `minConfidence` you set, is not charged.

That makes this practical for the jobs a subscription tool makes awkward: a conference attendee list you will never touch again, a webinar export, a batch of inbound signups you want to segment once, or a due-diligence list for a single deal. Run it, take the CSV, and stop — there is nothing to cancel afterwards.

If the list is large enough that the per-company price matters, raise `minConfidence`: low-confidence records are still returned for inspection but are not billed.

### Frequently asked questions

**Is this a Clearbit alternative without a subscription?**
Yes — that's the core trade. You get firmographics, technographics, and contacts per domain, billed per result ($20/1k) instead of through credit packs or an annual platform contract. You lose Clearbit's discovery search; you keep (and improve on) enrichment freshness.

**How accurate are the emails?**
Each email ships with a 0–1 confidence score, an `onDomain` flag, and a live MX check on the domain. Constructed pattern emails are explicitly flagged `emailInferred: true` — you always know confirmed vs. inferred.

**Can it replace Apollo for enrichment?**
For *enrichment of domains you already have*, yes, without the per-user plan. For *searching* a contact database you don't have inputs for, Apollo-style tools still make sense.

**Do I need an API key or account with a data vendor?**
No. It runs entirely on Apify. You can also call it via the Apify API from your own code and get JSON back.

**What does a 1,000-domain run cost?**
$0.01 start + $0.02 per record that clears your `minConfidence`. If 700 of 1,000 resolve with substantive data: ≈ **$14.01**. The 300 weak records cost nothing.

**Why are some fields null?**
Because the signal wasn't published on the site. Returning null instead of a fabricated value is deliberate — it's what makes the non-null fields trustworthy.

**Does it find decision-makers at large enterprises?**
Best results are companies that publish team/leadership pages (SMB, mid-market, agencies, professional services). Enterprises that hide their org behind a login still return firmographics, tech stack, and role emails.

**Can I use the output in HubSpot / Salesforce / Sheets?**
Yes — export CSV/Excel directly, or wire the dataset to your CRM with Apify's integrations (Zapier, Make, webhooks).

**Is it GDPR-safe?**
It reads only publicly published business information from the company's own website. You are responsible for lawful use in your jurisdiction.

**How is this different from your CRM Enrichment Actor?**
This one returns the **full nested profile** (all emails, all decision-makers, categorized technographics). [CRM Enrichment](https://apify.com/renzomacar/crm-enrichment) returns one **flat row** per company shaped for direct CRM import.

### Related Actors

- **[CRM Enrichment](https://apify.com/renzomacar/crm-enrichment)** — flat, import-ready company rows.
- **[People Enrichment](https://apify.com/renzomacar/people-enrichment)** — name + company → title, seniority & verified email.
- **[Lead Enrichment & Scoring](https://apify.com/renzomacar/lead-enrichment)** — 0–100 ICP-fit score per lead.
- All my [Actors](https://apify.com/renzomacar) — Yahoo Finance, SEC EDGAR, App Store & Google Play, Deep Research MCP.

***

Built by [Renzo Madueño](https://apify.com/renzomacar). Found a bug or want a field added? Open an **Issue** — I read and respond to every one.

# Actor input Schema

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

Company domains or website URLs to enrich (e.g. stripe.com, https://www.notion.so).

## `maxPagesPerDomain` (type: `integer`):

How many pages to crawl per company (homepage + about/team/contact/leadership). More pages = more decision-makers, slightly slower.

## `minConfidence` (type: `number`):

You only pay for results: records below this confidence score (or without substantive data) are returned but NOT charged.

## Actor input object example

```json
{
  "domains": [
    "stripe.com",
    "notion.so"
  ],
  "maxPagesPerDomain": 5,
  "minConfidence": 0.3
}
```

# Actor output Schema

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

One row per resolved company: firmographics, tech stack, social profiles and role emails with MX validation.

# 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 = {
    "domains": [
        "stripe.com",
        "notion.so"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("renzomacar/company-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 = { "domains": [
        "stripe.com",
        "notion.so",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("renzomacar/company-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 '{
  "domains": [
    "stripe.com",
    "notion.so"
  ]
}' |
apify call renzomacar/company-enrichment --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,renzomacar/company-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/ognJuajcweDkuHvC0/builds/ECsGEXGllfmwkwt3Z/openapi.json
