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 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.
But the reports connecting the conversion to user visits were broken and nobody knew why.
When I started investigating it, I found out that they didn’t pass the session_id param in the Measurement Protocol hit to GA4. HOLD ON? Sessions? In GA4? YES!
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 “place”, but GA4 is built on events and not sessions, and this is why we pass to “tell” GA the session_id.
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 for measurement protocol and CTR+F’d for “session_id”, and came up short. I was however able to find this release note from May ‘22 introducing the session_id param, and further exploration led me to this thread in which someone named Kevin from the Google Analytics API team acknowledges that Google needs to update their documentation on this matter (but can’t commit on a timeline).
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: “In order for user activity to display in standard reports like Realtime, engagement_time_msec and session_id must be supplied as part of the params for an event.”
This means that session_id should be part of the params array of the payload as such:
{
"client_id": "123123123",
"events": [
{
"name": "offline_purchase",
"params": {
"engagement_time_msec": "100",
"session_id": "123456"
}
}
]}
So now that we know where to put it, we need to find how to pull the session_id and from where. When a session_start event is fired a new session_id is created by GA, but it is not part of the _ga cookie like it was in Universal, it’s part of a new cookie that contains the container_id
And looks something like this: _ga_XXXXXXXXXX, where the X’s stand for the stream id.
Here you can see an example, in which the highlighted part is the session_id:
Just before the session_id is the session_count, i.e. the numeric counter of sessions, in this case, it’s 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 param under the params array.
Grabbing the Session ID
To grab the Session ID you can use this script:
function getSessionNumber() {
// replace ABCDEFG with your GA Stream ID
const pattern = /_ga_ABCDEFG=GSd.d.(w+).(d+)/;
const match = document.cookie.match(pattern);
const sessionNumber = match?.[2];
if (!sessionNumber) {
// Cookie not yet available; wait a bit and try again.
window.setTimeout(() => getSessionNumber(callback), 200);
return;
}
return sessionNumber;
}
If you’re using GTM, you can use this script instead:
function getSessionId() {
var pattern = /_ga_0J0X8GBMJE=GSd.d.(w+).(d+)/;
var match = document.cookie.match(pattern);
var sessionId = match && match[1];
if (!sessionId) {
return;
}
return sessionId;
}