Measurement Protocol v2 for GA4
What the Measurement Protocol is for
The Measurement Protocol (MP) is a raw HTTP API for sending events directly to GA4 without a browser, a gtag.js snippet, or GTM — a plain POST request with a JSON payload. It's the right tool whenever an event originates somewhere with no DOM: a backend order-fulfillment service confirming a shipment, a server-side webhook from a payment processor, an IoT device, or a batch job backfilling historical events. GA4's version (v2) replaced Universal Analytics' Measurement Protocol with a payload shape that matches GA4's event-parameter model rather than UA's category/action/label hits.
// POST https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXX&api_secret=YOUR_SECRET
{
"client_id": "123456789.1712345678",
"user_id": "crm_user_48213",
"timestamp_micros": 1735689600000000,
"non_personalized_ads": false,
"events": [
{
"name": "refund",
"params": {
"transaction_id": "T_98765",
"currency": "USD",
"value": 149.50
}
}
]
}The client_id problem: you must already have one
The single biggest practical obstacle to using MP correctly is that GA4 requires a valid client_id matching an existing, previously-seen GA4 client — MP doesn't create new users, it adds events to an existing user's history. If you're sending a server-side event for a user who already has a browser session (e.g., confirming a purchase after an async payment webhook resolves), you must capture and persist that user's client_id from the client-side GA4 cookie (_ga) at the time of their session, then pass it along through your backend so the later MP call can attach to the same user. Sending MP events with a fabricated or random client_id creates phantom users GA4 has never actually seen, polluting user counts.
MP events don't validate synchronously in production — use the debug endpoint
/debug/mp/collect) that returns detailed validation messages for malformed payloads, missing required fields, or unregistered event names. Always test new MP integrations against the debug endpoint first; shipping straight to production means failures are invisible until someone notices missing data in reports days later.api_secret management and why it needs rotation
Every MP call requires an api_secret generated in GA4 Admin > Data Streams > Measurement Protocol API secrets. This secret is the only authentication MP uses — there's no per-request signing or OAuth. Treat it like any other backend credential: never embed it in client-side code (it would let anyone send arbitrary events to your property), store it in a secrets manager, and rotate it if a server or repository holding it is ever compromised. Multiple secrets can be active simultaneously per stream, which lets you rotate one integration's credential without breaking others.
When to use MP vs. gtag.js/GTM vs. server-side GTM
| Scenario | Recommended approach |
|---|---|
| Browser pageviews, clicks, form submits | gtag.js / GTM client-side container |
| Server confirms an async event with an existing user session | Measurement Protocol, using the stored client_id |
| High-volume server-side tagging with multiple destinations (GA4 + Meta + Ads) | Server-side GTM container, which itself can forward to GA4 via MP internally |
| Backfilling historical events into GA4 retroactively | Measurement Protocol with historical timestamp_micros (within GA4's accepted backdating window) |
MP respects consent the same way client-side tags must
What's Next?
MP is the raw primitive underneath a lot of server-side tagging infrastructure — understanding it directly makes debugging a server-side GTM container, or building a custom backend integration, far less of a black box.
I build these systems professionally.
Whether it's a RAG pipeline, analytics migration, or AI workflow — let's talk.