Skip to main content

Agency & White-Label Services

How to Scope a Client's HubSpot Migration Without Eating the Overage


Migrations overrun because the estimate is built on record count. The units that predict the hours are object count and a rebuild inventory nobody made.

Julia HanneyBy Julia HanneyUpdated October 20, 20266 min read
A migration scoping worksheet listing object counts, history depth and rebuild items against agreed exclusions.

Key Takeaways

  • Contacts are the cheapest thing in a migration; every note, call, task and association is a separate object with its own failure modes.
  • Field fill rates are the largest single scope reduction available, and producing them takes minutes.
  • Split the quote at the API boundary so the client cannot assume the rebuild scales the way the data does.
  • Define completion as a checkable artifact, because a data project with no agreed finish line ends whenever the client stops finding things.
  • The overrun is usually visible in week one, in the gap between the inventory you were given and the inventory you counted.

Migration overruns are rarely caused by something going wrong. They are caused by the estimate being built from the wrong number, and the wrong number is almost always the contact count.

It is the number the client leads with, it is the easiest to obtain, and it correlates with roughly none of the work.

Contacts are the cheapest thing in the project

Every note, call, email, meeting, task and association is a separate object that has to be extracted, mapped, transformed, loaded and verified, each with its own failure modes.

On one migration we ran, moving 508 contacts with 90 days of activity history came to roughly 14,000 objects, kept in sync during the transition. Priced from the contact count, that is a trivial job. Priced from the object count, it is a real project, and the object count is the one that predicts the hours.

So the first correction is a unit change. Ask for record counts per object type, not a database size.

The four numbers to get before quoting

1. Records per object. Contacts, companies, deals, tickets, custom objects, and the activity types separately. If the client cannot produce this, producing it is the first paid piece of work rather than something you absorb.

2. History depth, in months. And which activity types within that window. This is the largest single lever on the number, and it is a client decision rather than a technical one. Ninety days of activity covers almost every operational need. Four years covers a feeling. Hold that conversation early and hold it honestly.

3. Fields, and how many are still used. Not the field count, the fill rate per field. Export it, sort ascending, and draw a line. On a portal we audited, 101 custom properties had under one percent fill and 44 had never been used at all. Migrating those is billable work that produces nothing but future confusion.

The question that makes this conversation work is not "do you need this field," which always gets a yes. It is "which report, list, workflow or integration would break if this field did not exist." A field nothing reads is a field nothing needs.

4. The rebuild inventory. Reports, dashboards, automations, templates and permission structures, counted. These do not transfer from any platform, so they are manual hours, and they routinely exceed the data work. Count what exists, then agree what survives, because the surviving list is always shorter. On one audit we found 1,306 reports, most of them stale; on another, 222 of 282 were owned by three former agencies' email addresses.

Those four numbers take a couple of hours to establish and they are the difference between a quote that holds and one that does not.

Split the quote at the boundary

Present the data migration and the rebuild as two sections with two different pricing logics.

Data, schema, lists and content in one, priced as automated work that scales with volume. Reports, automations, templates and permissions in the other, priced as build hours against the agreed inventory.

The reason is not presentational. A single combined number invites the client to assume the whole project scales the same way, so when they add a platform or extend the history window they expect a proportional change, and when they add fourteen reports they expect no change at all. Splitting the quote makes the second conversation possible without it feeling like a renegotiation.

Write the exclusions down

An assumptions block that says the platform "provides an API for this functionality" is doing no work. It is true of almost everything and it protects nobody.

What belongs in a migration statement of work instead:

  • The specific objects and operations, named. "Create and update deal records with company associations," not "migrate deals."
  • The history window, in months, with a date.
  • The field list, or the fill-rate threshold that generated it, attached as an appendix.
  • The rebuild inventory, itemised, with everything outside it named as excluded. Reports and web analytics history belong here explicitly, because neither can be moved by any tool at any price.
  • The volume the estimate assumes, because the estimate is only valid at that volume. An integration that works against 500 records can be structurally different at 80,000.
  • What the client supplies, and by when. Usually a credential with named scopes and an export. Late credentials are the most common cause of a timeline slipping, and they are entirely outside your control unless the date is in the document.
  • How completion is proven.

