Fuzzy String Matching for Company Names: 9 Rules

Home Blog Sales & Revenue Fuzzy String Matching for Company Names: 9 Rules
Sales & Revenue

Fuzzy company-name matching fails quietly: 9 pairs of different companies all scored 100. Get the 9-rule normalisation set and calibration method.

MS
August 12, 2026 Updated Aug 14 16 min

Your CRM holds two records for the same company and the matcher cannot see it. It also holds two records for genuinely different companies that the matcher swears are identical. Both failures trace back to the same root cause, and it is not your data.

Fuzzy string matching for company names is usually treated as a library call: import a scorer, pick a number, ship it. That approach fails in a specific and measurable way. We scored 16 company-name pairs with RapidFuzz 3.14.5 and jellyfish 1.2.1, verifying each entity against the Global Legal Entity Identifier Foundation register. Nine of those pairs are legally distinct companies. After normalisation, all nine scored a perfect 100 on token_set_ratio.

In Validity’s 2025 survey of 602 CRM users and stakeholders, 76% said less than half of their organisation’s CRM data is accurate and complete. Name matching is where a large share of that inaccuracy is manufactured, one confident wrong merge at a time.

Direct answer — How do you do fuzzy string matching on company names?

Fuzzy string matching for company names is a two-stage process. First normalise both names with a fixed, ordered rule set: fold diacritics, expand ampersands, strip punctuation, then remove trailing legal forms such as Ltd or GmbH. Then score the normalised pair with a string similarity metric. Token metrics handle word-order and abbreviation differences that edit distance cannot. Set the accept threshold by scoring a labelled sample of your own pairs, never by copying a published number.

Key Takeaways

  • The standard algorithms were designed for surnames and error-correcting codes. Company names break their core assumptions.
  • Normalisation must run before scoring, and the rule order matters: strip punctuation before legal forms, or Ltd. never matches Ltd.
  • Normalisation raises every score, including the wrong ones. It buys recall and costs precision.
  • token_set_ratio returned 100 for all nine legally distinct pairs we tested. It cannot separate a parent from its own subsidiary.
  • No threshold rescues identical strings: “Bank of China Limited” is three separate registered entities.
  • Derive your bands from a labelled sample of your own records. Budget about two hours for a first pass.

Why fuzzy string matching breaks on company names

Fuzzy string matching breaks on company names because the standard algorithms were built for surnames and typographical errors, not multi-token legal entities. This is not a criticism of the algorithms. It is a mismatch between what they were designed to measure and what a CRM asks them to decide.

Where the standard algorithms came from

Soundex was patented in 1918. Robert C. Russell’s patent, titled simply “Index”, describes grouping names “phonetically rather than according to the alphabetical construction of the names” so that variant spellings of a person’s name sit together in a card index. Jaro-Winkler came later and carries the same DNA: Winkler’s 1990 paper for the Fellegi-Sunter model tuned its prefix weighting on “typographical variation in strings such as first name or surname”, for the Post Enumeration Survey of the 1990 US census. Levenshtein distance, published in 1965, was not about names at all; it described error-correcting codes for binary sequences.

So the three metrics every tutorial reaches for were calibrated on human names and transmission errors. A company name is a different object. It is several tokens long, it carries a jurisdiction-specific legal form, and the tokens that distinguish two real entities are frequently the most ordinary words on the page.

The four ways company names differ

Company-name variation clusters into four kinds, and each one defeats a different metric. Confusing them is why single-metric matching plateaus. Getting the score right is only half the job; something downstream still has to decide which account a record belongs to, and which account a lead actually belongs to is a separate decision with its own tie-breakers.

Diagram of a company name split into noise, core, descriptor and legal form tokens

Failure modeExample pairWhat raw edit distance doesWhat actually fixes it
Legal form differsNestlé S.A. / Nestle SAPenalises the suffix and the accent as real differencesNormalisation: fold diacritics, strip the legal form
Token order differsDeutsche Bank AG / Bank, Deutsche (AG)Scores 62, treats reordering as many editsA token metric that sorts before comparing
Acronym or aliasIBM / International Business Machines CorporationScores 18, no shared substring to findAn alias table. No metric solves this
One added division tokenSiemens AG / Siemens Energy AGScores high, because one word is a small editA guardrail, not a threshold

