Branch8

How to Migrate Salesforce to HubSpot Without Data Loss

Matt Li
Matt Li
March 24, 2026
14 mins read
Technology
How to Migrate Salesforce to HubSpot Without Data Loss - Hero Image

Key Takeaways

Quick Answer

To migrate Salesforce to HubSpot without data loss, export all Salesforce objects as CSV files, map fields to HubSpot properties, deduplicate records, then import using HubSpot's native Salesforce migration tool or a middleware like Trujay. Validate record counts and field integrity at every stage.

Why Are Companies Moving from Salesforce to HubSpot in APAC?

The shift is largely about cost and operational complexity. According to HubSpot's 2024 ROI Report, companies that switched from Salesforce to HubSpot reported a 23% reduction in total cost of ownership over three years. For mid-market companies across Hong Kong, Singapore, and Australia — where regional teams often manage CRM administration without dedicated Salesforce admins — the operational overhead of Salesforce becomes disproportionate.

That said, this migration is not trivial. Salesforce and HubSpot model data differently. Salesforce uses a relational database structure with Accounts, Contacts, Opportunities, and custom objects linked by lookup relationships. HubSpot uses a flatter model with Companies, Contacts, and Deals connected through associations. Understanding this structural difference before you touch a single record is the difference between a clean migration and months of cleanup.

Global companies using Salesforce as their worldwide CRM often find that APAC subsidiaries — particularly in markets like Taiwan, Vietnam, and the Philippines — underutilize Salesforce's enterprise features. Moving these regional operations to HubSpot while maintaining data fidelity lets them right-size their CRM spend without losing historical intelligence.

What Should You Do Before Starting the Migration?

Audit Your Salesforce Data

Before exporting anything, run a full data audit. This step alone prevents the majority of migration failures.

