The migration looks finished. The record counts line up on both sides, the sales team can find their accounts, and somebody sends a message saying the new portal is live. Three weeks later a VP builds a pipeline velocity report and every deal in it appears to have been created on the same Tuesday in March.

Nothing failed. No import errored. The counts still match. What happened is that a migration was scoped as a data transfer when it is really a field mapping exercise, and the fields that describe when and how a record got to its current state are the ones that quietly do not come across.

This guide is about the difference between the two. What actually moves into HubSpot object by object, the five things that do not, why HubSpot's import tool reports success on rows it only partly loaded, and how to tell whether your migration is a spreadsheet job or a build. It sits alongside the broader guide to HubSpot integrations, focused on the one-time move rather than the ongoing connection.

In this article

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

What HubSpot Data Migration Services Actually Cover

Almost every migration that goes wrong went wrong because someone bought workstream four and assumed they had bought all five.

  • 1. Audit the source

    Not a record count. A field-level inventory: which properties are actually populated, which are populated but meaningless, which are formula fields that will need rebuilding, how many duplicates exist under how many spellings, and which objects have relationships that matter. Most legacy CRMs contain a large number of fields that nobody has written to in three years, and migrating them is how a clean portal gets born dirty.

  • 2. Map every field, and decide what is dropped

    The deliverable is a document with one row per source field and a decision against each: destination property, transformation, or deliberately discarded. The discarded column is the important one, because it is the only artefact that distinguishes a considered decision from an oversight when somebody asks in November where a field went. This is the same discipline covered in the guide to data mapping, applied once rather than continuously.

  • 3. Configure the destination first

    Every destination property, dropdown option, pipeline, stage, custom object and association label has to exist in HubSpot before the data arrives. Skip this and the import still runs. It just lands with values missing, which is a far worse outcome than a failure, because a failure gets fixed and a gap gets discovered.

  • 4. Load the data

    The part everyone pictures, and typically the shortest phase. For standard objects with clean identifiers it is a sequence of imports in dependency order. For anything involving attachments, activity history or custom objects it is API work.

  • 5. Reconcile and prove it

    Counts per object, sums of key numeric fields, spot checks on association integrity, and an explicit report of every row and field that did not land. Without this phase there is no difference between a migration that worked and a migration that appeared to, and you will not learn which one you had until someone builds a report on it.

A migration is finished when you can prove what arrived, not when the records appear. Those are separated by weeks in most projects and by nothing at all in a well-scoped one.

What the Import Tool Will and Will Not Move

HubSpot's import tool is genuinely capable, and for a large share of migrations it is the right and only tool needed. The point of the table below is not that it is weak. It is that the boundary runs through the middle of things people assume are one thing.

What you are movingImport toolThe catch
Contacts, companies, deals, ticketsYesRequired properties differ per object. Deals need a deal name, a pipeline and a deal stage before a new record will create at all.
Custom objects and custom propertiesYesThe destination property has to exist first, with matching dropdown options.
Associations between recordsYesMulti-object imports need a shared unique identifier column, and activities must be associated with an object. Line items must be associated with deals.
Record ownersYesMatched against active users. An owner who is not yet a user in the portal is not an error, it is a blank.
Calls, emails, meetings, notes, tasksCreate onlyHubSpot states that existing emails, meetings, notes and tasks cannot be updated via import, regardless of how you import the data. One shot.
Deal create dateYesExplicitly mappable, which is the exception people generalise from and should not.
Time in stage, stage entry datesNoGenerated by HubSpot when a record moves. Not settable on import or by API.
Calculated, rollup and score propertiesNoRead-only by design. HubSpot derives them, so there is nothing to write to.
File attachmentsNoNot a property value. Files API plus an associated note, one file at a time.

Two rows in that table cause most of the pain, and they are the two that a spreadsheet-shaped mental model does not predict: stage history and attachments. Both are things a user of the old CRM would describe as being "on the record", and neither is a field.

Why Your Import Says Success and Your Data Is Wrong

This is the most important section in the guide, and it is a consequence of a design decision that is defensible and almost universally misread.