That last row is the expensive one. The difference between a parent and a separately incorporated division is often a single word, which is exactly where edit distance is smallest and the commercial consequence is largest. Name is also simply a poor identifier compared with the fields that identify a company more reliably than its name, which is why mature matchers treat the name as one signal rather than the deciding one.

Normalise company names before you score them

To normalise a company name, apply a fixed, ordered set of rules before any similarity score is calculated. Order is not a stylistic choice here. Strip legal forms before punctuation and Ltd. keeps its full stop, so it never matches the token Ltd and the rule silently does nothing.

The nine-rule normalisation pipeline

This is the pipeline we used for every measurement in this article. We publish it as CNN-1.0 so it can be cited and reproduced rather than paraphrased.

#RuleInput → OutputWhy it sits here
1Fold Unicode and diacriticsCoöperatieveCooperatieveBefore case folding, so mapped characters fold consistently
2Case foldBANK OF CHINAbank of chinaEvery later comparison is case-insensitive
3Expand conjunctionsProcter & Gambleprocter and gambleBefore punctuation stripping, or the & is destroyed
4Collapse dotted initialismsS.A.saBefore the general strip, or S.A. becomes two tokens
5Strip remaining punctuationBank, Deutsche (AG)bank deutsche agBefore rule 7, or Ltd. never matches Ltd
6Collapse whitespaceapex toolapex toolTokenisation downstream assumes single spaces
7Drop a leading articleThe Procter and Gamble Companyprocter and gamble companyBefore legal-form stripping, so the tail is reached cleanly
8Strip trailing legal forms, iterativelyprocter and gamble companyprocter and gambleTail only, never mid-string, and never if it empties the name
9Protect descriptor tokensLiberty Global Holdings keeps holdingsA guard on rule 8. Without it, rule 8 manufactures false positives

Rule 9 is the one most implementations omit, and it is the one that prevents the pipeline from doing harm. Holdings, Group, Partners, Ventures, Labs, International, Technologies and Solutions all look like noise and none of them are: they routinely mark a distinct registered entity. Rule 9 also protects purely numeric and single-letter tokens.

Legal forms and entity tokens by jurisdiction

Rule 8 needs a token list, and most articles hand-wave it as “strip LLC, Inc, Corp, Ltd”. That covers one country. The authoritative source is ISO 20275, published by GLEIF as the Entity Legal Forms Code List, version 1.6 of February 2026, which catalogues more than 3,600 legal forms across more than 200 jurisdictions and is released under CC0.

GLEIF tells you which forms exist. It does not tell you whether stripping one is safe. That judgment is the column below.

JurisdictionCommon legal-form tokensCRM action
United Statesinc, incorporated, corp, corporation, co, company, llc, lp, llp, pllc, pcStrip
United Kingdom, Irelandltd, limited, plc, llp, cic, dac, teorantaStrip
Canada, Australia, New Zealandulc, cie, pty ltd, pty limited, nlStrip
Germany, Austria, Switzerlandgmbh, ag, kg, kgaa, ohg, se, ug, gesellschaft mit beschränkter haftungStrip
Netherlands, Belgiumbv, nv, cv, vof, uaStrip
France, Spain, Italy, Portugal, Brazilsarl, sas, sa, snc, sl, slu, spa, srl, ltda, ldaStrip
Nordicsab, hb, kb, as, asa, aps, oy, oyj, kyStrip
Japan, Singapore, India, Polandkk, kabushiki kaisha, pte ltd, pvt ltd, sp z o oStrip
Any jurisdictionholdings, group, partners, ventures, labs, international, technologies, solutions, energy, capital, mediaKeep, and flag as a review signal

IMPORTANT

Several tokens are legal forms in one language and ordinary words in another. as is a Norwegian legal form and an English preposition; co ends both a legal form and Coca-Cola’s registered name. Strip legal forms from the tail of the string only, never mid-string.

Name standardisation is the first step of any wider hygiene programme, and it belongs alongside the deduplication and validation work that a CRM data cleansing programme sequences around it. It is also worth checking whether you need to derive the canonical name at all, because an enrichment provider can often hand you the registered legal name directly, which changes normalisation from a guess into a lookup.

Where normalisation goes too far

