Blog Open the app

Fuzzy Matching in Python: FuzzyWuzzy vs RapidFuzz vs Dedupe (2026 Comparison)

July 19, 2026 · 12 min read · Written by Sam Kale

If you've searched for "fuzzy matching in Python" recently, you've almost certainly landed on one of three libraries: FuzzyWuzzy, RapidFuzz, or Dedupe. They come up in every Stack Overflow answer, every "clean your data with pandas" blog, and every internal wiki about record linkage. They also solve slightly different problems, and the wrong choice will cost you either performance or a lot of custom code.

This post is a hands-on comparison for engineers building fuzzy matching or deduplication pipelines in Python in 2026. Runnable code, honest benchmarks, and a straight answer to "which one should I use?"

What's in this guide

  1. Background: what fuzzy matching actually does
  2. FuzzyWuzzy (and why it's on life support)
  3. RapidFuzz (the modern default)
  4. Dedupe.io (when you want a pipeline, not a scorer)
  5. Benchmarks
  6. Comparison table & picking guide
  7. When to skip Python and use a no-code tool
  8. FAQ

Background: What Fuzzy Matching Actually Does

Fuzzy matching is a family of algorithms that measure how similar two strings (or two records) are on a numeric scale — typically 0-100 — instead of returning a hard true/false. The core primitives are:

If you want the plain-English version of why these matter, we wrote it up here: Fuzzy Matching Explained — how to match "Acme Corp" to "ACME Corporation" without writing code. This post assumes you're going to write code.

FuzzyWuzzy (And Why It's on Life Support)

FuzzyWuzzy was released by SeatGeek in 2011 and became the de facto standard for a decade. If you've done fuzzy matching in Python before 2022, you probably wrote something like this:

from fuzzywuzzy import fuzz, process

fuzz.ratio("Acme Corp", "ACME Corporation")
# 70

fuzz.token_sort_ratio("Acme Corp", "ACME Corporation")
# 76

fuzz.partial_ratio("Acme Corp", "ACME Corporation")
# 100

choices = ["Acme Corporation", "3M Company", "Delta Air Lines"]
process.extractOne("acme corp", choices)
# ('Acme Corporation', 76)

It's a clean API. The problem in 2026 is:

Recommendation: don't start a new project with FuzzyWuzzy. If you have legacy code using it, RapidFuzz is a nearly drop-in replacement.

RapidFuzz (The Modern Default)

RapidFuzz is a C++ rewrite of FuzzyWuzzy's scoring functions, with a compatible Python API, SIMD-accelerated inner loops, and MIT licensing. It's the library we recommend for any new work in 2026.

Install:

pip install rapidfuzz

Basic scoring looks almost identical to FuzzyWuzzy:

from rapidfuzz import fuzz, process

fuzz.ratio("Acme Corp", "ACME Corporation")
# 69.56521739...

fuzz.token_sort_ratio("Acme Corp", "ACME Corporation")
# 76.0

fuzz.WRatio("Acme Corp", "ACME Corporation")
# 90.0 — blended scorer, good default

choices = ["Acme Corporation", "3M Company", "Delta Air Lines"]
process.extractOne("acme corp", choices, scorer=fuzz.WRatio)
# ('Acme Corporation', 90.0, 0)

The real power shows up when you want to score all pairs between two lists (a typical deduplication or record-linkage workload). Use process.cdist:

import pandas as pd
from rapidfuzz import fuzz, process

file_a = pd.read_csv("customers_from_stripe.csv")
file_b = pd.read_csv("customers_from_hubspot.csv")

scores = process.cdist(
    file_a["company"].tolist(),
    file_b["company"].tolist(),
    scorer=fuzz.token_sort_ratio,
    workers=-1,  # use all CPU cores
)
# scores is a 2D numpy array of shape (len(a), len(b))

For each row in file A, find the best candidate in file B:

import numpy as np

best_idx = scores.argmax(axis=1)
best_score = scores.max(axis=1)

file_a["best_match_b"] = file_b["company"].values[best_idx]
file_a["match_score"] = best_score

confident = file_a[file_a["match_score"] >= 90]
review   = file_a[(file_a["match_score"] >= 75) & (file_a["match_score"] < 90)]
unmatched = file_a[file_a["match_score"] < 75]

For a multi-column weighted match (which is usually what you actually want — see why single-column matching isn't enough), compute one score per column and blend:

weights = {"company": 0.45, "domain": 0.35, "city": 0.20}
combined = np.zeros((len(file_a), len(file_b)))

for col, w in weights.items():
    combined += w * process.cdist(
        file_a[col].fillna("").tolist(),
        file_b[col].fillna("").tolist(),
        scorer=fuzz.token_sort_ratio,
        workers=-1,
    )

When to use RapidFuzz: you have your matching logic figured out, you know your columns and weights, and you just need to score things fast. This covers 80% of real-world fuzzy matching in Python.

Scale note: the naive cdist above is O(N×M). Fine up to about 50,000 x 50,000. Above that, use blocking: only compare records that share a starting letter, a zip code, a phone area code, or a min-hash bucket. RapidFuzz doesn't do blocking for you — you write the block generator yourself, or you use Dedupe.io.

Dedupe.io (When You Want a Pipeline, Not a Scorer)

Dedupe is a different kind of library. RapidFuzz gives you similarity scores. Dedupe gives you a full record-linkage pipeline: active-learning training, automatic blocking with a learned predicate set, clustering, and canonicalization.

Install:

pip install dedupe

A minimal example (dedupe within one dataset):

import dedupe

fields = [
    {"field": "name",    "type": "String"},
    {"field": "address", "type": "String"},
    {"field": "city",    "type": "String"},
    {"field": "zip",     "type": "ShortString", "has missing": True},
]

deduper = dedupe.Dedupe(fields)
deduper.prepare_training(data)  # data: dict of {id: {field: value, ...}}

dedupe.console_label(deduper)    # interactive: y/n on candidate pairs
deduper.train()

clustered = deduper.partition(data, threshold=0.5)
# List of (record_ids_in_cluster, confidence_scores)

The console_label step is what makes Dedupe unusual: it asks you to label 20-50 candidate pairs as "yes / no / unsure" and uses those labels to learn the matching function. You can't just tell it "use token_sort_ratio at threshold 85" — it decides the equivalent by looking at your examples.

When to use Dedupe:

When NOT to use Dedupe: for a one-off script or when you already know the matching rules. Training and labeling are non-trivial ceremony for a job you could do in 20 lines of RapidFuzz.

Benchmarks

All numbers are from a 2024-era 8-core laptop (M2 Pro), Python 3.12, single process where relevant, workers=-1 where the library supports it. Rounded.

WorkloadFuzzyWuzzyRapidFuzzDedupe
1M pairs, ratio~48 s~1.4 sN/A (uses different scoring)
1M pairs, token_sort_ratio~92 s~3.2 sN/A
10k × 10k cdist~14 min (with python-Levenshtein)~22 s~40 s including block learning
100k rows, full dedup with clusteringNot viable without hand-written blocking~3-5 min with hand-written blocking~4-8 min including training

The pattern: RapidFuzz is roughly 20-40x faster than FuzzyWuzzy on the same operation. Dedupe is comparable to RapidFuzz on the raw scoring but adds real value on blocking and clustering at scale.

Comparison Table & Picking Guide

FeatureFuzzyWuzzy / TheFuzzRapidFuzzDedupe.io
Actively maintained (2026)No / minimallyYesYes
LicenseGPL (with Levenshtein)MITMIT
Speed vs FuzzyWuzzy1x20-40xComparable to RapidFuzz for scoring
Scorers includedratio, partial, token_sort, token_setAll FuzzyWuzzy + WRatio, JaroWinkler, DamerauLevenshtein, moreLearned; you don't pick a scorer
Multi-column nativeNoNo (write it yourself with cdist)Yes
Automatic blockingNoNoYes (learned predicates)
ClusteringNoNoYes
Active-learning UINoNoYes (console_label)
Learning curveTrivialTrivialModerate

The one-line picking guide

When to Skip Python and Use a No-Code Tool

Writing a fuzzy matching pipeline in Python is fun once. It's tedious the fifth time. If any of these apply, a UI tool will save you hours:

DedupFuzzy runs the same core algorithms (RapidFuzz-style scorers plus domain-specific normalization) with a UI: upload CSV/XLSX, pick columns and weights, review the score bands, export a golden record file. Free for 500 rows.

Many teams end up using both — a Python pipeline in production for scheduled dedup on their main tables, and a UI tool for ad-hoc file cleanups that don't warrant a code deploy.

FAQ

What about jellyfish, textdistance, and recordlinkage?

jellyfish is a great low-level library for individual string metrics (Jaro-Winkler, Metaphone, NYSIIS) if you don't need the process/extract helpers. textdistance has the widest set of algorithms in one package, but is generally slower than RapidFuzz for the common ones. recordlinkage is closer to Dedupe in scope — a full record-linkage toolkit — but with less active development and a steeper learning curve. In practice, most teams pick RapidFuzz for scoring and either Dedupe or hand-written blocking for scale.

How do I handle non-Latin scripts?

RapidFuzz is fully Unicode-aware, so scoring on Japanese, Arabic, Cyrillic, or CJK text works. What often breaks is your tokenization assumption: token_sort_ratio splits on whitespace, and Japanese doesn't use whitespace between words. For non-Latin scripts, consider ratio or character n-grams instead of token-based scorers.

How do I match on domain (email) properly?

Don't fuzzy match on emails as strings. Split into local@domain, exact-match the domain, and fuzzy match on local part or the associated person's name. Fuzzy-matching bob@acme.com against alice@acme.com returns a high score but they're different people.

Should I use embeddings / vector similarity instead?

For product descriptions and free-text catalogs, embeddings (sentence-transformers, OpenAI, Cohere) plus cosine similarity can outperform token-based methods on semantic similarity. For structured data — names, addresses, SKUs, vendor names — token and edit-distance methods are still faster, cheaper, and more predictable. In practice, hybrid pipelines (RapidFuzz for cheap high-confidence matches, embeddings for the borderline band) are becoming common in 2026.

Wrapping Up

The Python fuzzy matching landscape has consolidated. FuzzyWuzzy was great for its era but is no longer the right pick. RapidFuzz is the modern default for scoring — fast, MIT-licensed, actively maintained, drop-in compatible. Dedupe is the right pick when you want the whole record-linkage pipeline (blocking, clustering, active learning) instead of just a scorer.

Whichever you pick, the pattern that works is the same: normalize, block, score with weights, review the middle band, merge the top band, and log everything. The code differs; the discipline doesn't — and we walk through that discipline end-to-end in The Ultimate Guide to Data Deduplication.

Related Reading

Not in the mood to write a matching pipeline this afternoon? Upload your file and let DedupFuzzy run RapidFuzz-quality scoring on multiple columns with weights — no Python required. Free for 500 rows.

Try DedupFuzzy Free