Read HubSpot's own list of import errors and the pattern is consistent enough to be a design philosophy. Partial success is preferred to total failure:

  • Could not find owner. The owner did not match an active user. Records import unassigned. This one is close to inevitable in a migration, because owners are users and users are usually provisioned by a different person on a different schedule than the data load.
  • Multiple owners found. Two users share a name. The owner property stays empty.
  • Invalid enumeration option. The value does not match an existing dropdown option. The record imports without that value.
  • Could not parse date and could not parse number. Format mismatch or stray characters. The value does not import, the record does.
  • Failed to create association. The records imported, they just are not associated with each other.
  • Association record not found. A unique identifier the association needed was missing.
  • Duplicate record ID and duplicate unique property value. Affected records are not updated or associated.
  • Missing required property. The one class that does reject the row outright.

Only the last of those stops a row. Everything above it produces a portal that looks migrated.

Every import error in the list is recoverable on the day and expensive in a month. The difference is entirely whether anyone reconciled.

The practical consequence is that "the import completed" is not a status worth reporting. The status worth reporting is the error file, per object, read line by line, with a named person deciding for each error class whether it is acceptable or a reload. Migrations that skip this do not discover the gaps. Their reports do, later, in front of an audience.

The Limits You Will Actually Hit

Volume is rarely the binding constraint, and the numbers are more generous than most people expect. The constraints that bite are concurrency and shape.

1,048,576 rowsthe maximum rows in a single import file on a paid account, with a 512 MB file size ceiling, up to 500 imports per day and 10,000,000 rows per day. Free accounts are held to 20 MB, 50 imports per day and 500,000 rows per day, which is the first thing to check if a trial portal is being used to rehearse.Format import files
3 at a timethe simultaneous import limit, of which at most 2 can be larger than 10,000 rows. This, not the daily row allowance, is what sets the wall clock on a migration weekend, because imports have to run in dependency order anyway: companies before contacts, deals before line items, records before the activities associated with them.Format import files
80,000,000 rowsthe daily ceiling through the CRM Imports API, against 10,000,000 through the UI. The API route is eight times the throughput and carries one significant trade-off: HubSpot notes that imports completed via API are not available when filtering records by import in views, lists, reports or workflows, which is exactly the filter a rollback wants.CRM API | Imports

That last trade-off deserves a moment, because it is the kind of detail that decides an architecture. Importing through the UI gives every record an import stamp you can filter on, which is the cheapest possible undo: if a load goes wrong, you can select everything from that import and remove it. Importing through the API gives you eight times the throughput and no such handle. On a migration of any consequence, the ability to reverse a bad load is worth more than the speed of a good one, so the usual answer is UI imports for the bulk objects and API only for what the UI cannot do at all, which is mainly attachments and anything needing per-record transformation.

A few smaller shape constraints that reliably surprise people: files must contain a single sheet and fewer than 1,000 columns, they need UTF-8 encoding for any non-English characters, and a single cell holding more than 1 MB of data will not process. The last one is not exotic. It is what happens when a legacy CRM's long-text notes field is exported into a spreadsheet column.

Deduplication Is Decided by the Columns You Map

There is no step in a HubSpot import where a dialog asks how you would like duplicates handled. The rules are fixed, they are applied silently, and which of them applies depends entirely on which columns are in your file.

The matching rules that run whether or not you chose them

  • Contacts match on email. Email is the recommended unique identifier, with Record ID as the fallback when emails are unavailable. Two source rows carrying the same email are one HubSpot contact, and the second row's values win where they are populated.

  • Companies match on domain. Same pattern, same consequence. Every subsidiary, franchise and regional office sharing a parent domain collapses into a single company record. For some businesses that is correct and tidy. For anyone selling to multi-site organisations it is a structural error that is painful to unpick afterwards.

  • Record ID beats everything. HubSpot is explicit that if Record ID is mapped during your import, it will supersede any other mapped unique identifiers. This is the single most consequential column in any migration file, and it is frequently included by accident because it came along in an export.

  • Duplicates inside one file are rejected rather than merged. Duplicate record IDs and duplicate unique property values are reported as errors, and the affected records are not updated or associated. Note the second half of that: not associated. A duplicate in the contacts file does not just skip a contact, it can silently sever the relationship that file was carrying.

  • Merges are one-way. Nothing above is reversible into its original halves. This is why identity decisions belong before the load and formatting decisions can wait until after it.

The version of this that costs real money involves two source systems. A company migrating from a legacy CRM plus a separate support tool has two customer lists, overlapping partially, with no shared identifier and inconsistent email hygiene across them. There is no import setting for that. Somebody has to decide, per record, which system is authoritative for which field, and encode that decision before either file is uploaded. That is a data engineering problem that happens to end in an import, and it is the point at which a migration stops being a spreadsheet job.