Aggressive normalisation is the most common way teams make matching worse while believing they improved it. Treat “Global”, “Media” or “Holdings” as noise and Liberty Global Ltd. collapses onto Liberty Global Holdings Ltd. Those are two distinct registered entities with different LEIs in different jurisdictions. The pipeline did not find a match; it created one.

Which similarity metric to use on company names

String similarity metrics score how close two names are, and each one is blind to a different kind of company-name difference. Choosing between them is the actual engineering decision, and it is not the same as choosing a number.

Edit distance, and a correction worth knowing

Edit distance counts the single-character operations needed to turn one string into another. It handles typos and truncation well and token reordering badly. One detail trips up most write-ups: fuzz.ratio is not Levenshtein. The RapidFuzz documentation defines it as “the normalized Indel similarity”, which permits insertions and deletions but not substitutions. Levenshtein allows substitution, so the two disagree on exactly the single-character swaps that separate similar company names.

Token metrics

Token metrics split the name into words and compare the sets, which is why they beat edit distance on reordering. Deutsche Bank AG against “Bank, Deutsche (AG)” scores 62 on fuzz.ratio and 100 on token_sort_ratio. That gain is real. The cost arrives with token_set_ratio, which the same documentation describes as comparing “the words in the strings based on unique and common words between them”. Because it discounts the words that are not shared, an added division token becomes nearly free, and the metric reports a perfect match between a parent and its own subsidiary.

Phonetic and prefix metrics

Soundex and Metaphone reduce a string to a phonetic key, which is useful for blocking and unreliable for scoring multi-token company names in mixed languages. Jaro-Winkler weights early characters heavily, which flatters any pair sharing a first word. Every company in a corporate family shares its first word.

MetricWhat it is blind toUse it whenAvoid it when
fuzz.ratio (Indel)Word order; abbreviationsNames differ by typos or truncationFields arrive in inconsistent word order
token_sort_ratioExtra or missing tokensWord order is unreliable, token count is notOne name carries a division or region word
token_set_ratioWhole added tokensRecall matters more than precision, with reviewYou are auto-merging. It cannot separate parent from subsidiary
Jaro-WinklerDifferences after the prefixRanking candidates inside a blockNames share a first word, as corporate families do
Jaccard on tokensSpelling entirelyA cheap first filterNames are one or two tokens long

The decision framework is short. Use token_sort_ratio as the default company-name scorer. Use fuzz.ratio as a second opinion when token counts match. Use Jaro-Winkler to rank candidates you have already blocked, never as the accept signal. Avoid token_set_ratio for any automatic merge. Reach for an alias table, not a metric, when acronyms and rebrands appear.

PRO TIP

Check the licence before the benchmark. RapidFuzz is MIT (3.14.5, April 2026) and thefuzz is MIT (0.22.1, January 2024) and runs on RapidFuzz underneath. The original fuzzywuzzy is GPLv2 and was last released in February 2020, which matters if you are embedding it in commercial code.

Worth saying plainly: a name that should never have reached the matcher is not a matching problem. Free-text junk, test records and personal email domains are cheaper to reject at capture, which is what a validation gate ahead of the matcher is for.

Cut the comparison count with blocking

Blocking groups records into candidate buckets so the matcher compares thousands of pairs instead of billions. Without it, comparison count grows quadratically and any real CRM becomes uncomputable.

Formula
Comparisons = n × (n − 1) ÷ 2

Splink, the record-linkage library published by UK Ministry of Justice Analytical Services, puts the consequence in plain numbers: a dataset of one million records generates around 500 billion pairwise comparisons, and the overwhelming majority cannot possibly be matches.

Company name matching pipeline from normalisation and blocking to scoring, thresholds and review

Blocking keys that work for company names

Good keys are cheap, stable and available on both sides. The first token of the normalised name works well because normalisation has already removed the articles and punctuation that would otherwise scatter it. Email domain, country and the first four characters of the normalised name are all serviceable. Phonetic keys are reasonable here, where a false grouping costs compute rather than correctness.

Blocking is where recall dies

A true pair split across two blocks is never compared, so no threshold can recover it. Block on country and every cross-border duplicate survives forever. This is the failure mode worth testing deliberately: take pairs you know are matches, run the blocking step alone, and count how many end up in different buckets. Blocking also sets the latency budget, which matters because inbound records are usually on a clock that the routing layer’s speed-to-lead targets already define.

How to set and defend a match threshold

