HubSpot provides its own tracking infrastructure as part of its CRM system. But installing the tracking code is just the beginning; the real value lies in integrating it with GTM, tracking form submissions, and measuring chat conversions.
This guide covers HubSpot conversion tracking end-to-end: tracking code setup, _hsq API usage, GA4/GTM integration, AJAX form submission, and Conversations (chat) tracking.
Tracking Code Setup
The HubSpot tracking code is tied to a unique Hub ID and works across all HubSpot Hubs (Marketing, Sales, Service, CMS)1.
Key Rules
- Each tracking code is associated with a single HubSpot account (Hub ID)
- If multiple HubSpot tracking codes load on a single page, only the first one fires2
- Does not work on AMP sites
- SPAs (Single Page Applications) require separate setup3
- If using a different domain or subdomain, add it under Settings > Tracking & Analytics > Advanced Tracking4
Code Placement
<!-- Start of HubSpot Embed Code -->
<script
type="text/javascript"
id="hs-script-loader"
async
defer
src="//js.hs-scripts.com/<hub-id>.js"
></script>
<!-- End of HubSpot Embed Code -->
Place this code just before the </body> tag. Verify the setup by checking for the hs-scripts.com request in your browser’s Network tab5.
WordPress: If you can’t use the HubSpot plugin, add the code to footer.php (requires Business plan).
Shopify: Install via the theme.liquid file.
_hsq API: Page and Event Tracking
The HubSpot tracking code operates through the _hsq global object. This API lets you track page views, identify users, and log custom events.
Page Views
To report page changes in SPAs or dynamic content:
var _hsq = (window._hsq = window._hsq || []);
_hsq.push(["setPath", "/about-us"]);
_hsq.push(["trackPageView"]);
trackPageView must be called again for each page change. It also works with history.pushState state transitions3:
var stateObj = { foo: "updated" };
history.pushState(stateObj, "updated page", "updated.html");
_hsq.push(["trackPageView"]);
User Identification (Identify)
To associate a known user with a HubSpot contact:
_hsq.push([
"identify",
{
email: "user@example.com",
favorite_color: "orange",
},
]);
Custom Event Tracking
For button clicks, product views, add-to-cart, and other custom events:
_hsq.push([
"trackEvent",
{
id: "Clicked Buy Now button",
value: 20.5,
},
]);
GA4 and GTM Integration
On HubSpot Pages (website, landing page, blog), you can directly enable GA4, GTM, and AdRoll tags. Under Settings > Tools > Website > Pages > Integrations:
- Integrate with Google Analytics: Previously loaded
analytics.js(UA), now addsgtag.js(GA4) code between<head>tags - Integrate with Google Tag Manager: Adds GTM snippet to
<head>and<body>tags - Integrate with AdRoll: Adds AdRoll tracking code after the
<body>tag
_hsp Privacy Consent
HubSpot integration codes operate through the _hsp global object and check consent status via addPrivacyConsentListener:
var _hsp = (window._hsp = window._hsp || []);
_hsp.push([
"addPrivacyConsentListener",
function (consent) {
if (
consent.allowed ||
(consent.categories && consent.categories.analytics)
) {
// GA4 or GTM code here
}
},
]);
Domain Settings
Under Choose a domain to edit its settings, you can configure settings for all domains or for a specific domain separately.
Templates: Header and Footer HTML
Under Pages > Templates, two custom areas are available:
- Site header HTML: Added after all integration codes, just before
</head> - Site footer HTML: Added just before
</body>
Use these areas for dataLayer definitions and custom GA events.
Form Conversion Tracking
HubSpot forms can be submitted via AJAX in addition to the standard embed. This method provides more flexibility and control.
AJAX Submission via Forms API
HubSpot Forms API endpoint:
POST https://api.hsforms.com/submissions/v3/integration/submit/<portalId>/<formId>
Payload structure:
{
"submittedAt": "1517927174000",
"fields": [
{ "name": "email", "value": "user@example.com" },
{ "name": "firstname", "value": "Joe" }
],
"context": {
"hutk": "<hubspotutk-cookie-value>",
"pageUri": "www.example.com/page",
"pageName": "Registration Page"
},
"legalConsentOptions": {
"consent": {
"consentToProcess": true,
"text": "I have read and agreed to the privacy notice."
}
}
}
hubspotutk Cookie
The hubspotutk cookie is a unique value HubSpot uses for visitor activity tracking. For AJAX submissions, include this value in the context.hutk field:
const gethutk = Object.fromEntries(
document.cookie.split(/; /).map((c) => {
const [key, v] = c.split("=", 2);
return [key, decodeURIComponent(v)];
}),
);
const hutk = gethutk.hubspotutk || window.__hsUserToken;
AJAX Form Submission Example
document.getElementById("hb-submit").addEventListener("click", function (e) {
e.preventDefault();
const name = document.getElementById("hs-firstname");
const email = document.getElementById("hs-email");
const portalId = "<account-id>";
const formId = "<form-id>";
const endpoint = "https://api.hsforms.com/submissions/v3/integration/submit";
fetch(`${endpoint}/${portalId}/${formId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
fields: [
{ name: "firstname", value: name?.value || "" },
{ name: "email", value: email?.value || "" },
],
submittedAt: Date.now(),
context: {
hutk: document.cookie.match(/hubspotutk=([^;]+)/)?.[1] || "",
pageName: document.title,
pageUri: window.location.href,
},
legalConsentOptions: {
consent: {
consentToProcess: true,
text: "I have read and agreed to the privacy notice.",
},
},
}),
})
.then((response) => response.json())
.then((data) => {
/* Success */
})
.catch((error) => {
/* Error */
});
});
Form Event Tracking with GTM
To forward AJAX or standard form submissions to GTM, use dataLayer.push():
dataLayer.push({
event: "hubspot_form_submitted",
formId: "<form-id>",
formName: "Newsletter Signup",
});
Create a hubspot_form_submitted Custom Event trigger in GTM and attach your desired conversion tags.
Chat (Conversations) Conversion Tracking
HubSpot Conversations (chat widget) enables live chat with visitors on websites and Facebook pages. Manage Live Chat and Chatbot options under Chatflows.
Conversations API
Use the HubSpot Conversations API to listen for chat events and push them to GTM6:
// Check API availability
if (window.HubSpotConversations) {
initChatTracking();
} else {
window.hsConversationsOnReady = [initChatTracking];
}
function initChatTracking() {
// Chat started
window.HubSpotConversations.on("conversationStarted", (payload) => {
dataLayer.push({ event: "hs_chat_started" });
});
// Chat closed
window.HubSpotConversations.on("conversationClosed", (payload) => {
dataLayer.push({ event: "hs_chat_closed" });
});
}
Widget Tracking via postMessage
Beyond the Conversations API, you can also monitor widget state changes using message event listeners:
window.addEventListener("message", function (event) {
if (event.origin !== "https://app.hubspot.com") return;
let eventData = JSON.parse(event.data);
let eventAction = false;
switch (eventData.type) {
case "closed-welcome-message":
eventAction = "Closed - Welcome Message";
break;
case "open-change":
eventAction = eventData.data ? "Opened - Window" : "Closed - Window";
break;
case "external-api-event":
if (eventData.data.eventType === "conversationStarted")
eventAction = "conversationStarted";
break;
}
if (eventAction) {
dataLayer.push({
event: "hubspot_chat_event",
event_category: "Live Chat",
event_action: eventAction,
event_label: eventData.uuid,
});
}
});
Testing and Verification
- Tracking code: Check for the
hs-scripts.com/<hub-id>.jsrequest in your browser’s Network tab - _hsq API: Type
window._hsqin the console to verify the queue exists - GA4/GTM: If integration is active on HubSpot Pages, check the
_hspobject’s consent listener - Form submission: Submit a form via AJAX and verify a new contact appears under HubSpot > Contacts
- Chat tracking: Start a chat and check for the
hs_chat_startedevent in GTM Preview - hubspotutk: Verify the
hubspotutkvalue exists indocument.cookie
If multiple HubSpot tracking codes load on a single page, only the first one fires. If you enable both GA4 and GTM integrations within HubSpot Pages, managing everything through GTM produces more consistent results.
Footnotes
- Track visitors in HubSpot. HubSpot Knowledge Base ↩
- Install the HubSpot tracking code. HubSpot Knowledge Base ↩
- Tracking in single-page applications. HubSpot Developers ↩ ↩2
- Set up site tracking in HubSpot. HubSpot Knowledge Base ↩
- Troubleshoot the HubSpot tracking code. HubSpot Knowledge Base ↩
- HubSpot Conversations JavaScript API. HubSpot Developers ↩
- 01 The HubSpot tracking code is tied to a Hub ID; if multiple codes load on one page, only the first one fires
- 02 The _hsq API enables page view tracking, user identification (identify), and custom event logging
- 03 On HubSpot Pages, GA4, GTM, and AdRoll integrations are enabled directly from Settings > Integrations
- 04 AJAX form submissions must include the hutk cookie value, pageName, and pageUri as context parameters
- 05 The Conversations chat widget can be tracked via postMessage events, pushing chat conversion events to the dataLayer
+ How do I set up the HubSpot tracking code?
Place the HubSpot tracking code just before the closing body tag. The code is a Hub ID specific JavaScript file (js.hs-scripts.com). On WordPress, add it to footer.php; on Shopify, use theme.liquid. Verify by checking for the hs-scripts.com request in the browser Network tab.
+ How do I track AJAX form submissions in HubSpot?
Send a POST request to the HubSpot Forms API endpoint (api.hsforms.com/submissions/v3/integration/submit) with your portalId and formId. Include the hutk cookie value, pageName, and pageUri as context. This method is used for custom form designs outside the standard embed.
+ How do I track HubSpot chat (Conversations) conversions?
The HubSpot Conversations widget sends postMessage events. Use window.addEventListener to capture closed-welcome-message, open-change, and conversationStarted events, then push them to the dataLayer via dataLayer.push() for GTM processing.
+ How do I integrate HubSpot with GTM?
On HubSpot Pages, enable GTM directly from Settings > Tools > Website > Pages > Integrations. On external sites, add GTM snippets to the Header/Footer HTML fields or place the code directly in your theme files.