Webclat / GA4 Practice
Home / QA / Measurement Protocol and server-side

What exact client_id and session_id format does the GA4 Measurement Protocol require, and where do I get a valid one?

Quick answer

GA4's client_id must be in the exact format <random 32-bit number>.<unix timestamp of first visit>, for example 1234567890.1690000000 - it is not a free-text string or a UUID. The only reliable source for a real client_id is reading the browser's own _ga cookie (or the gtag.js get client_id callback) rather than generating one yourself for server-side calls that need to match an existing browser session.

Why This Happens

01 / CAUSE

The Measurement Protocol was designed to let a server send events on behalf of a client GA4 already knows about, not to create new clients from scratch. Its client_id field is deliberately shaped like the value GA4's own JavaScript library writes into the _ga cookie, because the intended flow is: read the existing client_id from the cookie, then reuse it server-side, not invent a new one.

When a client_id is malformed (missing the dot, not numeric, or the wrong length) GA4 does not always return a hard error - it may accept the hit with a 204 and then either drop it during processing or attribute it to a synthetic, disconnected user that never shows up as continuity with the visitor's browser session. That's why this failure mode is so easy to miss: the request looks successful.

Fix It

02 / STEPS

Read the real client_id from the browser when possible

If gtag.js is already loaded client-side, retrieve the live value with gtag('get', 'G-XXXXXXX', 'client_id', (cid) => { /* send cid to your server */ }) rather than parsing the cookie by hand, since the callback returns the exact string GA4's library already generated.

If reading the cookie directly, parse it correctly

The _ga cookie's value looks like GA1.1.1234567890.1690000000. The client_id you send to the Measurement Protocol is only the last two dot-separated segments: 1234567890.1690000000 - drop the GA1.1. prefix, which is a cookie-format version marker, not part of the ID.

For genuinely new/server-only clients, generate the format correctly

If there truly is no existing browser client (a batch job, a webhook, a first server-side touchpoint), construct <random large integer>.<current unix timestamp in seconds> yourself - most server SDKs and the official GA4 Measurement Protocol libraries include a helper for this so you don't hand-roll the random component.

Also validate session_id the same way

session_id (sent as the _ss/session parameter, or via the ga_session_id event parameter depending on your library) should likewise be read from the existing session, generated by gtag.js, rather than invented, or session-scoped metrics like session count and engagement time will be wrong.

How To Verify It Worked

03 / VERIFY

Send a test event to the Measurement Protocol's validation endpoint (https://www.google-analytics.com/debug/mp/collect instead of /mp/collect, same payload) and check the JSON response's validationMessages array - a malformed client_id triggers an explicit message there even though the equivalent live endpoint would return a bare 204.

Then confirm real attribution in DebugView: send the live event with debug_mode: true in the payload, open GA4 Admin > DebugView, and confirm the event appears attached to the same user/device thread as the browser session it's supposed to extend, not as a brand-new, disconnected debug user.

Still Not Fixed?

We audit and fix GA4 implementations for a living - if this doesn't resolve it, the next step is usually a full event-by-event audit.

Talk to a GA4 Consultant