That last one is the difference between a project that ends and one that lingers. If nobody agreed in advance what done looks like, done is whenever the client stops finding things.

Make it an artifact: reconciled record counts per object, association counts, and an agreed sample of records verified by hand. Deliver it, get it signed, and the project has a finish line.

The conversations that make it cheaper

Worth saying plainly, because it does not feel like it should work: the largest cost reductions on a migration come from conversations, not from technique.

The history window. Ask what they will actually do with four-year-old call notes. The honest answer is usually "look at them once, if ever."

The field cut. Sorted fill rates in front of the client, with the question about what reads each field. Archive the full source export rather than discarding it, which converts "we deleted your data" into "it is in the archive," a much easier sentence.

The report list. Show them the inventory and ask which they opened in the last quarter. This is the single most effective scope reduction available on a large portal.

Each of these makes the project smaller and the client happier, which is an unusual combination and worth using.

Where the overrun becomes visible

Almost always in week one, in the gap between the inventory the client described and the inventory you counted.

The signals, in the order they show up:

  • The export does not match the stated counts. Before you conclude the client was wrong, check whether the export was filtered, because it usually was.
  • The identifier column is inconsistent. Blank or duplicated matching keys mean the import will either duplicate or collapse records, and cleaning that is real work nobody quoted.
  • Nobody can name the owner of the reporting requirement. If no single person can say which reports must exist on day one, the rebuild will expand until somebody does.
  • The list of integrations grows when you ask a second person. It always grows. Ask three people.

Raise these in week one, in writing, with the number attached. A scope conversation in week one is a decision. The same conversation in week six is a dispute, and the difference is not the facts.

Change control that does not poison the relationship

Two habits that keep this civil.

Price the change when it appears, not at the end. A short note saying what was added, what it costs and what it does to the date. Clients accept these routinely when they arrive one at a time and resist them fiercely when they arrive as a single number in the final invoice.

Keep a decision log. Every scope call, with the date and who made it. This is not defensive paperwork, it is the thing that lets you say "we agreed on the twelfth to carry ninety days" without either party having to remember.

The bottom line

Scope from object count and history depth rather than contacts, get fill rates before you price the fields, count the rebuild inventory separately, and split the quote so the two halves are visibly priced on different logic. Name the exclusions, state the volume the estimate assumes, and define done as an artifact somebody signs.

Then hold the three conversations that make the project smaller, because they work better than any technical decision available to you.

If you would rather hand the scoping itself to a team that has counted these before, our white-label HubSpot migration service runs discovery and delivery under your brand, migration scoping is where that conversation starts, and our agency partnership model keeps us behind your logo throughout.

Sources

  1. HubSpot: Import records for multiple objects (opens in new tab)
  2. HubSpot: Reporting API reference (opens in new tab)
  3. HubSpot: Create and edit properties (opens in new tab)

Frequently Asked Questions

How should an agency scope a HubSpot migration?

By object count and history depth rather than contact count, plus a counted inventory of everything that has to be rebuilt by hand. Records are the cheapest part; activities, associations, reports, automations and templates are where the hours are, and none of them scale with the contact number.

Why do HubSpot migration projects overrun?

Almost always because the estimate was built on the wrong unit. A quote priced from the contact count assumes the whole project scales with records, when in practice the rebuild work, reporting and automations do not scale that way at all and frequently exceed the data work.

What should a migration statement of work exclude explicitly?

Reports and dashboards, historical web analytics, automations not on the agreed list, templates beyond the counted set, and any history older than the agreed window. Naming exclusions is what makes the change conversation a decision rather than an argument.

How do you reduce the cost of a client migration?

Export field fill rates and cut what nothing reads, agree which reports and automations are genuinely still used, and set the history window deliberately. These conversations remove more work than any technical optimisation, and they happen before anything is built.

How do you define done on a data migration?

With a checkable artifact agreed in advance: reconciled record counts per object, association counts, and a sample of records verified by hand. Without one, completion is whenever the client stops finding things, which is not a date you can plan around.

White-Label HubSpot Migrations

Migrations Shouldn't Eat Your Margin

We move your clients to HubSpot (data, content, automations) on time and under your brand, while your team stays billable.