Salesforce to HubSpot Migration Is Not the Connector

A specific warning, because the wrong tool here is so plausible that it gets chosen deliberately.

The failure mode is delayed and specific. The connector is installed, records populate, everyone agrees it went well, and Salesforce is decommissioned on schedule. The following quarter someone tries to compare this year's win rate to last year's and finds that the pipeline only has depth from the install date onward. By then the source system is gone.

Running both systems in parallel is a legitimate long-term architecture and the connector is the right tool for it, which is covered in the HubSpot Salesforce integration guide. Switching is a different project with a different tool. The two get conflated because the same connector appears in both stories.

CSV Import vs iPaaS vs a Custom Migration

Three routes, and unlike most build-versus-buy questions in this category they genuinely do not overlap much. Most migrations use two of the three.

PickThe native import toolWhenStandard and custom objects, clean unique identifiers, no attachments, and activity history is either out of scope or a single clean load

Free, filterable, reversible by import, and generous enough on volume that a million rows is not the problem. If your source exports cleanly and your destination properties are configured first, this is the whole job, and paying anyone to do something more sophisticated is paying for sophistication you will not use. Start here and find out what it cannot do with your data rather than with someone else's.

PickAn iPaaS such as Make or ZapierWhenYou need light per-record transformation, or a trickle migration that runs alongside a live cutover

Useful for shaping records in flight and for keeping two systems aligned during a parallel-running period. The guide to iPaaS covers the category properly. The limit is that per-operation pricing turns hostile at migration volumes, and the tooling is built for ongoing flows rather than for a one-time load with a reconciliation report at the end.

Best fitPickA custom migrationWhenAttachments are in scope, multiple sources need deduplicating against each other, history has to be reconstructed, or the cutover cannot involve downtime

Written against the HubSpot APIs directly, so files can be uploaded and attached per record, transformations can be arbitrary rather than templated, failures retry rather than disappear into a per-field blank, both systems can stay correct during a parallel period, and the deliverable includes a reconciliation report that proves what arrived. This is also the only route where somebody is accountable for the fields that did not land.

Rate Limits to Know Before You Scope a Build

If any part of the migration is going through the API, the constraint is not the daily allowance. It is which endpoint you are forced to use per record.

100 per requestthe batch endpoint limit on the object APIs, and the number that makes API migrations feasible at all, because a batch of 100 counts as a single rate-limited request rather than 100. Any migration plan that reads as one API call per record is a migration plan that has not been costed.Using Object APIs
5 per secondthe CRM Search API burst limit, returning at most 200 records per response. Search is how you match an incoming record to an existing one when you do not hold its Record ID, so a migration that resolves identity by lookup is metered here, at roughly one four-hundredth of the batch write path. Resolve identity in your own staging layer, not in HubSpot.Increasing our API limits
190 per 10 secondsthe burst limit for private apps on Professional and Enterprise, against 650,000 calls per day on Professional and 1,000,000 on Enterprise. Generous for writes. The reason attachment migrations still take days is that files are one upload each, outside the batch path entirely.Increasing our API limits

The design consequence is worth stating plainly, because it separates people who have run one of these from people who have read about them. Bulk record loading is cheap: batches of 100, one request each, comfortably inside the burst limit. Identity resolution is expensive: 5 searches per second means 18,000 an hour, so resolving 200,000 records by search takes most of a day and starves everything else in the portal while it runs. Attachments are the most expensive of all, because each file is an upload followed by a note creation followed by an association, and none of it batches.

So the right architecture inverts the naive one. Stage the source data somewhere you control, resolve every identity there against a single export of HubSpot Record IDs, and then write in batches carrying the IDs you already resolved. Ask anyone quoting for a migration where they resolve identity. If the answer is the Search API, the timeline is wrong.

