Pixel & Platform IntegrationsTutorialIntermediate

GA4 Measurement Protocol done right: the EU endpoint, client_id, debug validation and what it cannot do

Sending server events to Google Analytics 4 through the Measurement Protocol: the region1 endpoint, why client_id matters, the 25-event limit, timestamp_micros, the debug endpoint and the consent fields.

By
Track editorial team
Published
Last reviewed
Reading time
3 min read

Key takeaways

  • Track sends Measurement Protocol requests to the region1 endpoint by default so traffic originates and terminates in the EU; the api_secret belongs in the vault.
  • Server events are stitched into sessions only with the real client_id from the _ga cookie, captured under analytics consent; without it, user counts are inflated.
  • The production endpoint always answers 2xx, so test mode validates every event on the debug endpoint first and forwards it only when there are no validation messages.
  • The pragmatic split: gtag for page views and interactions, Measurement Protocol for the authoritative purchase with transaction_id, refunds and backend events, with consent fields derived from the visitor's purposes.

The endpoint and the EU variant

POST https://www.google-analytics.com/mp/collect?measurement_id=G-XXXX&api_secret=… is the documented collection endpoint. Google also documents https://region1.google-analytics.com/mp/collect for EU-based traffic; Track uses the region1 host by default so requests originate and terminate in the EU.

The measurement_id is public (it is in your gtag snippet). The api_secret is created under Admin → Data streams → Measurement Protocol API secrets and belongs in the vault.

The body

json
{
  "client_id": "1234567890.1700000000",
  "user_id": "u_42",
  "timestamp_micros": 1767225600000000,
  "non_personalized_ads": false,
  "consent": { "ad_user_data": "GRANTED", "ad_personalization": "DENIED" },
  "user_data": { "sha256_email_address": ["<hash>"] },
  "events": [{ "name": "purchase", "params": { "transaction_id": "A1001", "value": 129.9, "currency": "EUR", "items": [{ "item_id": "SKU-1", "price": 99.9, "quantity": 1 }], "engagement_time_msec": 100 } }]
}

Limits from the reference: at most 25 events per request, 25 parameters per event, event names up to 40 characters, and timestamp_micros no older than 72 hours. Names starting with google_, ga_ or firebase_ are reserved.

client_id is the thing people forget

GA4 stitches server events into sessions using client_id, the value stored in the _ga cookie (GA1.1.<random>.<timestamp>client_id = "<random>.<timestamp>"). Without the real client id, server events form their own orphaned users and inflate user counts.

Track reads the _ga cookie in the browser when analytics consent is granted, sends it with every event as a vendor id and uses it as client_id for Measurement Protocol deliveries. When no _ga value exists (pure server sources), it derives a stable pseudo client id from the anonymous id so events at least group per visitor.

It always says yes — validate on the debug endpoint

The production endpoint returns 2xx for anything syntactically acceptable. The only way to learn about semantic problems (unknown parameter types, reserved names, missing client id) is the debug endpoint /debug/mp/collect, which answers with validationMessages. In test mode Track sends every event to the debug endpoint first and only forwards it when the list is empty, so a broken mapping fails loudly in the wizard rather than silently in production.

What the Measurement Protocol cannot do

  • It does not create sessions on its own; page views should come from gtag in the browser.
  • It has no geo, device or campaign data unless you send it; most teams send purchases, refunds and backend events only.
  • Realtime reports need engagement_time_msec (any positive value) to count the user as engaged.
  • Attribution to campaigns still relies on the browser session the client_id belongs to.

The pragmatic split: gtag for page views and interactions, Measurement Protocol for the authoritative purchase with transaction_id (GA4 deduplicates purchases by transaction id), refunds and backend events.

Send consent.ad_user_data and consent.ad_personalization derived from the visitor's purposes and set non_personalized_ads: true when marketing consent is missing. Analytics consent is required for the event to be sent at all — the GA4 destination needs the analytics purpose in Track, not marketing.

Checklist

  • To do: Measurement ID in the destination, API secret in the vault
  • To do: _ga client id captured with analytics consent
  • To do: Purchases carry transaction_id; browser and server purchases share it
  • To do: Debug validation passes in test mode
  • To do: Events confirmed in DebugView with the expected parameters

Primary sources

Documentation and standards this article is based on.

  1. Google Analytics — Measurement Protocol (GA4) referencedevelopers.google.com
  2. Google Analytics — Validating eventsdevelopers.google.com

Was this article helpful?

Responsible editor

Track editorial team

Product & engineering

The people building Track: engineers and analysts who work on server-side tracking, consent tooling and connector integrations every day.