HubSpot + GA4 Attribution: Connecting Form Submissions to Traffic Sources
HubSpot knows who submitted the form. GA4 knows where they came from. Joining them takes UTM discipline, a privacy decision, and honesty about what the join can and cannot prove.
Updated 7 min read
HubSpot tells you 47 people filled in the demo form last month. GA4 tells you organic search drove 2,100 sessions and paid drove 900. The client asks how many of the 47 came from paid. You open a third tab and start guessing.
This is the most common analytics gap in B2B agency work, and it is genuinely hard, because the two systems were built to answer different questions. HubSpot is a record of people. GA4 is a record of sessions. A join between them is a claim about identity, and identity is where both the accuracy problems and the privacy problems live.
Why the Naive Approach Fails
The obvious move is to read UTM parameters off the HubSpot contact record and count submissions by source. HubSpot stores original source and latest source on contacts, and you can pull them. It fails in three specific ways.
Hidden fields go stale. The standard pattern is a hidden form field populated from the query string or a first-touch cookie. If the visitor lands on a UTM-tagged page, browses for a week, comes back direct, and converts, the hidden field carries whatever your script decided to persist. Last-touch and first-touch scripts disagree about the same submission, and most sites run one of each without knowing it.
Direct swallows everything. Any traffic without tags, any stripped referrer, any app-to-browser handoff, any pasted link from a Slack message, all of it becomes direct or not set. On most B2B sites this is a substantial share of form submissions. You end up with a source breakdown where the largest bucket means we do not know.
Timestamp differences. HubSpot timestamps a submission in epoch milliseconds. Whatever you compare it to is dated in some timezone. A submission at 23:30 UTC is Tuesday in UTC and Wednesday in Europe/Rome. Do that consistently across a month and your daily curve is shifted by one day against GA4, which dates events in the property's reporting timezone. The fix is straightforward: convert all timestamps to the same timezone before bucketing into daily aggregates. For more on UTM tracking best practices, see the dedicated guide.
The Privacy Decision Comes First
Here is the part agencies skip and then regret during a client's security review.
Joining a HubSpot submission to a GA4 session at the individual level means handling personal data. The submitter's email is personal data. So is any identifier that lets you re-find that person later. Under GDPR, your agency is usually a processor and your client is the controller, which means the decision about how granular this data gets is not yours to make quietly.
At the aggregate tier, you get submission counts per form per day and submissions per normalized page path per day. You do not get individual-level data. What this means for your reporting: you get the pricing page produced 12 submissions on the 14th and organic produced 31 submissions in August. You do not get this specific person came from this specific ad. If you need the second, that is a HubSpot-native workflow with the client's explicit sign-off, not a reporting pipeline.
Two Modes of Data Collection
HubSpot exposes form data through two quite different API surfaces, and which one a given portal can use depends on the HubSpot tier, not on preference.
| Mode | What You Get | What You Cannot Get |
|---|---|---|
| Analytics API | Views, submissions, sessions, contacts broken down by form, page, or source | Requires Marketing Hub Professional or higher |
| Forms Aggregation | Submission counts per form, per page, per day | No view counts, no session data, no traffic source breakdowns |
The distinction matters because views determine whether you can calculate a form conversion rate. A form with 500 views and 12 submissions has a 2.4% conversion rate. That is actionable. A form with 12 submissions and no view data just has a submission count. The system should detect which API surface is available and select the right mode automatically. A form with null views is not a form with zero views. It is a form where view data is unavailable, and any consumer needs to handle that distinction.
Building the Attribution Bridge
Once you have both GA4 and HubSpot data, the real work is connecting them. The connection is not made by matching strings. It is made by having a person explicitly state which HubSpot form corresponds to which GA4 key event and stand behind that mapping.
A request a demo form in HubSpot might correspond to a generate_lead key event in GA4, or it might map to a custom event. The mapping makes the comparison meaningful. Without it, you are comparing totals that may include different forms, different pages, and different conversion definitions.
Even with the mapping in place, the comparison engine should recognize that HubSpot counts a submission when the form posts and GA4 counts a key event when its trigger fires in a session. These are different measurement bases, and a percentage between them is meaningless. What you get is both numbers and the direction: are form submissions growing in both systems, or only one?
Practical Setup Steps
Fix UTM discipline before touching any tool
One tagging convention, documented, applied to paid, email, and social. Lowercase everything. Decide first-touch or last-touch once, per client, in writing.Align timezones
Set the HubSpot connector timezone to the same zone the client's GA4 property reports in. Check the GA4 property admin for the real value rather than assuming the client's office timezone.Choose the privacy tier deliberately
With the client, not for them. Start at aggregates. Move up only when there is a question aggregates genuinely cannot answer, and record who decided.Name GA4 key events after outcomes, not mechanics
A demo_request event survives a form rebuild. A form_submit_7 event does not. Stable names make the form-to-event mapping durable.Map forms to key events explicitly
One at a time, and treat the mapping as something with an owner rather than a config file nobody remembers writing.Report direction, not ratios
Until you can prove both sides count the same thing, report whether they agree directionally. You almost never can prove exact parity.
What the Join Can and Cannot Tell You
At the aggregate and attribution tiers, you can compare channel-level conversion counts across both platforms. Directional agreement tells you the tracking is working. A channel that shows 47 events in GA4 but 5 submissions in HubSpot has a data integrity problem worth investigating.
What you cannot get from this join is a single perfect number. HubSpot's attribution depends on UTM persistence to the submission page, which is lossy. GA4 has better session-level attribution because it captures UTMs at the entry point. Trust HubSpot for lead volume. Trust GA4 for traffic source classification. Cross-reference for validation. For more on how these disagreements play out across all your data sources, see CRM analytics integration and cross-source reconciliation.