rolodexter · v2.11.1 · python + typescript

Every export names the same field differently. Map them all to one schema.

Point it at a CRM payload, a form post, or a CSV nobody has opened since 2019, and get canonical fields with a confidence score.

Free & MIT-licensed 600+ aliases 62 canonical fields 40 languages, on demand
import.py
from rolodexter import ContactMapper

mapper = ContactMapper()

result = mapper.map_payload({
    "fname": "jane",
    "surname": "doe",
    "mobile": "+1-650-253-0000",
    "employer": "Tech Corp",
    "Column 1": "jane.doe@example.com",
})

# result.normalized
{
  "first_name": "Jane",
  "last_name":  "Doe",
  "phone":      "+16502530000",
  "company":    "Tech Corp",
  "email":      "jane.doe@example.com"
}

// the problem

Nobody agrees on what to call a phone number.

Every CRM and CSV export invents its own names for the same five fields. Hand-written mapping works until the next integration.

ServiceFirst namePhoneCompany
HubSpotfirstnamemobilephonecompany
SalesforceFirstNameMobilePhoneCompany
MailchimpFNAMEPHONECOMPANY
Google CSVGiven NamePhone 1 - ValueOrganization 1 - Name
Random CSVColumn AColumn BColumn C
RoloDexterfirst_namephonecompany

Even Column A is recoverable. With a meaningless header it reads the value's shape instead, so an email still lands on email.


// four-layer matching

Four strategies, tried in order of certainty.

Every field walks the chain until something matches, and the strategy that caught it sets the confidence you get back.

1

Exact

An O(1) lookup against 600+ known aliases across 62 canonical fields. This is where most real payloads land.

confidence 1.0
2

Normalized

Handles CamelCase, dot.path, spaces to underscores, and the rest of the casing zoo.

high confidence
3

Fuzzy

Typos survive. phne_nmbr still resolves to phone, and you can see that it was a guess.

confidence 0.70–0.85
4

Heuristic

No usable header? It reads the data instead, detecting emails, phones, URLs, and postal codes by shape.

confidence 0.6

Nothing is silent. Non-fatal issues come back as warnings; strict mode turns a low-confidence guess into a loud failure.


// value normalization

Matching the field is only half of it.

The value is cleaned too, so what comes out is ready to store, not ready for more regexes.

  • Phones to E.164 via libphonenumber, and phone numbers buried inside a free-text field can be extracted on their own.
  • Names title-cased with particle awareness, so jane van der berg becomes Jane van der Berg, not Jane Van Der Berg.
  • Emails lowercased and trimmed. Addresses collapsed and title-cased. Tags coerced to a real list.
  • Your own names too. Per-caller overrides map a vendor's MMERGE6 or cf_lead_score onto a canonical field without patching the alias table.
why it chose that
mapper.identify("fname")
FieldMatch(canonical='first_name',
           confidence=1.0, strategy='exact')

mapper.identify("phne")
FieldMatch(canonical='phone',
           confidence=0.70, strategy='fuzzy')

mapper.identify("Column X",
                value="jane@test.com")
FieldMatch(canonical='email',
           confidence=0.6, strategy='heuristic')

// at scale

One contact, or the whole export.

The same mapper handles a single webhook payload and a multi-gigabyte CSV, from a script, a notebook, or a terminal.

Batch and streaming

Map a list in one call, or stream a huge CSV or JSONL in constant memory. Preview mode retains nothing.

🐼

DataFrames

Hand it a DataFrame and get canonical columns with normalized values. Unrecognised columns are kept, not dropped.

Command line

Map a CSV, JSON or JSONL export from a shell, quarantine bad rows to JSONL, and ask how any header resolved.

Compile once

Resolve a source's headers into a mapping profile a single time, then reuse it for every row that follows.

🌍

40 languages

Alias tables for 40 languages generate on demand and cache locally, with bounded network behaviour while they build.

TS

Python and TypeScript