How to Run the Migration

  1. 1

    Freeze the schema before you export

    Agree that no new fields, pipelines or dropdown options will be created in either system until cutover. Migrations that run against a moving destination fail their own reconciliation, and the failure is unfalsifiable because nobody can say what the correct number was.

  2. 2

    Export everything, including what you plan to discard

    Take a complete export of the source and archive it somewhere durable before touching anything. The discarded fields are cheap to keep now and impossible to retrieve after the old system is decommissioned. This single step has saved more migrations than any tool.

  3. 3

    Build the mapping document and get it signed off

    One row per source field, one decision per row, including the deliberate discards. Have the people who use the data approve it, not just the people moving it. Half the disputed fields in any migration are disputed because a team was never asked.

  4. 4

    Configure the destination portal completely

    Properties, dropdown options with values that exactly match the source, pipelines, stages, custom objects, association labels, and every user provisioned so owner assignment resolves. Provisioning users last is the most common cause of a portal full of unassigned records.

  5. 5

    Rehearse in a sandbox with real data

    Not a sample of ten clean rows. A representative extract including the ugly records: the contact with three emails, the company with an apostrophe, the deal in a stage that no longer exists, the note containing 1.2 MB of pasted email thread. The rehearsal exists to generate the error file, and a clean rehearsal has told you nothing.

  6. 6

    Load in dependency order

    Companies, then contacts, then deals and tickets, then line items, then activities, then attachments. Associations depend on both sides existing, so an out-of-order load produces exactly the association errors HubSpot reports as successes with missing links.

  7. 7

    Read every error file, then reconcile

    Counts per object, sums of the numeric fields anyone reports on, association spot checks, and a written list of what did not land and why. Then have someone from the sales team open twenty records they know well and tell you what looks wrong. They will find things no count catches.

  8. 8

    Keep the old system readable for a quarter

    Not live, not synced, just readable. The questions that expose a migration gap arrive on a reporting cycle, which means the first serious test is a month after go-live and the second is at quarter end. Decommissioning before both have passed converts a fixable gap into permanent data loss.

What HubSpot Migration Services Cost

Nobody in this market publishes a price, and the reason is that the honest answer is a range so wide it looks evasive. It is worth explaining what actually moves the number, because it is not what buyers usually ask about.

Record volume barely matters. The import tool handles a million rows per file and batch endpoints move 100 records per request, so the difference between 20,000 contacts and 400,000 contacts is an afternoon. What multiplies the cost is the number of distinct shapes of work: each custom object is its own mapping exercise, each additional source system is a deduplication problem against every other source, activity history is a separate load with its own required fields and no second chance, and attachments are a per-file API job that does not batch.

Ask what a migration costs and you will be quoted for the load. Ask what the reconciliation report contains and you will find out whether you are buying a migration at all.

The five things worth itemising separately in any quote, because a cheap quote is usually cheap by omission rather than by efficiency: the mapping document, the sandbox rehearsal, the production load, the reconciliation report, and the ownership window afterwards. That last one is the one people forget to buy. Migration problems surface on a reporting cycle rather than on the day, and a fixed-scope engagement that ends at go-live ends several weeks before the first real test.

That is how we price it. StackTie builds the migration against the HubSpot APIs for a fixed fee, with the mapping document and the reconciliation report as deliverables rather than as extras, then maintains the connections that outlive the migration on a flat monthly retainer. The build fee and the retainer are published on the pricing page rather than quoted per call. If the work turns out to be a straightforward import that you can run yourself, we will tell you that, because a migration that does not need a build is the cheapest good outcome available to you.

Moving to HubSpot and unsure what survives the trip?

If attachments are in scope, if activity history has to come across, if two systems need deduplicating against each other, or if the cutover cannot involve downtime, that is a build rather than an import. StackTie migrates data into HubSpot for a fixed fee, with a field-level mapping document and a reconciliation report that proves what arrived. Live in 14 days or your money back. Book a free audit and we will tell you honestly whether you need us.

Get your blueprint

The Bottom Line

HubSpot's import tooling is better than its reputation. The volume ceilings are high, custom objects and custom properties are fully supported, activities can be created, and for a large number of migrations the correct answer is a well-ordered sequence of spreadsheet imports run by someone careful, with no vendor involved at all.

What it does not do is tell you what it did not do. Errors resolve into blanks rather than failures, matching rules apply on the basis of which columns happened to be in the file, stage history is generated rather than carried, and attachments are not part of the model at all. None of that is a defect. It is the difference between a tool that loads data and a project that moves a business from one system of record to another.

The tell is simple enough to apply before you talk to anyone. If your source exports cleanly, your identifiers are unambiguous, and nobody will need to know what a record looked like last year, you have an import and you should go and run it. If two systems have to be reconciled into one, if the history is the reason you are migrating at all, or if the files attached to your records are part of your contractual record, you have a build. The expensive version is discovering which one you had after the old system has been switched off.

Frequently Asked Questions