Contact List Cleaner: Fix Phones, Emails & Names, Dedupe avatar

Contact List Cleaner: Fix Phones, Emails & Names, Dedupe

Pricing

from $1.20 / 1,000 contact row cleaneds

Go to Apify Store
Contact List Cleaner: Fix Phones, Emails & Names, Dedupe

Contact List Cleaner: Fix Phones, Emails & Names, Dedupe

Clean a contact list: fix name case, tidy emails (syntax and typos), format and validate phone numbers, then remove duplicates. Takes JSON, CSV or a dataset. Offline logic: no mailbox, DNS or phone-line checks. Pay per contact row.

Pricing

from $1.20 / 1,000 contact row cleaneds

Rating

0.0

(0)

Developer

Data Done Right

Data Done Right

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Security

Limited permissions

Share

Contact List Cleaner: Fix Phones, Emails & Names, Remove Duplicates

Contact List Cleaner fixes phone numbers, emails and names in a contact list and removes duplicate contacts. Give it a JSON list, a CSV link or an Apify dataset; get back one tidy row per contact with +E.164 phone numbers, corrected name capitalisation, cleaned email addresses and a clear list of what was fixed and what is still wrong.

What this does NOT do. It does not check that an email mailbox exists (no DNS lookup, no SMTP, nothing is sent), does not check that a phone line is connected or who owns it, does not enrich contacts, and does not find fuzzy or misspelled duplicates (matches are exact on a normalised key: email, phone, or name plus company). Name splitting is a rule-based heuristic for Western name order; check non-Western names yourself. For mailbox-level email checks use Verify Email Addresses in Bulk: Syntax, MX & Disposable.

What you get

  • Names: JANE DOE becomes Jane Doe, doe, jane becomes Jane Doe, van/de stay lower case, O'Brien and McDonald are handled, titles (Dr) and suffixes (Jr, III) are split out. Mixed-case names are never touched.
  • Emails: lower-cased, extracted from Name <a@b.com> or mailto:, syntax-checked (international domains work), role accounts flagged (info@), known domain typos suggested (gmial.com to gmail.com).
  • Phones: parsed with Google's libphonenumber port (phonenumbers 9.0.41), written as E.164, international or national format, with validity, line type (mobile, fixed line, toll free) and country.
  • Duplicates: removed or flagged, the most complete row is kept and can be filled from its duplicates.
  • Your other columns (id, notes, tags) travel along untouched.

How to clean a contact list

  1. Paste contacts into Contacts as a JSON list of objects, or set CSV file URL, or pick a Dataset. Sources are combined.
  2. Column names are detected (name, Full Name, email, Work Email, phone, Mobile, company, First Name, Last Name and similar). If detection is wrong, set the column mapping fields.
  3. Set Default country (for example GB) so numbers written without +44 are read correctly.
  4. Pick how duplicates are found and whether to remove or flag them, then press Start.
  5. Download the dataset as CSV, Excel or JSON. Views: Overview, Phone details, Duplicates. The SUMMARY record has the counts.

How to format phone numbers to E.164 in bulk

