# Company Name Normalizer & Deduplicator for CRM (`ultimatemc/company-name-canonicalizer`) Actor

Normalize messy company names and group duplicates in your CRM, leads or vendor lists. Strips Inc, LLC, Ltd and GmbH, splits d/b/a names, fixes caps, punctuation and common aliases (IBM, P\&G), and returns a clean name, canonical key, legal form and duplicate group ID per row.

- **URL**: https://apify.com/ultimatemc/company-name-canonicalizer.md
- **Developed by:** [Ultimate Foresight Inc](https://apify.com/ultimatemc) (community)
- **Categories:** Lead generation, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 results

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

**Company Name Normalizer & Deduplicator for CRM** cleans messy company names and **groups the duplicates**. It turns "ACME PLUMBING L.L.C.", "The Acme Plumbing Co." and "Acme Plumbing Inc. d/b/a Acme Rooter" into one company, **Acme Plumbing**, with the same `groupId`, and keeps the legal form, DBA and former names as separate fields. Use it to dedupe CRM accounts, merge lead lists from different scrapers, match vendors or employers across datasets, or prepare names for enrichment. Click **Start** with the prefilled 10 names to see the result in a few seconds.

> **Deterministic, explainable cleaning.** Every row shows the steps applied and how it was grouped (exact key, alias, spacing/plural variant or, if you turn it on, typo). It doesn't look companies up in any registry, so two different companies with the same cleaned name share a group: check `groupSize` before merging records automatically.

### What does Company Name Normalizer & Deduplicator do?

- ✅ Removes **legal forms worldwide** (81 forms: Inc, LLC, L.L.C., Corp, Co, Ltd, LLP, PLC, GmbH, GmbH & Co. KG, AG, S.A., S.A. de C.V., SARL, B.V., N.V., Pty Ltd, Pvt Ltd, K.K. and more) and returns them as `legalForm`, `legalFormType` and `jurisdictionHint`
- ✅ Splits **DBA, "trading as" and FKA/formerly** names into `dbaName` and `formerName`
- ✅ Removes **store and location numbers** ("McDonald's #1234"), parenthetical notes, leading "The", country qualifiers ("Deel US LLC"), and turns domains into names (acme.com → Acme)
- ✅ Fixes **casing** for ALL CAPS and all-lowercase input while keeping acronyms (IBM, AT\&T) and leaving mixed-case names as written
- ✅ Expands **common abbreviations** in the match key (Intl, Mfg, Svcs, Assoc, Natl, Mgmt, Bros), so "Acme Intl Mfg" groups with "Acme International Manufacturing"
- ✅ Knows **116 well-known aliases** (IBM, P\&G, AT\&T, GE, HP, 3M, McDonald's...) and accepts your own
- ✅ Groups **spacing, punctuation, "&"/"and" and plural variants** ("HospiceMD" / "Hospice MD"); optional **typo grouping** ("Deloite" / "Deloitte") with safety rules
- ✅ Returns every row with `cleanedName`, `canonicalKey`, `groupId`, `groupSize`, `groupCanonicalName`, `duplicateOfRow`, `matchMethod`, `steps` and `flags`, or **one row per group** as a deduplicated list
- ✅ Works on **plain lists, your own records, or another Actor's dataset** (company name field detected automatically, original record copied through)

### Who is it for?

- 📇 **Sales ops and RevOps teams** deduplicating CRM accounts and merging lead lists
- 🧲 **Lead-gen agencies** combining Google Maps, LinkedIn-style and directory scrapes into one clean list
- 🧾 **Finance and procurement** matching vendor names across systems
- 📊 **Data analysts and researchers** joining datasets on employer or company name
- 🤖 **Developers and AI agents** that need a cheap, repeatable name-cleaning step before enrichment

### How to normalize and deduplicate company names

1. Click **Try for free** or **Start**. The input is prefilled with ten messy names.
2. Paste your own names in **Company names** (one per line), or pass **Records** or a **Dataset ID** from another Actor.
3. Choose **Output**: all rows with their group, or one row per group.
4. Optional: turn on **Also group typos**, or add **Your aliases**.
5. Click **Start**, then download CSV/Excel or fetch the results through the API.

### Input example

This is the prefilled input:

```json
{
  "names": [
    "Acme Plumbing, LLC",
    "ACME PLUMBING L.L.C.",
    "The Acme Plumbing Co.",
    "Acme Plumbing Inc. d/b/a Acme Rooter",
    "Acme Plumbers LLC",
    "International Business Machines Corp.",
    "IBM",
    "McDonald's #1234",
    "MCDONALD'S CORPORATION",
    "Siemens AG"
  ]
}
```

Clean the company field of a scraper's dataset and output one row per company:

```json
{
  "datasetId": "YOUR_LEADS_DATASET_ID",
  "nameField": "companyName",
  "outputMode": "onePerGroup",
  "aliases": {
    "Big Blue": "International Business Machines"
  }
}
```

### Output example

Real output from the prefilled input above (run on 2026-10-21, America/Chicago): 10 names became **5 groups**.

| Row | Input | Cleaned | Group name | Group size | Match | Legal form |
|---|---|---|---|---|---|---|
| 1 | Acme Plumbing, LLC | Acme Plumbing | Acme Plumbing | 4 | group representative | LLC |
| 2 | ACME PLUMBING L.L.C. | Acme Plumbing | Acme Plumbing | 4 | exact key | LLC |
| 3 | The Acme Plumbing Co. | Acme Plumbing | Acme Plumbing | 4 | exact key | Co |
| 4 | Acme Plumbing Inc. d/b/a Acme Rooter | Acme Plumbing | Acme Plumbing | 4 | exact key | Inc |
| 5 | Acme Plumbers LLC | Acme Plumbers | Acme Plumbers | 1 | unique | LLC |
| 6 | International Business Machines Corp. | International Business Machines | International Business Machines | 2 | group representative | Corp |
| 7 | IBM | International Business Machines | International Business Machines | 2 | alias | - |
| 8 | McDonald's #1234 | McDonald's | McDonald's | 2 | group representative | - |
| 9 | MCDONALD'S CORPORATION | McDonald's | McDonald's | 2 | exact key | Corp |
| 10 | Siemens AG | Siemens | Siemens | 1 | unique | AG |

"Acme Plumbers LLC" is deliberately **not** merged with "Acme Plumbing": the words differ, and they may be different businesses (turn on typo grouping to be more aggressive). Two rows in full, untrimmed:

```json
[
  {
    "row": 4,
    "id": null,
    "inputName": "Acme Plumbing Inc. d/b/a Acme Rooter",
    "cleanedName": "Acme Plumbing",
    "canonicalKey": "acme plumbing",
    "groupId": "g-831b7ef85b",
    "groupSize": 4,
    "groupCanonicalName": "Acme Plumbing",
    "isFirstInGroup": false,
    "duplicateOfRow": 1,
    "matchMethod": "exact key",
    "legalName": "Acme Plumbing Inc.",
    "legalForm": [
      "Inc"
    ],
    "legalFormType": "corporation",
    "jurisdictionHint": "US",
    "dbaName": "Acme Rooter",
    "formerName": null,
    "aliasApplied": false,
    "steps": [
      "split d/b/a",
      "removed legal form"
    ],
    "flags": []
  },
  {
    "row": 7,
    "id": null,
    "inputName": "IBM",
    "cleanedName": "International Business Machines",
    "canonicalKey": "international business machines",
    "groupId": "g-02fb16bcc4",
    "groupSize": 2,
    "groupCanonicalName": "International Business Machines",
    "isFirstInGroup": false,
    "duplicateOfRow": 6,
    "matchMethod": "alias",
    "legalName": "IBM",
    "legalForm": null,
    "legalFormType": null,
    "jurisdictionHint": null,
    "dbaName": null,
    "formerName": null,
    "aliasApplied": true,
    "steps": [
      "alias"
    ],
    "flags": []
  }
]
```

### How much does it cost to normalize company names?

This Actor uses **pay-per-event** pricing:

- **$0.0005 per output row** (one row per name, or one per group in "one row per group" mode)
- a tiny Actor start fee of **$0.00005** per run
- plus the small Apify **platform usage** of the run, paid by you. For the prefilled 10-name example this was about **$0.0001** (256 MB, under 4 seconds).

So **1,000 names cost about $0.50** and **10,000 names about $5**, plus a few cents of platform usage. Apify lets you set a maximum cost per run.

### Use it through the API, integrations or AI agents

- **API:** `POST https://api.apify.com/v2/acts/ultimatemc~company-name-canonicalizer/run-sync-get-dataset-items` with your Apify token and the input JSON as the body. Client libraries for JavaScript and Python are on the **API** tab.
- **AI agents (MCP):** add `https://mcp.apify.com?tools=ultimatemc/company-name-canonicalizer` to Claude, Cursor, ChatGPT or any MCP client, so your agent can clean and group a list of companies as a tool. See [Apify MCP server](https://docs.apify.com/mcp).
- **Chain it after a scraper:** pass the `datasetId` of a leads, jobs or Maps scraper run, or trigger this Actor with a webhook.
- **Automation:** connect webhooks, Make, Zapier, n8n, HubSpot exports or Google Sheets from Apify Console.

### FAQ

#### How accurate is the grouping?

On a test of about 6,800 real employer names from public H-1B filings, written in different ways, 99-100% of each company's spelling variants ended up in one group, with about 5 joins of different employers per sample (mostly the same company under a slightly different name). Typo grouping is off by default because it trades precision for recall.

#### Will it merge two different companies with the same name?

Yes, if their cleaned names are identical ("Acme Plumbing LLC" in Texas and "Acme Plumbing Inc" in Ohio). It has no location or registry data. Use `groupSize`, your own IDs and other fields before auto-merging.

#### Does it change how names are displayed?

Only ALL CAPS or all-lowercase input is re-cased. The `legalName` field keeps the name with its legal form; `cleanedName` is without it.

#### Does it look companies up online or use AI?

No. It's deterministic rules plus a built-in alias list. No names are sent to any outside service, and the same input always gives the same output.

#### Which languages and countries?

Latin-script names. Legal forms cover the U.S., UK, EU, Latin America, India, Australia, Japan and more; accents are kept in the display name and ignored in the key.

### Limitations

- No registry or website lookup, so it can't tell apart two companies with identical names, or know that a subsidiary belongs to a parent (unless you add an alias).
- The alias list is curated, not complete: "Big Blue" → IBM needs your alias.
- Typo grouping can join similar but different names ("Computek" / "Computer"); review rows where `matchMethod` is "fuzzy typo".
- Non-Latin scripts (Chinese, Arabic, Cyrillic) are passed through with minimal cleaning.

### Data and privacy

Only company names are processed; no lookups or outside calls. Data is used only during the run and saved only to your own Apify dataset.

### Other tools by Ultimate Foresight

- [Ad Copy Policy Checker for Meta, Google & TikTok](https://apify.com/ultimatemc/ad-copy-policy-checker): checks ad text against Facebook, Instagram, Google Ads and TikTok ad policies before you submit.
- [H-1B Visa Sponsor Checker & Score for Job Listings](https://apify.com/ultimatemc/h1b-visa-sponsor-score): adds Department of Labor H-1B sponsorship history and a 0-100 score to company names or job listings.
- [Chain vs Independent Labeler for Google Maps Leads](https://apify.com/ultimatemc/chain-vs-independent-labeler): labels local businesses as chain, franchise or independent.
- [YouTube Monetization Checker for Scripts & Titles](https://apify.com/ultimatemc/youtube-script-monetization-checker): pre-checks titles, descriptions and scripts for wording that leads to limited or no ads.
- [Etsy & eBay Listing Policy & Banned Item Checker](https://apify.com/ultimatemc/etsy-ebay-listing-policy-checker): pre-checks marketplace listings for prohibited items, counterfeit wording and off-platform contact.
- [Job Scam Detector & Trust Score for Job Postings](https://apify.com/ultimatemc/job-posting-trust-score): scores job postings 0-100 for scam and ghost-job signals.
- [SAM.gov Amendment & Opportunity Change Tracker](https://apify.com/ultimatemc/sam-gov-opportunity-change-detector): shows what changed between versions of a SAM.gov notice: deadlines, set-asides, NAICS and attachments.
- [Product Recall Checker for Brands, UPCs & Products](https://apify.com/ultimatemc/product-recall-matcher): matches your brands, products, UPCs or model numbers against CPSC, FDA and NHTSA recalls.
- [App Store & Google Play Listing Policy Checker](https://apify.com/ultimatemc/app-store-listing-policy-checker): pre-checks app names, subtitles and descriptions against Apple and Google Play metadata rules.
- [FCC Covered List & Section 889 Bulk Screener](https://apify.com/ultimatemc/fcc-covered-list-screener): screens FCC IDs and brand names against the public FCC Covered List, with sources and timestamps (screening aid only).
- [Green Claims Checker: EU Greenwashing Ban & FTC](https://apify.com/ultimatemc/green-claims-checker): flags risky eco-friendly, carbon neutral and recyclable wording under EU, FTC and UK CMA rules.
- [Influencer #ad Disclosure Checker: FTC & ASA](https://apify.com/ultimatemc/influencer-ad-disclosure-checker): flags influencer posts with missing or unclear #ad labels under FTC, ASA/CAP and CMA rules.
- [Job Title to SOC Code Mapper with O\*NET Data](https://apify.com/ultimatemc/job-title-soc-code-mapper): maps job titles to SOC and O\*NET-SOC codes in bulk, with seniority and tech skills found in the posting.

Got a wrong merge or a missed duplicate? Open an issue on the **Issues** tab with the names, and we'll review the rule.

# Actor input Schema

## `names` (type: `array`):

One company name per line, as messy as you have them. The prefill has ten examples; replace them with your own list.

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

Objects with an "id" and a company name field (companyName, company, employer, organization, businessName, vendor, name ... detected automatically, or set Name field). The whole record is copied to the output under "record".

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

Apify dataset to clean, for example the output of a leads, jobs or Google Maps scraper. The company name field is detected automatically; override with Name field.

## `nameField` (type: `string`):

Field holding the company name in records or dataset items, e.g. "companyName" or "employer". Leave empty to detect it.

## `groupDuplicates` (type: `boolean`):

Give names that normalize to the same company the same groupId (exact canonical key, known aliases, and spacing or plural variants like 'HospiceMD' / 'Hospice MD').

## `fuzzyTypos` (type: `boolean`):

Also group names that differ by one or two letters ('Deloite' / 'Deloitte'). Catches typos, but can join different companies with similar names ('Computek' / 'Computer'), so review rows where matchMethod starts with 'fuzzy'. Off by default.

## `fuzzyThreshold` (type: `number`):

Minimum similarity (0-1) for typo grouping. Higher is stricter.

## `expandAbbreviations` (type: `boolean`):

In the canonical key, expand Intl, Mfg, Svcs, Assoc, Bros, Natl, Mgmt, St./Mt. at the start and similar, so 'Acme Intl Mfg' matches 'Acme International Manufacturing'. The cleaned display name keeps your wording.

## `stripStoreNumbers` (type: `boolean`):

Remove trailing '#1234', 'Store 55' or 'Location 3' so branches group with the brand.

## `stripCountryQualifiers` (type: `boolean`):

Remove a trailing US / USA / NA / North America / UK / Canada / India qualifier from the key ('Deel US LLC' = 'Deel Inc'), except in names like 'Bank of America' or 'BMW of North America'.

## `aliases` (type: `object`):

Extra alias -> canonical name pairs, e.g. {"Big Blue": "International Business Machines", "MSFT": "Microsoft"}. Added to the built-in list of common aliases.

## `outputMode` (type: `string`):

All rows (one result per input name, with its group) or one row per group (deduplicated list; you pay only for those rows).

## `includeOriginal` (type: `boolean`):

For records and dataset items, copy the original item into the output field "record".

## `maxItems` (type: `integer`):

Maximum number of names to process in this run (grouping happens across all of them).

## Actor input object example

```json
{
  "names": [
    "Acme Plumbing, LLC",
    "ACME PLUMBING L.L.C.",
    "The Acme Plumbing Co.",
    "Acme Plumbing Inc. d/b/a Acme Rooter",
    "Acme Plumbers LLC",
    "International Business Machines Corp.",
    "IBM",
    "McDonald's #1234",
    "MCDONALD'S CORPORATION",
    "Siemens AG"
  ],
  "groupDuplicates": true,
  "fuzzyTypos": false,
  "fuzzyThreshold": 0.92,
  "expandAbbreviations": true,
  "stripStoreNumbers": true,
  "stripCountryQualifiers": true,
  "outputMode": "allRows",
  "includeOriginal": true,
  "maxItems": 10000
}
```

# Actor output Schema

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

No description

# 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 = {
    "names": [
        "Acme Plumbing, LLC",
        "ACME PLUMBING L.L.C.",
        "The Acme Plumbing Co.",
        "Acme Plumbing Inc. d/b/a Acme Rooter",
        "Acme Plumbers LLC",
        "International Business Machines Corp.",
        "IBM",
        "McDonald's #1234",
        "MCDONALD'S CORPORATION",
        "Siemens AG"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ultimatemc/company-name-canonicalizer").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 = { "names": [
        "Acme Plumbing, LLC",
        "ACME PLUMBING L.L.C.",
        "The Acme Plumbing Co.",
        "Acme Plumbing Inc. d/b/a Acme Rooter",
        "Acme Plumbers LLC",
        "International Business Machines Corp.",
        "IBM",
        "McDonald's #1234",
        "MCDONALD'S CORPORATION",
        "Siemens AG",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("ultimatemc/company-name-canonicalizer").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 '{
  "names": [
    "Acme Plumbing, LLC",
    "ACME PLUMBING L.L.C.",
    "The Acme Plumbing Co.",
    "Acme Plumbing Inc. d/b/a Acme Rooter",
    "Acme Plumbers LLC",
    "International Business Machines Corp.",
    "IBM",
    "McDonald'\''s #1234",
    "MCDONALD'\''S CORPORATION",
    "Siemens AG"
  ]
}' |
apify call ultimatemc/company-name-canonicalizer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ultimatemc/company-name-canonicalizer"
        }
    }
}
```

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/kNdPbHKfqTvu1mh9R/builds/ApWFYX4j46zroWAiW/openapi.json
