# B2B CRM Enricher — Clean, Dedupe & Enrich Lists (AI) (`noniner/b2b-crm-enricher`) Actor

Hand it a messy list of companies; get back a clean, deduplicated, enriched CRM-ready file. Derives the domain, fills firmographics and contacts, flags duplicates, verifies emails and scores data completeness.

- **URL**: https://apify.com/noniner/b2b-crm-enricher.md
- **Developed by:** [Fredrik Hansson](https://apify.com/noniner) (community)
- **Categories:** Lead generation, AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $100.00 / 1,000 record enricheds

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

## B2B CRM Enricher — Clean, Dedupe & Enrich Lists (AI)

**Hand it a messy list of companies; get back a clean, deduplicated, enriched CRM-ready file.** Exports, form fills, scraped lists and trade-show spreadsheets all arrive the same way: inconsistent columns, half-filled rows, the same company three times under three spellings. This Actor fixes that in one pass. It derives the real company domain for every row, collapses duplicates that share a domain, fills in firmographics and contacts, verifies the emails you already have, and scores how complete each record is — so what lands back in your CRM is clean, unique and usable.

You bring the mess. It hands back a deduplicated, enriched call sheet.

***

### The flow

```
messy list   →   derive domain   →   dedupe   →   enrich   →   verify emails   →   clean file
(any columns:     (website → email     (collapse    (firmographics  (SMTP verdict     (one row per
 company, email,   → company name)      rows that     + contacts +    on provided       unique company,
 website, domain)                       share a       socials +       emails)           scored for
                                        domain)       ICP fit)                          completeness)
```

1. **Load** your rows from `records` (paste objects) or an existing `datasetId`.
2. **Derive the domain** for each row — from a website/domain field, else the email domain (business domains only, free webmail is ignored), else a best-effort guess from the company name.
3. **Dedupe by domain** — rows sharing a domain collapse into one, keeping the most complete row as the primary and counting how many collapsed (`duplicateCount`).
4. **Enrich** every unique domain with firmographics, description, size signal, contacts, socials and optional ICP fit.
5. **Verify** the email each row provided (syntax, MX, live SMTP probe) and attach a deliverability status + confidence.
6. **Score completeness** 0–100 per record and push one clean row per unique company.

***

### What you get per unique company

1. **`domain`** — the derived, normalised company domain (the dedupe key).
2. **`name`, `industry`, `description`, `sizeSignal`** — firmographics filled in by AI from the company's own website.
3. **`emails`, `socials`** — contact emails and social profiles discovered during enrichment.
4. **`providedEmail`, `emailStatus`, `emailConfidence`** — the email that came in on your row, plus its deliverability verdict (`deliverable` / `risky` / `undeliverable` / …) and a 0–100 confidence.
5. **`icpFitScore`** — 0–100 fit against what *you* sell (only when you set `offering`).
6. **`duplicateCount`** — how many of your input rows collapsed into this record (1 = no duplicates).
7. **`completeness`** — 0–100 score of how many key CRM fields are now filled, so you can sort worst-first and chase the gaps.
8. **`input`** — your original row, untouched, for traceability.

***

### Three ways people use it

- **CRM data cleaning / RevOps hygiene** — "clean my CRM export": drop a HubSpot/Salesforce/Pipedrive export in and get back a normalised, domain-keyed, completeness-scored file ready to re-import. No more blank industry columns or mystery rows.
- **List deduplication** — merge several lists (events, webinars, scraped leads, partner referrals) and let the Actor collapse everything that shares a company domain into a single golden record, with `duplicateCount` showing how dirty the raw list really was.
- **Lead enrichment waterfall** — feed a thin list (just company names, or just emails) and let it fan out into full firmographics + verified contacts, the classic enrichment waterfall, but as one no-code step you can run from your CRM or an AI agent.

***

### Input

| Field | Type | Notes |
|---|---|---|
| `records` | array | Raw CRM rows as objects. Each row may carry any of `company` / `name`, `domain`, `website`, `email`. The domain is derived automatically. |
| `datasetId` | string | Alternative to `records`: ID of an existing dataset to read rows from (e.g. another scraper's output). Each item is treated as a raw row. |
| `offering` | string | Optional. What you sell — adds an `icpFitScore` (0–100) to every record. |
| `verifyEmails` | boolean | Default `true`. Verify the email each row provided (syntax, MX, live SMTP probe). |
| `maxResults` | integer | Max input rows to process. Default `1000`. One charge per **unique** company after dedupe. |
| `model` | string | Override the enrichment model. Default `gpt-4o-mini`. |

Provide **either** `records` **or** `datasetId` (or both).

#### Minimal input example

```json
{
  "records": [
    { "company": "Stripe", "website": "https://stripe.com" },
    { "email": "info@stripe.com" },
    { "company": "Notion", "website": "notion.so" }
  ],
  "verifyEmails": true
}
```

Or point it at a dataset:

```json
{
  "datasetId": "aBcDeFgHiJkLmNoPq",
  "offering": "Outbound sales tooling for B2B SaaS teams",
  "verifyEmails": true
}
```

The two Stripe rows above collapse into **one** record (`duplicateCount: 2`); Notion comes back as a second record.

***

### Output example

```json
{
  "input": { "company": "Stripe", "website": "https://stripe.com" },
  "domain": "stripe.com",
  "name": "Stripe",
  "industry": "Financial technology / payments",
  "description": "Stripe builds economic infrastructure for the internet — payments, billing and financial APIs for businesses.",
  "sizeSignal": "Large enterprise (thousands of employees)",
  "emails": ["support@stripe.com"],
  "socials": {
    "linkedin": "https://www.linkedin.com/company/stripe",
    "twitter": "https://twitter.com/stripe"
  },
  "providedEmail": "info@stripe.com",
  "emailStatus": "risky",
  "emailConfidence": 55,
  "icpFitScore": null,
  "duplicateCount": 2,
  "completeness": 88
}
```

***

### How it works

Under the hood this Actor is an orchestrator — it does the cleaning, domain derivation and deduplication itself, then calls two proven public Actors for the heavy lifting:

- **Firmographics + contacts** → [`noniner/ai-company-enrichment`](https://apify.com/noniner/ai-company-enrichment), once across all unique domains. It visits each company's site and uses OpenAI to extract name, industry, description, size signal, emails, socials and (if you set `offering`) an ICP fit score.
- **Email verification** → [`noniner/b2b-email-verifier`](https://apify.com/noniner/b2b-email-verifier), once across all provided emails. It runs an RFC syntax check, MX lookup and a live SMTP mailbox probe, returning a status and a 0–100 confidence. (Fully deterministic — no extra API keys.)

Both organs are best-effort: if one returns nothing, the run still completes with whatever could be derived.

***

### Pay-per-event pricing

| Event | Price | Charged when |
|---|---|---|
| `record-enriched` | $0.10 | Once per **unique** company record cleaned, deduplicated and enriched (after dedupe — duplicates are free). |

You're billed per unique company, not per messy input row, so a list that's 40% duplicates costs 40% less than it looks.

***

### Setup

The enrichment organ calls the OpenAI API. Set the environment variable / secret **`OPENAI_API_KEY`** on the Actor. Deduplication, domain derivation and email verification run without it — only the firmographic fields are left empty if it's missing.

***

### Use it from an AI agent (Apify MCP server)

This Actor is available through the [Apify MCP server](https://mcp.apify.com), so AI agents (Claude, ChatGPT, or anything that speaks MCP) can call it as a tool — hand it `records` or a `datasetId`, and read back the clean, deduplicated, enriched file — with no custom integration code.

***

### Run it locally

```bash
npm install
## put your input in storage/key_value_stores/default/INPUT.json
export OPENAI_API_KEY=sk-...   # enables the enrichment organ
apify run -p
```

***

### Chain it

**This Actor is a building block in the No Niner B2B suite.** Wire it to the next step with no code in the **Integrations** tab (trigger: *Run succeeded* → start the next Actor), or run them in sequence.

- ⬆️ Upstream: [B2B Lead List Builder](https://apify.com/noniner/b2b-lead-list-builder) — feed its dataset straight in via the `datasetId` input.
- ⬇️ Next: [B2B Intent Radar](https://apify.com/noniner/b2b-intent-radar) — score the cleaned list.
- ⬇️ Next: [B2B Stakeholder Mapper](https://apify.com/noniner/b2b-stakeholder-mapper) and [B2B Cold Email Sequence Writer](https://apify.com/noniner/b2b-cold-outreach-sequencer) — map who to contact and write the outreach.

***

### Related actors

- [AI Company Enrichment](https://apify.com/noniner/ai-company-enrichment) — Turn a list of domains into structured company profiles — firmographics, contacts, socials and ICP fit.
- [B2B Email Verifier](https://apify.com/noniner/b2b-email-verifier) — Give email addresses; get a deliverability verdict per email — syntax, MX, live SMTP probe and a risk score.
- [B2B Lead List Builder](https://apify.com/noniner/b2b-lead-list-builder) — Build a targeted list of B2B companies and contacts from a simple description of who you want to reach.
- [AI Cold Email Personalizer](https://apify.com/noniner/ai-cold-email-personalizer) — Turn a list of domains into personalized cold-email openers — AI reads each company's site.

***

**Part of the [No Niner](https://noniner.io) toolkit** — no-code web scraping, enrichment & AI data tools, all pay-per-use. Browse the full set at **[noniner.io](https://noniner.io)**.

# Actor input Schema

## `records` (type: `array`):

Raw CRM rows as objects. Each row may contain any of: company / name, domain, website, email. The domain is derived automatically. Leave empty if you use a datasetId instead.

## `datasetId` (type: `string`):

ID of an existing dataset to read rows from (e.g. the output of another scraper). Each item is treated as a raw CRM row. Use this instead of pasting records.

## `offering` (type: `string`):

Describe what your company sells. If set, each enriched record also gets an ICP fit score (0-100).

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

When a row includes an email, verify its deliverability (syntax, MX, live SMTP probe). Attaches a status and confidence per record.

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

Max input rows to process. One pay-per-event charge per UNIQUE company enriched (after dedupe).

## `model` (type: `string`):

Override the enrichment model (default gpt-4o-mini).

## Actor input object example

```json
{
  "records": [
    {
      "company": "Stripe",
      "website": "https://stripe.com"
    },
    {
      "email": "info@stripe.com"
    },
    {
      "company": "Notion",
      "website": "notion.so"
    }
  ],
  "verifyEmails": true,
  "maxResults": 1000
}
```

# Actor output Schema

## `records` (type: `string`):

All cleaned, deduplicated and enriched records in the default dataset.

# 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 = {
    "records": [
        {
            "company": "Stripe",
            "website": "https://stripe.com"
        },
        {
            "email": "info@stripe.com"
        },
        {
            "company": "Notion",
            "website": "notion.so"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("noniner/b2b-crm-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 = { "records": [
        {
            "company": "Stripe",
            "website": "https://stripe.com",
        },
        { "email": "info@stripe.com" },
        {
            "company": "Notion",
            "website": "notion.so",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("noniner/b2b-crm-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 '{
  "records": [
    {
      "company": "Stripe",
      "website": "https://stripe.com"
    },
    {
      "email": "info@stripe.com"
    },
    {
      "company": "Notion",
      "website": "notion.so"
    }
  ]
}' |
apify call noniner/b2b-crm-enricher --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,noniner/b2b-crm-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/N4BXLjgDDvOGffhDI/builds/Xayg7ZnWiwF6XGp7T/openapi.json