Put the numbers in a phone column (or any column and name it in Phone column). Set Default country. Valid numbers come back in phone (in your chosen format) and always in phoneE164. Invalid numbers keep their original text, have phoneValid: false, and an entry in issues telling you why (wrong length, or not valid in that country's numbering plan). A cell with two numbers separated by ; keeps the first as the phone and the rest in extraPhones.

How to remove duplicate contacts from a list

Choose Find duplicates by:

  • email (default): same address after lower-casing; for Gmail also ignoring dots and +tags; +tags are also ignored for Outlook, Hotmail, Live, iCloud and Proton.
  • phone: same valid number in E.164 form. Shared office lines can merge different people, which is why this is not the default.
  • emailOrPhone, nameAndCompany, any: matches chain, so A=B by email and B=C by phone puts all three in one group.
  • none: only clean, no duplicate search.

With What to do with duplicates = remove only the most complete row of each group is delivered; with flag every row is delivered with isDuplicate and duplicateOf. Fill gaps from duplicates copies a missing valid email, phone, name or company from a duplicate to the kept row (listed in filledFromDuplicates).

How to fix names and emails in a CRM export

Send the export as CSV or paste the rows. fullName, firstName, lastName, prefix and nameSuffix are filled; changes lists name-recased, name-reordered, email-lowercased, email-extracted, phone-formatted; issues lists problems such as email-invalid: needs exactly one @, email-domain-possible-typo: did you mean ..., phone-invalid: ..., name-contains-digits. Domain typos are only suggested unless you switch on Auto-correct email domain typos.

Input

{
"contacts": [
{"name": "JANE DOE", "email": "Jane.Doe@GMAIL.com", "phone": "(415) 555-2671", "company": "Acme Inc."},
{"name": "doe, jane", "email": "<jane.doe@gmail.com>", "phone": "", "company": "Acme Inc"},
{"name": "mcdonald, ronald a. jr", "email": "info@mcdonalds-example.con", "phone": "+44 20 7946 0958", "company": "McD Ltd"}
],
"defaultCountry": "US",
"phoneFormat": "e164",
"dedupeBy": "email",
"duplicates": "remove",
"maxContacts": 1000
}
FieldDefaultWhat it does
contacts6 sample rowsJSON list of objects, one per contact.
csvUrlemptyPublic CSV link (header row, up to 25 MB). Internal/private addresses are blocked.
datasetIdemptyApify dataset; each item is one contact.
nameColumn, firstNameColumn, lastNameColumn, emailColumn, phoneColumn, companyColumnauto-detectOverride the detected column (case and punctuation are ignored).
defaultCountryUSISO country for numbers without +.
phoneFormate164e164, international or national.
fixNameCasetrueRecase ALL CAPS / all lower case names.
fixEmailTyposfalseApply known domain typo fixes (otherwise only suggested).
dedupeByemailemail, phone, emailOrPhone, nameAndCompany, any, none.
duplicatesremoveremove or flag.
mergeDuplicatestrueFill gaps of the kept row from its duplicates.
maxContacts1000Cap on non-empty rows processed and charged (1 to 200,000).

Output

Real output of the sample input (the input has 6 rows: 1 blank, 5 contacts; shown here are two of the 4 delivered rows, Jane Doe merged with her duplicate):

{
"rowNumber": 1, "firstName": "Jane", "lastName": "Doe", "fullName": "Jane Doe",
"email": "jane.doe@gmail.com", "emailValid": true, "emailDomain": "gmail.com", "emailIsRole": false,
"phone": "+14155552671", "phoneE164": "+14155552671", "phoneValid": true, "phoneType": "fixed_line_or_mobile", "phoneCountry": "US",
"company": "Acme Inc.", "issues": [], "changes": ["name-recased", "email-lowercased", "phone-formatted"],
"isDuplicate": false, "duplicatesFound": 1, "duplicateRows": [2], "filledFromDuplicates": []
}
{
"rowNumber": 3, "firstName": "Ronald", "middleName": "A.", "lastName": "McDonald", "nameSuffix": "Jr", "fullName": "Ronald A. McDonald Jr",
"email": "info@mcdonalds-example.con", "emailValid": true, "emailIsRole": true, "emailSuggestion": "info@mcdonalds-example.com",
"phone": "+442079460958", "phoneValid": true, "phoneType": "fixed_line", "phoneCountry": "GB", "company": "McD Ltd",
"issues": ["email-domain-possible-typo: did you mean info@mcdonalds-example.com"],
"changes": ["name-reordered", "name-recased", "phone-formatted"]
}

(Shortened: unchanged null fields are left out of this README; the real rows contain every field.) Your own extra columns are added to each row under their own names; a column that collides with an output field is renamed input_<name>. emailValid: true means the syntax is valid; a flagged typo domain can still be syntactically valid.

How much does it cost

Pay per event: $0.0015 per contact row cleaned (event contact-row-cleaned, $1.50 per 1,000). A row is charged when it is processed, including rows later removed as duplicates; completely empty rows are free. Lower prices apply on higher Apify plans.

RowsCost
1,000$1.50
10,000$15.00
100,000$150.00

maxContacts caps the spend, and the Apify run limit is respected: the run stops and says "spending limit" in its status.

Use cases

  • Clean a CRM or spreadsheet export before importing it into HubSpot, Salesforce or a mailing tool.
  • Convert mixed-format phone numbers to E.164 for an SMS or dialer import.
  • Merge duplicate signups from several forms into one row per person.
  • Prepare scraped lead lists: tidy names, drop invalid emails and phones, keep source columns.
  • Add a cleaning step in an n8n, Make or Zapier flow, or let an AI agent call it through MCP.

FAQ

Does it verify that an email exists? No. It checks syntax, flags role accounts and suggests fixes for known domain typos. No DNS or mailbox check is made, nothing is sent.

Does phoneValid: true mean the number is in use? No. It means the number fits a real numbering plan (libphonenumber rules, version 9.0.41 at build time). It can still be disconnected. The library's data lags new number ranges.

How are names handled? Names that are ALL CAPS or all lower case are recased token by token; mixed-case tokens are left alone. Last, First with a comma is swapped; Smith John without a comma is not (it cannot be told apart from a real name). First name is the first word, last name the last word plus particles like van.

Can it find near-duplicates like Jon and John? No, matching is exact on normalised keys. Use the nameAndCompany mode for same name and company after case, accent and company-suffix (Inc, Ltd, GmbH) normalisation.

Which row is kept among duplicates? The row with the most usable data (name, valid email, valid phone, company); the earlier row wins ties.

What if my CSV link is private? Public links only. Private or internal addresses are blocked and the run notes it in SUMMARY.

Use with the Apify API / MCP / n8n / Make

from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("<username>/contact-list-cleaner").call(run_input={
"contacts": [{"name": "JANE DOE", "email": "Jane@Acme.com", "phone": "(415) 555-2671"}],
"defaultCountry": "US",
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["fullName"], item["email"], item["phoneE164"])
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<APIFY_TOKEN>' });
const run = await client.actor('<username>/contact-list-cleaner').call({
contacts: [{ name: 'JANE DOE', email: 'Jane@Acme.com', phone: '(415) 555-2671' }],
defaultCountry: 'US',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

In n8n or Make use the Apify node/module "Run Actor" with the same input and read the dataset. AI agents can call it through the Apify MCP server.

Data and credits

Phone parsing uses the open-source phonenumbers package (Apache 2.0, a port of Google's libphonenumber). No external data source or list is downloaded at run time except the CSV you point to.

  • Remove Duplicates: Deduplicate Datasets, CSV & Excel: generic duplicate removal on any dataset.
  • Phone Number Validation & Lookup: Format, Carrier, Line Type: phone-only validation with carrier and time zones.
  • Verify Email Addresses in Bulk: Syntax, MX & Disposable: MX and disposable-domain checks for the emails you keep.
  • Email Extractor from Text: Emails, Phones & URLs from Any Text: pull contacts out of raw text first.