The Python package is canonical and owns the alias table; the npm package syncs it at build time.


// the schema

62 canonical fields, and one honest unknown.

Exposed as a string enum, so it drops straight into JSON without a conversion step.

first_namelast_namefull_namemiddle_namenicknameprefixsuffixemailphonehome_phonework_phonefaxwhatsappwebsitecompanyjob_titledepartmentindustryaddress_line1address_line2citystatepostal_codecountryfull_addresslinkedintwitterfacebookinstagramgithubyoutubetiktokdiscordtelegramlead_statuslifecycle_stageemail_opt_outtagssourceutm_parametersscoreownerbirthdayagecreated_atupdated_atlast_contactedrevenuecurrencymessagesubjectcompany_sizenotesmetadatagendertimezonelanguage_preferencereferrer_urlsource_idsource_servicesubscribedverifiedunknown

// how it compares

How RoloDexter compares

Where RoloDexter sits next to the tools and workarounds people already reach for on this exact problem.

{ }

Hand-written mapping code

A dict or regex per integration, extended by hand forever, that silently misroutes the next unexpected header.

OR

OpenRefine

A free GUI for cleaning one dataset by hand. Not a library, and no maintained cross-CRM alias table.

Zapier / Make field mapping

Hand-mapped fields inside one workflow step. No reusable alias table, no confidence scores, nothing callable from code.


// faq

Questions people ask

Is RoloDexter free to use?

Yes, RoloDexter is free, open-source software released under the MIT license. The Python package installs with pip install rolodexter and the JavaScript/TypeScript package installs with npm install rolodexter; both are maintained as one dual-package, MIT-licensed project.

What does RoloDexter do?

RoloDexter routes messy, inconsistent contact data from any CRM, form, or CSV export to one clean canonical schema. It resolves 600+ known field aliases to 62 canonical fields, like first_name, email, and phone, and returns a confidence score on every match, from exact hits down to heuristic guesses on unnamed columns.

How does RoloDexter figure out what a messy column name means?

Every incoming field is tried against four strategies in order of certainty: an exact lookup against the alias table (confidence 1.0), normalized matching that handles CamelCase and dot.path variants, fuzzy matching that catches typos like phne_nmbr, and a heuristic pass that detects emails, phones, and URLs by the shape of the value itself.

Does RoloDexter work with pandas DataFrames?

Yes. Calling map_dataframe() on a pandas DataFrame renames its columns to the canonical schema and normalizes the values in place, while columns RoloDexter doesn't recognize are kept rather than dropped. The same mapper also handles a single dict payload, a batch of payloads, or a streamed CSV/JSONL export in constant memory.

Does RoloDexter support languages other than English?

Yes. English ships by default, and alias tables for 40 languages, including Spanish, French, German, and Japanese, generate on demand and cache locally, so runtime loading is cache-only and never translates on the fly. Network behavior during generation is bounded with configurable timeouts, retries, and worker counts.

Does RoloDexter fail silently on bad data?

No, non-fatal issues, like a phone number that can't be normalized to E.164, come back as warnings on the result instead of disappearing silently. Enabling strict mode turns any low-confidence match or unparseable value into a loud failure instead of a quietly wrong row, and each warning carries a category for grouping instead of matching message text.

Does RoloDexter work offline?

Yes, matching and normalization for the default English alias table run entirely locally, with no network calls. Only generating alias caches for the other 40 supported languages needs a one-time network fetch, via the optional i18n-generate extra; once cached, those load and match offline too.

How is RoloDexter different from a no-code tool like Zapier?

Zapier and Make let you map one app's fields to another by hand, inside a single workflow step. RoloDexter is a library: call it from Python or TypeScript to resolve any header automatically against 600+ known aliases, with a confidence score on every match, no per-workflow mapping UI required.

// pick your ecosystem

Stop writing the same field map again.

Install it, hand it the payload nobody wants to look at, and read what comes back.