To set a match threshold, score a labelled sample of your own pairs and read precision and recall at each cutoff. Copying a number from an article, including this one, produces a threshold you cannot defend when someone asks why a deal landed on the wrong rep.

Label a stratified sample

Take roughly 300 candidate pairs, drawn deliberately across the score range rather than randomly, since a random draw from a real CRM is almost all obvious non-matches. Mark each pair same entity or different entity using a registry rather than intuition. The GLEIF LEI register is free, and two distinct LEIs settle the question.

Read the trade-off, then choose

Precision is the share of accepted pairs that are genuinely the same entity. Recall is the share of true pairs you accepted. Raising the cutoff buys precision and loses recall, and there is no setting that gives you both. What the curve tells you is where the cost changes character.

Illustrative company-name match threshold chart showing precision rising as recall falls

Pick the cutoff by asking which error you can afford. A wrong merge destroys history and is expensive to unwind; a missed match leaves a duplicate that someone will notice later. Most teams discover their auto-merge bar sits higher than they assumed. The same discipline applies to any scoring cutoff you have to justify, which is the argument behind choosing a cutoff you can defend to a sales leader.

Seed bands you are meant to replace

If you need somewhere to start before the sample is labelled, begin at 92 for auto-accept on token_sort_ratio over CNN-1.0 names, 80 to 92 for the review queue and below 80 for reject. Treat those as placeholders with no evidential weight. They are a starting point for calibration, not a result.

Workflow · 2 hours

How to calibrate a company-name matcher: one first pass

Build and calibrate a company-name matcher against your own records, ending with auto-accept, review and reject bands you can defend.

  1. Normalise both name lists

    Run every name through the nine CNN-1.0 rules in order. Store the normalised key in its own column so you can compare raw against normalised later.

  2. Generate candidate pairs with a blocking key

    Block on the first token of the normalised name plus country. Count how many pairs survive; if it is still millions, add a second key.

  3. Score every candidate with two metrics

    Compute token_sort_ratio and fuzz.ratio for each pair. Keep both columns; disagreement between them is a useful review signal.

  4. Hand-label 300 pairs across the score range

    Sample deliberately from high, middle and low scores. Resolve each pair against a registry, not intuition. This step takes most of the two hours.

  5. Read precision and recall at each cutoff

    Step the threshold in increments of two and tabulate both measures. Find the point where precision starts falling faster than recall rises.

  6. Set the auto-accept, review and reject bands

    Write the three numbers down with the date and the sample size beside them, then re-run the pass whenever a new data source is connected.

Budget about two hours for a first calibration pass, most of it hand-labelling. That estimate covers one pass over one pair of lists, not a production deployment with monitoring and re-training.

False positives: high scores, different companies

A false positive is a pair that scores above your threshold but belongs to two different legal entities. This is the part of company-name matching that thin write-ups raise and then abandon, so here is the measured version.

Every score below comes from RapidFuzz 3.14.5 and jellyfish 1.2.1 over CNN-1.0-normalised names. Every “different entity” pair was confirmed as two separately registered entities in the GLEIF LEI register. One row, marked as such, is a synthetic export artefact rather than an observed record.

PairSame legal entity?fuzz.ratiotoken_set_ratio raw → CNN-1.0Jaro-Winkler
Siemens Aktiengesellschaft / Siemens Energy AGNo66.758.3 → 100.090.0
Siemens Aktiengesellschaft / Siemens Aktiengesellschaft ÖsterreichNo31.8100.0 → 100.083.8
Allianz SE / Allianz Technology SENo56.0100.0 → 100.087.8
Liberty Global Ltd. / Liberty Global Holdings Ltd.No75.7100.0 → 100.092.2
Coöperatieve Rabobank U.A. / Coöperatieve Rabobank Bollenstreek U.A.No76.4100.0 → 100.092.4
Nestlé S.A. / NESTLE ECUADOR S.A.No60.053.3 → 100.088.6
Apex Tool Group, LLC / APEX TOOL GROUP PTY. LIMITEDNo100.029.2 → 100.0100.0
The Procter & Gamble Company / The Procter & Gamble Global Finance Company, LLCNo70.683.3 → 100.090.9
Bank of China Limited / BANK OF CHINA LIMITEDNo100.028.6 → 100.0100.0
Nestlé S.A. / Nestle SAYes100.080.0 → 100.0100.0
L’Oréal S.A. / LOreal SAYes100.076.2 → 100.0100.0
Procter & Gamble Co / Procter and Gamble CompanyYes100.084.8 → 100.0100.0
Coöperatieve Rabobank U.A. / Cooperatieve Rabobank UAYes100.092.0 → 100.0100.0
Deutsche Bank AG / Bank, Deutsche (AG) (synthetic)Yes61.591.4 → 100.064.7
Meta Platforms, Inc. / Facebook, Inc.Yes18.247.1 → 18.241.9
IBM / International Business Machines CorporationYes17.68.7 → 11.857.7

