{"id":5497,"date":"2023-01-15T14:21:30","date_gmt":"2023-01-15T11:21:30","guid":{"rendered":"https:\/\/trackingchef.com\/?p=5497"},"modified":"2023-04-16T13:04:20","modified_gmt":"2023-04-16T10:04:20","slug":"how-to-add-session-id-to-ga4-measurement-protocol-events","status":"publish","type":"post","link":"https:\/\/trackingchef.com\/google-analytics\/how-to-add-session-id-to-ga4-measurement-protocol-events\/","title":{"rendered":"How to add Session ID to GA4 Measurement Protocol events?"},"content":{"rendered":"\n

I was recently helping a client migrate to GA4 (it’s right around the corner, July 1st, 2023<\/a>). This client, a SaaS B2B product, has a complex funnel, in which deals are closed offline via sales representatives. Most leads are generated on the marketing site and sent directly to their CRM (Hubspot), and from there the client wanted to report that conversion back to both GA4 and the various marketing platforms. Unsurprisingly, the answer was using GA’s Measurement Protocol API to report to GA. <\/p>\n\n\n\n

But the reports connecting the conversion to user visits were broken and nobody knew why.<\/p>\n\n\n

\n
\"image-9727972\"<\/figure><\/div>\n\n\n

When I started investigating it, I found out that they didn\u2019t pass the session_id<\/em> param in the Measurement Protocol hit to GA4. HOLD ON? Sessions? In GA4? YES!<\/strong><\/p>\n\n\n\n

Universal analytics was based on sessions, and once you passed the ga cookie id (i.e. client id) in the measurement protocol hit, then GA would match that hit to its corresponding \u201cplace\u201d, but GA4 is built on events and not sessions, and this is why we pass to \u201ctell\u201d GA the session_id.<\/p>\n\n\n\n

Google hasn’t done the best job in terms of documentation on this one, to say the least. I went over the GA 4 documentation<\/a> for measurement protocol and CTR+F’d for \u201csession_id<\/em>\u201d, and came up short. I was however able to find this release<\/a> note from May \u201822 introducing the session_id <\/em>param, and further exploration led me to this thread<\/a> in which someone named Kevin from the Google Analytics API team acknowledges that Google needs to update their documentation on this matter (but can\u2019t commit on a timeline).<\/p>\n\n\n\n

\"capture-5420803\"<\/figure>\n\n\n\n

So let me provide you guys with an answer on how to add session_id to GA4 measurement protocol (and where to take it from). According to Google<\/a>: \u201cIn order for user activity to display in standard reports like Realtime, <\/em>engagement_time_msec<\/em> and <\/em>session_id<\/em> must be supplied as part of the <\/em>params<\/em> for an <\/em>event<\/em>.\u201d <\/p>\n\n\n\n

This means that session_id should be part of the params array of the payload as such:<\/p>\n\n\n\n

{\n\"client_id\": \"123123123\",\n\"events\": [\n  {\n    \"name\": \"offline_purchase\",\n    \"params\": {\n      \"engagement_time_msec\": \"100\",\n      \"session_id\": \"123456\"\n    }\n  }\n]}<\/code><\/pre>\n\n\n\n

So now that we know where to put it, we need to find how to pull the session_id<\/em> and from where. When a session_start<\/em> event is fired a new session_id<\/em> is created by GA, but it is not part of the _ga cookie like it was in Universal, it\u2019s part of a new cookie that contains the container_id<\/em><\/p>\n\n\n\n

And looks something like this: _ga_XXXXXXXXXX, where the X\u2019s stand for the stream id. <\/p>\n\n\n\n

Here you can see an example, in which the highlighted part is the session_id<\/em>:<\/p>\n\n\n

\n
\"image-7796109\"
The Session ID in the cookie<\/figcaption><\/figure><\/div>\n\n\n

Just before the session_id<\/em> is the session_count<\/em>, i.e. the numeric counter of sessions, in this case, it\u2019s the first session. Now all you have to do is direct your developer to this number to be pulled from the client and then pass it to the measurement protocol hit, as a session_id<\/em> param under the params array.<\/p>\n\n\n\n

Grabbing the Session ID<\/h2>\n\n\n\n

To grab the Session ID you can use this script:<\/p>\n\n\n\n

function getSessionNumber() {\n\/\/ replace ABCDEFG with your GA Stream ID\n  const pattern = \/_ga_ABCDEFG=GSd.d.(w+).(d+)\/;\n  const match = document.cookie.match(pattern);\n  const sessionNumber = match?.[2];\n\n  if (!sessionNumber) {\n    \/\/ Cookie not yet available; wait a bit and try again.\n    window.setTimeout(() => getSessionNumber(callback), 200);\n    return;\n  }\n\n  return sessionNumber;\n}\n<\/code><\/pre>\n\n\n\n

If you’re using GTM, you can use this script instead:<\/p>\n\n\n\n

function getSessionId() {\r\n  var pattern = \/_ga_0J0X8GBMJE=GSd.d.(w+).(d+)\/;\r\n  var match = document.cookie.match(pattern);\r\n  var sessionId = match && match[1];\r\n\r\n  if (!sessionId) {\r\n    return;\r\n  }\r\n\r\n  return sessionId;\r\n}\r\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"

I was recently helping a client migrate to GA4 (it’s right around the corner, July 1st, 2023). This client, a SaaS B2B product, has a complex funnel, in which deals are closed offline via sales representatives. Most leads are generated on the marketing site and sent directly to their CRM (Hubspot), and from there the […]<\/p>\n","protected":false},"author":6,"featured_media":5499,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[136,135],"class_list":["post-5497","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-google-analytics","tag-google-analytics-4","tag-measurement-protocol"],"_links":{"self":[{"href":"https:\/\/trackingchef.com\/wp-json\/wp\/v2\/posts\/5497","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/trackingchef.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/trackingchef.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/trackingchef.com\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/trackingchef.com\/wp-json\/wp\/v2\/comments?post=5497"}],"version-history":[{"count":3,"href":"https:\/\/trackingchef.com\/wp-json\/wp\/v2\/posts\/5497\/revisions"}],"predecessor-version":[{"id":5619,"href":"https:\/\/trackingchef.com\/wp-json\/wp\/v2\/posts\/5497\/revisions\/5619"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/trackingchef.com\/wp-json\/wp\/v2\/media\/5499"}],"wp:attachment":[{"href":"https:\/\/trackingchef.com\/wp-json\/wp\/v2\/media?parent=5497"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/trackingchef.com\/wp-json\/wp\/v2\/categories?post=5497"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/trackingchef.com\/wp-json\/wp\/v2\/tags?post=5497"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}