On a Salesforce to HubSpot migration, the duplicates you end up with are not the ones Salesforce showed you. HubSpot deduplicates contacts automatically on the Email property and companies on Company domain name, and that is the whole of it. Deals, tickets and custom objects are not deduplicated automatically at all; they match only on Record ID or on a custom unique property you set up first. So a contact with no email, or with a second email, becomes a second contact, and an account written two ways with no website becomes two companies.
That is why the dedupe work belongs on the CSV, in the days before the import, rather than in HubSpot afterwards. A merge in HubSpot is applied to live records that already have owners, activity and workflow enrollments. A merge on the export is a line in a file that you approve or reject, and the file is still just a file.
What HubSpot actually matches when you import
HubSpot documents its matching plainly, and it is narrower than most migration plans assume. Two properties dedupe by themselves. Everything else has to be told what to match on.
| Object | Deduplicated automatically on | Also available to you |
|---|---|---|
| Contacts | Record ID, or a custom unique value property | |
| Companies | Company domain name (the primary value) | Record ID, or a custom unique value property |
| Deals | Nothing | Record ID, or a custom unique value property |
| Tickets | Nothing | Record ID, or a custom unique value property |
| Products and custom objects | Nothing | Record ID, or a custom unique value property |
HubSpot allows up to ten custom properties per object that require unique values. On an import that carries more than one unique property, Record ID supersedes the others.
Read the first column again with a Salesforce export in mind. Salesforce is happy to hold two contacts with the same name at the same account, one entered by a rep with a work address and one loaded from a list with the email field empty. Salesforce never forced those two together, and HubSpot will not either, because it has nothing to compare. The pass that does compare them is ordinary fuzzy matching on name and normalized company name, run on the file before it goes anywhere.
The Salesforce rows that become duplicate HubSpot contacts
Here is a six row contact export, the kind a Data Loader pull produces, run through our engine exactly as written. Each row also carried its Salesforce Contact ID, which is left out below only to keep the table readable.
| Row in the Salesforce export | What HubSpot does on import | What the pass proposes | |
|---|---|---|---|
| Dana Reed, Northwind Traders | DANA.REED@Northwind.com, with a trailing space | Creates the contact | Keep, email trimmed and lowercased |
| Dana Reed, Northwind Traders Inc | empty | Creates a second contact, nothing to match on | Close name and company match, likely |
| Priya Raghunathan, Cedar Point Systems | praghu@cedarpoint.io | Creates the contact | Keep |
| Priya Raghunathan, Cedar Point Systems LLC | empty | Creates a second contact | Close name and company match, likely |
| Tom Alvarez, Acme Corp | tom@acme.com | Creates the contact | Keep |
| Tom Alvarez, Acme Corp | t.alvarez@acme.com | Creates a second contact | Left as two people on purpose |
The last pair is deliberate. Two different addresses at one company are not proof of one person, and a large account has plenty of people who share a name, so those rows stay as two rows and the decision is yours rather than the tool.
The same run also proposed the formatting fixes the import would otherwise carry through: the trailing space and capitals in DANA.REED@Northwind.com folded to dana.reed@northwind.com, Created Date values of 03/04/2026 and 4 Mar 2026 both resolved to 2026-03-04, and Mailing State values of mass, Massachusetts and tex turned into MA and TX. Those are not cosmetic on a migration. A list segmented on state silently misses every contact whose state was spelled out, and the CRM specific version of that whole pass is CRM data cleansing.
How to dedupe the Salesforce export before the HubSpot import
- 1 Export from Salesforce with the Data Loader or the weekly export service, and keep the 18 character Record ID. Both of those tools return the 18 character form, which is case insensitive and safe to join on in a spreadsheet. The 15 character ID you see in the browser URL is case sensitive, so two IDs that differ only in capitalization collapse into one the moment Excel or a lookup touches them.
- 2 Drop the Contact Owner and Created By columns, or move them well away from the name fields. They hold the name of a rep, and a matcher that reads a person name column keeps two copies of one contact apart simply because two different reps created them.
- 3 Load the CSV into the browser demo. The file is parsed and matched inside your own tab, so a customer list does not leave the machine while you are still deciding what to do with it.
- 4 Work the duplicate clusters first. Each one states why the rows matched, gives a confidence, and marks the most complete row to keep. Reject anything borderline: a duplicate that survives into HubSpot can still be merged later, while a wrong merge on the export quietly deletes a real person before anyone sees them.
- 5 Approve the formatting proposals, paying attention to the dates. A US month first reading and a day first reading of 03/04/2026 produce two different records, and renewal dates that are wrong by a month are the kind of thing nobody notices until a renewal is missed.
- 6 Export the cleaned file and the change log, then import. Reconcile before you call it done: rows imported plus rows merged in the log should equal the rows you pulled out of Salesforce, and the log is keyed on Record ID so any row can be traced back.
The wider cutover version of this plan, for ERP moves as well as CRM ones, is in data migration cleansing software. If you are moving in the other direction, the matching rules on the Salesforce side are covered in HubSpot to Salesforce migration. If your marketing automation is moving to HubSpot at the same time, the Marketo side is in Marketo to HubSpot migration, and the Pardot side, where one email can belong to several prospects, is in Pardot to HubSpot migration. If you are staying put and cleaning what is already there, the two system specific pages are Salesforce deduplication and HubSpot deduplication. Duplicates that already made it into the portal are merged two at a time, as set out on HubSpot merge contacts.
Does HubSpot merge duplicates automatically on import?
Only on an exact match of one property. Contacts merge into an existing record when the Email matches, companies when the Company domain name matches, and other objects only when you map Record ID or a custom unique property. Anything that differs by a spelling, a suffix or an empty email field is imported as a new record instead.
HubSpot does surface likely duplicates afterwards in its duplicate management tool, and you can review and merge them there. The catch is the one every CRM shares: once a merge is applied to live records it cannot be reversed, and by then the records carry owners, workflow enrollments and reporting history that the merge redistributes.
How do I avoid duplicate companies when importing into HubSpot?
Give every company row a clean domain and let HubSpot match on it. Company domain name is the only property HubSpot deduplicates companies on automatically, so derive it from the Salesforce Website field, strip the protocol and any www prefix, and fill the blanks by hand before the import rather than after.
Then look at the rows that still have no domain, because those are the ones that will double. Acme Corp and Acme Corporation with no website is two companies in HubSpot, every time. Normalizing the legal suffix and matching the names side by side is what brings that pair up while it is still a row in a file, and it is the same matching problem as entity resolution on any customer master.
What to do with the Salesforce Record IDs after the move
Keep them. Create a custom text property on the HubSpot contact and company objects, mark it as requiring unique values if you want it to dedupe future imports, and load the 18 character Salesforce ID into it. It costs one column and it is the only thing that lets anyone answer, months later, which HubSpot record a given Salesforce report line became.
That question arrives more often than migration plans expect, usually from finance or from whoever owns the reporting that changed shape after cutover. Teams running several systems handle the general form of it with data lineage tooling that tracks where each field came from, but for one CRM move a retained ID column and the change log from the cleaning pass answer it well enough.
When a sync tool is the better buy than a file pass
If duplicates keep arriving because forms, lead lists and an ads integration all write into HubSpot every day, a one time file pass is the wrong shape of tool. What you want is something connected to the portal that merges on a schedule. Insycle and Dedupely both do that, and we compare each of them honestly, including where they beat us, on the Insycle alternative and Dedupely alternative pages.
A migration is the other case. It is one file, one deadline, and a decision someone has to sign off, and the thing that matters is being able to show what was merged and why. DataUntangler does not connect to Salesforce or HubSpot and does not claim to. It takes the export, proposes every duplicate cluster and every format fix as a change you approve or reject, and hands back the cleaned file with a CSV log of what you decided. The Analyst plan covers unlimited files of up to about 100,000 rows each, and the data cleaning tool itself runs in the browser if you want to put the real export through it before you commit to anything.