High score, different company

Read the token_set_ratio column again. All nine legally distinct pairs reach 100.0 after normalisation. Fourteen of the sixteen pairs reach 100.0, and only five of those fourteen are actually the same entity. On this set, accepting everything that scores 100 on token_set_ratio would have been correct 5 times out of 14.

Two rows are unwinnable by any string method. Apex Tool Group’s US and Australian entities normalise to the identical string, and “Bank of China Limited” resolves to three separately registered entities with byte-identical legal names in Australia, France and Panama. When two different companies share a name exactly, no threshold and no metric can help. Only a second field can.

IMPORTANT

Normalisation is not a precision tool. It raised token_set_ratio by 71.4 points on the Bank of China pair and by 46.7 on the Nestlé pair, both wrong matches. Normalisation buys recall and spends precision. Adding it obliges you to re-calibrate the threshold, not to relax it.

CNN-1.0 normalisation chart showing both same-entity and different-entity scores rising

Low score, same company

The mirror case gets almost no coverage and costs just as much. IBM against its own registered name scores 17.6, and no amount of tuning fixes it. Meta Platforms scores 18.2 against Facebook, and normalisation makes it worse, because “Inc.” was the only token the two names shared. SEC filings record the former name as “Facebook Inc” until 27 October 2021, which is the fix: acronyms and rebrands need an alias table, maintained by hand, checked before scoring.

The guardrails that catch both

Guardrails are token-level rules that run after scoring and override it. Four earn their keep: reject when numeric tokens disagree, reject when geographic tokens disagree, reject when two candidates carry distinct LEIs, and demote to review when the only difference is a protected descriptor such as Holdings or Energy. Guardrails work where thresholds cannot, because they inspect which token differs rather than how much. What happens to a confirmed duplicate afterwards, including which record survives, is governed by the merge rules that decide which record wins. Subsidiary and post-acquisition ownership policy is a separate decision from matching, and it belongs with your account hierarchy rules.

One honest limit on the numbers above: 16 pairs chosen to be adversarial is a demonstration, not a benchmark. The pairs are published so the result can be reproduced, and the precision figure describes this set only.

Frequently Asked Questions

No single algorithm wins. Use token_sort_ratio over normalised names as the default, because it handles word-order differences that edit distance cannot. Add fuzz.ratio as a second opinion, and keep an alias table for acronyms. Avoid token_set_ratio for automatic merges.

Normalise both names first, then score them with RapidFuzz, which is MIT-licensed and fast. Use jellyfish for Jaro-Winkler and phonetic keys, and Splink for large-scale probabilistic linkage. Avoid the original fuzzywuzzy: it is GPLv2 and was last released in 2020.

Yes, several browser-based matchers exist, and they are fine for testing a handful of names. Do not paste customer records into one. Pasting CRM data into a third-party web form is a disclosure, and it is unlikely to be covered by your processing agreements.

There is no portable number, because the right cutoff depends on your name distribution and on which error costs you more. Start near 92 for auto-accept on normalised names, send 80 to 92 to review, then replace both figures with values measured on 300 labelled pairs of your own.

Share
MS
Written by
Mahesh Sirvi
Founder, Ivris Tech
Started in sales, moved into B2B demand generation — ABM, lead scoring, BANT, and pipeline operations. Now focused on technical SEO, AI workflows, and n8n automation. Writes about B2B strategy, AI & automation, and MarTech at Ivris Tech from hands-on experience. MBA in Business Analytics. Still learning, still building.

Get B2B marketing insights weekly

Strategies, frameworks, and tools — no fluff. Join operators who read Ivris Tech.

No spam. Unsubscribe anytime.
Link copied!