Open Salesforce and navigate to Setup → Object Manager. For each object you plan to migrate, document:

  • Total record count
  • Number of custom fields versus standard fields
  • Lookup and master-detail relationships
  • Fields with picklist values (you'll need to recreate these in HubSpot)
  • Attachments and file storage volume

Run this SOQL query in the Salesforce Developer Console to get a count of all Contacts with associated Opportunities:

1SELECT COUNT(Id), Account.Name
2FROM Contact
3WHERE Id IN (SELECT ContactId FROM OpportunityContactRole)
4GROUP BY Account.Name
5ORDER BY COUNT(Id) DESC

This tells you which accounts carry the most relationship complexity and where data loss risks are highest.

Identify What NOT to Migrate

Not everything in Salesforce belongs in HubSpot. According to Validity's 2023 State of CRM Data Management report, roughly 25% of CRM records are duplicates or stale data. Migrating garbage into a new system just gives you expensive garbage.

Create exclusion criteria:

  • Contacts with no activity in the last 24 months
  • Leads that were never converted
  • Test records (filter for email domains like @test.com or @example.com)
  • Duplicate accounts (use Salesforce's built-in Duplicate Management rules or a tool like DemandTools by Validity)

Map Salesforce Fields to HubSpot Properties

This is where most teams underestimate the work. Create a spreadsheet with four columns:

  • Salesforce Field API Name (e.g., Account.BillingCountry)
  • Salesforce Field Type (e.g., Picklist, Text, Date)
  • HubSpot Target Property (e.g., company.country)
  • Transformation Required (e.g., "Map picklist values to HubSpot dropdown options")

HubSpot has specific property types: Single-line text, Multi-line text, Dropdown select, Number, Date picker, and Checkbox. Salesforce's multi-select picklist fields require special attention — HubSpot handles these as "Multiple checkboxes" properties, and the delimiter format differs.

For Salesforce multi-select values stored as Value1;Value2;Value3, HubSpot expects semicolons as well, but you must ensure no trailing spaces exist. This Python snippet handles the cleanup:

1import pandas as pd
2
3df = pd.read_csv('salesforce_contacts_export.csv')
4
5# Clean multi-select picklist fields
6def clean_multiselect(value):
7 if pd.isna(value):
8 return ''
9 return ';'.join([v.strip() for v in str(value).split(';')])
10
11df['Industry_Segments'] = df['Industry_Segments'].apply(clean_multiselect)
12
13# Standardize date formats to YYYY-MM-DD for HubSpot
14df['CloseDate'] = pd.to_datetime(df['CloseDate']).dt.strftime('%Y-%m-%d')
15
16df.to_csv('hubspot_contacts_import.csv', index=False)

Ready to Transform Your Ecommerce Operations?

Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.

How Do You Export Data from Salesforce Correctly?

Use Data Loader for Large Exports

Salesforce's built-in report export is limited to 2,000 rows in the browser. For any serious migration, use Salesforce Data Loader (the desktop app, not the web-based alternative) or Dataloader.io for cloud-based exports.

Export objects in this order to preserve relational integrity:

  1. Accounts (these become Companies in HubSpot)
  2. Contacts (linked to Companies via Company Name or domain)
  3. Opportunities (these become Deals in HubSpot)
  4. Tasks and Activities (these become Engagements in HubSpot)
  5. Notes and Attachments
  6. Custom Objects (these require HubSpot Custom Objects, available on Enterprise plans)

For each export, include the Salesforce Record ID (Id field). You'll need this as a reference key during validation.

In Data Loader, use this approach:

1Operation: Export
2Object: Account
3Fields: Id, Name, BillingStreet, BillingCity, BillingState,
4 BillingPostalCode, BillingCountry, Phone, Website,
5 Industry, AnnualRevenue, NumberOfEmployees, OwnerId,
6 CreatedDate, LastModifiedDate
7Filter: LastModifiedDate >= 2020-01-01T00:00:00Z

Export Attachments Separately

Salesforce stores files in ContentDocument and ContentVersion objects (Lightning) or the legacy Attachment object (Classic). These won't come through a standard CSV export.

Query ContentVersion for all files:

1SELECT Id, Title, FileExtension, ContentDocumentId,
2 FirstPublishLocationId, ContentSize
3FROM ContentVersion
4WHERE IsLatest = true
5ORDER BY ContentSize DESC

This gives you the total file volume. HubSpot's file storage limits vary by plan — the Professional plan includes 5GB of file storage according to HubSpot's 2024 pricing page. If your Salesforce org has 50GB of attachments, you'll need a strategy: migrate only attachments linked to active deals, or use an external storage service like AWS S3 with linked URLs in HubSpot.

How Do You Set Up HubSpot Before Importing?

Create Custom Properties First

Every custom field from Salesforce that doesn't have a standard HubSpot equivalent needs to be created as a custom property before import. Navigate to Settings → Properties in HubSpot and create properties for each object type.

If you have more than 20 custom properties, use the HubSpot API to batch-create them:

1import requests
2import json
3
4api_key = 'your-hubspot-private-app-token'
5headers = {
6 'Authorization': f'Bearer {api_key}',
7 'Content-Type': 'application/json'
8}
9
10properties_to_create = [
11 {
12 "name": "sf_account_tier",
13 "label": "Account Tier",
14 "type": "enumeration",
15 "fieldType": "select",
16 "groupName": "contactinformation",
17 "options": [
18 {"label": "Tier 1", "value": "tier_1"},
19 {"label": "Tier 2", "value": "tier_2"},
20 {"label": "Tier 3", "value": "tier_3"}
21 ]
22 },
23 {
24 "name": "sf_region",
25 "label": "APAC Region",
26 "type": "enumeration",
27 "fieldType": "select",
28 "groupName": "contactinformation",
29 "options": [
30 {"label": "Greater China", "value": "greater_china"},
31 {"label": "Southeast Asia", "value": "southeast_asia"},
32 {"label": "ANZ", "value": "anz"}
33 ]
34 }
35]
36
37for prop in properties_to_create:
38 response = requests.post(
39 'https://api.hubapi.com/crm/v3/properties/contacts',
40 headers=headers,
41 data=json.dumps(prop)
42 )
43 print(f"Created {prop['name']}: {response.status_code}")

Configure Deal Pipelines and Stages

Salesforce Opportunity Stages don't automatically map to HubSpot Deal Stages. Before importing deals, recreate your pipeline in Settings → Objects → Deals → Pipelines.

Document the mapping explicitly:

  • Salesforce "Prospecting" → HubSpot "Appointment Scheduled"
  • Salesforce "Qualification" → HubSpot "Qualified to Buy"
  • Salesforce "Needs Analysis" → HubSpot "Presentation Scheduled"
  • Salesforce "Closed Won" → HubSpot "Closed Won"
  • Salesforce "Closed Lost" → HubSpot "Closed Lost"

Stage probability percentages work differently between platforms. Salesforce assigns probability at the stage level; HubSpot uses deal-level forecasting. Plan for this behavioral difference in your sales team's workflows.

Ready to Transform Your Ecommerce Operations?

Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.

What Is the Step-by-Step Import Process?

Option 1: HubSpot's Native Import Tool

For datasets under 500,000 rows, HubSpot's built-in CSV import works reliably.

  1. Go to Contacts → Import (or Deals, Companies)
  2. Select "File from computer"
  3. Choose the object type
  4. Upload your cleaned CSV
  5. Map each CSV column to a HubSpot property
  6. Select your deduplication key (Email for Contacts, Domain for Companies)
  7. Run the import

Critical detail: set the import to "Update existing records and create new ones" rather than "Create new records only." If you've run a test import previously, this prevents duplicates.

Option 2: Middleware Migration Tools

For complex migrations involving more than three object types or requiring association preservation, use a dedicated tool:

  • Trujay (now Import2) — Handles direct Salesforce-to-HubSpot transfers with field mapping UI. Pricing starts at approximately $500 for 10,000 records.
  • Insycle — Stronger for deduplication and ongoing data quality, useful if your Salesforce data is messy.
  • HubSpot's Salesforce Integration — This can sync data bidirectionally, which some teams use as a temporary bridge during migration. However, it's designed for ongoing sync, not one-time migration, and can create confusion if left running after the migration.

Option 3: Custom API Migration

For Enterprise-level migrations with custom objects, workflows, and complex associations, a scripted API migration gives you the most control.

Here's a Python script that migrates Salesforce Accounts to HubSpot Companies using both APIs:

1from simple_salesforce import Salesforce
2import requests
3import json
4import time
5
6# Connect to Salesforce
7sf = Salesforce(
8 username='your-sf-username',
9 password='your-sf-password',
10 security_token='your-sf-token',
11 domain='login' # Use 'test' for sandbox
12)
13
14# HubSpot setup
15hs_token = 'your-hubspot-private-app-token'
16hs_headers = {
17 'Authorization': f'Bearer {hs_token}',
18 'Content-Type': 'application/json'
19}
20
21# Query Salesforce Accounts
22accounts = sf.query_all(
23 "SELECT Id, Name, Website, Phone, BillingCountry, "
24 "Industry, AnnualRevenue, NumberOfEmployees "
25 "FROM Account WHERE IsDeleted = false"
26)
27
28migration_log = []
29
30for record in accounts['records']:
31 hubspot_company = {
32 "properties": {
33 "name": record.get('Name', ''),
34 "domain": record.get('Website', ''),
35 "phone": record.get('Phone', ''),
36 "country": record.get('BillingCountry', ''),
37 "industry": record.get('Industry', ''),
38 "annualrevenue": str(record.get('AnnualRevenue', '')),
39 "numberofemployees": str(record.get('NumberOfEmployees', '')),
40 "salesforce_id": record['Id'] # Custom property for reference
41 }
42 }
43
44 response = requests.post(
45 'https://api.hubapi.com/crm/v3/objects/companies',
46 headers=hs_headers,
47 data=json.dumps(hubspot_company)
48 )
49
50 migration_log.append({
51 'sf_id': record['Id'],
52 'sf_name': record['Name'],
53 'hs_status': response.status_code,
54 'hs_id': response.json().get('id', 'FAILED')
55 })
56
57 # Respect HubSpot API rate limits (100 requests per 10 seconds)
58 time.sleep(0.15)
59
60# Save migration log for validation
61import csv
62with open('migration_log.csv', 'w', newline='') as f:
63 writer = csv.DictWriter(f, fieldnames=migration_log[0].keys())
64 writer.writeheader()
65 writer.writerows(migration_log)
66
67print(f"Migrated {len(migration_log)} accounts")
68print(f"Failed: {sum(1 for r in migration_log if r['hs_status'] != 201)}")

Always store the Salesforce Record ID as a custom property in HubSpot. This creates a lookup key for validating the migration and is essential for troubleshooting.

How Do You Validate That No Data Was Lost?

Validation is non-negotiable. A migration without validation is just a data copy with wishful thinking.

Record Count Comparison

Compare record counts across every object type:

1# Salesforce counts
2sf_accounts = sf.query("SELECT COUNT(Id) FROM Account WHERE IsDeleted = false")
3sf_contacts = sf.query("SELECT COUNT(Id) FROM Contact WHERE IsDeleted = false")
4sf_opps = sf.query("SELECT COUNT(Id) FROM Opportunity WHERE IsDeleted = false")
5
6# HubSpot counts (using search API)
7def get_hs_count(object_type):
8 response = requests.post(
9 f'https://api.hubapi.com/crm/v3/objects/{object_type}/search',
10 headers=hs_headers,
11 data=json.dumps({"filterGroups": [], "limit": 0})
12 )
13 return response.json()['total']
14
15print(f"Accounts: SF={sf_accounts['records'][0]['expr0']} | HS={get_hs_count('companies')}")
16print(f"Contacts: SF={sf_contacts['records'][0]['expr0']} | HS={get_hs_count('contacts')}")
17print(f"Opportunities: SF={sf_opps['records'][0]['expr0']} | HS={get_hs_count('deals')}")

Field-Level Spot Checks

Randomly sample 50 records from each object. For each, compare every field value between the Salesforce export CSV and the HubSpot record. Focus on:

  • Currency fields (Salesforce supports multi-currency; HubSpot uses a single currency per portal unless you're on Enterprise)
  • Date fields (timezone handling differs — Salesforce stores in UTC, HubSpot displays in portal timezone)
  • Rich text fields (Salesforce HTML formatting may not render identically in HubSpot)

Association Verification

Check that Contacts are still associated with their correct Companies, and Deals are linked to the right Contacts and Companies. This is where most migrations silently fail.

Pull a sample of HubSpot associations via the API and cross-reference against your Salesforce export:

1# Verify contact-company associations
2contact_id = '12345' # Sample HubSpot contact ID
3response = requests.get(
4 f'https://api.hubapi.com/crm/v4/objects/contacts/{contact_id}/associations/companies',
5 headers=hs_headers
6)
7associations = response.json()
8print(f"Contact {contact_id} is associated with companies: {associations}")

Ready to Transform Your Ecommerce Operations?

Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.

What Are the Common Pitfalls to Avoid?

Losing Activity History

Salesforce Tasks and Events don't have a direct 1:1 equivalent in HubSpot. HubSpot uses Engagements (calls, emails, meetings, notes, tasks). When migrating activities, you must map Salesforce Task.Type to the correct HubSpot engagement type.

According to HubSpot's developer documentation, engagement timestamps require Unix millisecond format — not ISO 8601. This single format difference accounts for a disproportionate number of "missing" activities that were actually imported with a date of January 1, 1970.

Ignoring Owner Mapping

Salesforce uses User IDs for record ownership. HubSpot uses Owner IDs. Before importing, create all users in HubSpot and build a mapping table:

  • Salesforce User ID 005xx000001abc → HubSpot Owner ID 12345678

Replace all OwnerId values in your export files before importing.

Underestimating Custom Object Complexity

HubSpot Custom Objects (available on Enterprise plans) can replicate Salesforce custom objects, but they have limitations: a maximum of 10 custom objects and 500,000 records per object, according to HubSpot's 2024 product limits documentation. If your Salesforce org relies heavily on custom objects with millions of records, this constraint requires architectural decisions before migration.

A Real Migration: What We Learned Moving 430,000 Records

In late 2023, Branch8 migrated a Singapore-based fintech company from Salesforce Sales Cloud to HubSpot Sales Hub Enterprise. The Salesforce org contained approximately 430,000 Contacts, 85,000 Accounts, and 12,000 Opportunities across three pipelines, plus two custom objects tracking regulatory compliance interactions specific to MAS (Monetary Authority of Singapore) requirements.

The migration took six weeks end-to-end. We used a combination of Python scripts with the simple-salesforce library (v1.12.4) for extraction and the HubSpot v3 API for loading. The critical challenge was preserving the relationship between compliance interaction records (a Salesforce custom object) and their associated Contacts and Accounts. We solved this by migrating in strict dependency order and using the stored Salesforce IDs as lookup keys to rebuild associations in HubSpot.

The validation phase caught 1,247 Contact records where phone number formatting had been mangled — Salesforce stored them with country codes (e.g., +65 9123 4567) while our cleaning script had inadvertently stripped the + prefix. We caught this through field-level spot checks on the third day of validation. Without that step, the sales team would have discovered the issue mid-call — a bad first impression for the new CRM.

Total downtime for the sales team was two days (over a weekend), during which both systems were frozen to prevent divergent data.

Ready to Transform Your Ecommerce Operations?

Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.

What Should You Do After the Migration?

Run Parallel Operations for Two Weeks

Keep Salesforce accessible (read-only) for at least 14 days post-migration. Sales reps will need to reference historical context that they remember being "somewhere in Salesforce" but can't immediately find in HubSpot.

Retrain Your Team on HubSpot Conventions

Salesforce users will instinctively search for "Accounts" — in HubSpot, they're "Companies." "Opportunities" are "Deals." These aren't just label differences; the underlying workflows and automation logic differ. Budget time for training.

Monitor Data Quality Weekly

Use HubSpot's data quality tools (under Settings → Properties → Data Quality) to flag incomplete records, formatting inconsistencies, and potential duplicates that may have slipped through. Schedule a weekly review for the first month.

Gartner's 2023 research estimated that poor data quality costs organizations an average of $12.9 million per year. A migration is your one opportunity to start clean — invest the time in post-migration hygiene.

If your team is planning a Salesforce-to-HubSpot migration across APAC markets and needs hands-on technical execution — from data mapping through API scripting and validation — reach out to Branch8. We've done this across Hong Kong, Singapore, and Australia, and we know where the edge cases hide.

Sources

  • HubSpot ROI Report (2024): https://www.hubspot.com/roi
  • Validity State of CRM Data Management (2023): https://www.validity.com/resources/state-of-crm-data-management/
  • HubSpot Developer Documentation — Engagements API: https://developers.hubspot.com/docs/api/crm/engagements
  • Salesforce Data Loader Documentation: https://developer.salesforce.com/docs/atlas.en-us.dataLoader.meta/dataLoader/
  • HubSpot Product Limits and Usage Guidelines: https://legal.hubspot.com/product-specific-terms
  • Gartner Data Quality Market Survey (2023): https://www.gartner.com/en/newsroom/press-releases/2023-data-quality
  • simple-salesforce Python Library: https://github.com/simple-salesforce/simple-salesforce

FAQ

For a mid-market company with 100,000 to 500,000 records, expect four to eight weeks including planning, data cleaning, migration, and validation. The timeline extends if you have custom objects, complex automations, or multi-currency setups that require architectural decisions in HubSpot.