Measurement Protocol

The Measurement Protocol is a Google API that lets any server send events directly to Google Analytics 4 over HTTPS. It replaces the browser as the sender: instead of a page firing gtag('event', ...), your backend posts a JSON payload to a Google endpoint. It exists so that things that happen away from the browser, such as a payment confirmation, a refund, or a CRM status change, can still be recorded as GA4 events.
Every request needs two credentials: the data stream’s measurement ID and an API secret generated inside GA4.
Why the Measurement Protocol Matters
It captures conversions the browser never sees. A subscription that renews on a billing cycle, an order that a fraud check approves an hour later, a lead your sales team marks qualified in the CRM: none of these produce a page view, so client-side tracking cannot record them.
It also survives what blocks JavaScript. Ad blockers, Safari’s Intelligent Tracking Prevention, and denied consent all cut into browser-collected data. A server-to-server request is not affected by any of them.
The trade-off is that you lose everything the browser supplies automatically. Page location, referrer, screen size, and device details are collected by the gtag library from its environment. A server request has none of that context unless you send it yourself.
How the Measurement Protocol Works
Four things make up a valid request.
- The endpoint.
POSTtohttps://www.google-analytics.com/mp/collectfor live data, orhttps://www.google-analytics.com/debug/mp/collectfor validation. - The credentials. Pass
measurement_id(theG-XXXXXXXXXXvalue for a web data stream) andapi_secretas query string parameters. App streams usefirebase_app_idinstead. - The identifier. A web payload requires a
client_id, which is the value GA4 already assigned to that browser. App payloads useapp_instance_id. Adduser_idwhen you have a signed-in identifier. - The events array. One to 25 events, each with a
nameand aparamsobject.
The client_id is what decides whether the event joins a real person or invents a new one. GA4 stores it in the _ga cookie, so the usual pattern is to read the GA4 client ID in the browser, save it with the order or the lead record, and send it back with the server event later.
To create the API secret: Admin, then Data streams, select the stream, then Measurement Protocol API secrets, then Create. Treat the value as a credential. Never put it in client-side code, because anyone holding it can write events into your property.
Measurement Protocol Example
A server-side purchase event sent with curl:
curl -X POST "https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"client_id": "1806452711.1713945600",
"events": [
{
"name": "purchase",
"params": {
"session_id": "1713945600",
"engagement_time_msec": "100",
"transaction_id": "T-48291",
"currency": "USD",
"value": 129.00,
"items": [
{
"item_id": "SKU-771",
"item_name": "Annual Plan",
"price": 129.00,
"quantity": 1
}
]
}
}
]
}'
Two parameters in that payload are easy to skip and expensive to omit. session_id attaches the event to an existing session, and engagement_time_msec is what makes GA4 count the activity as engagement. Leave both out and the event still lands, but it sits outside any session and contributes nothing to engagement metrics.
Measurement Protocol Limits
| Constraint | Limit |
|---|---|
| Events per request | 25 |
| Parameters per event | 25 |
| User properties per request | 25 |
| Event name length | 40 characters |
| Parameter name length | 40 characters |
| Parameter value length | 100 characters |
Backdating with timestamp_micros |
72 hours |
Event names must start with a letter and contain only letters, numbers, and underscores. No spaces. GA4 reserves a set of names, including session_start and first_open, and rejects payloads that try to use them.
The 72-hour backdating window rules out bulk historical imports. Events older than that are dropped, so a Measurement Protocol job cannot backfill last quarter.
How to Test Measurement Protocol Events
Send the payload to the validation endpoint first. Swap /mp/collect for /debug/mp/collect and the response body contains a validationMessages array. An empty array means the payload is valid.
Validate before you go live, because the production endpoint gives you nothing. /mp/collect returns HTTP 204 with an empty body whether the payload was perfect or malformed. A 204 is not confirmation that anything was recorded.
Once the payload validates, add debug_mode": true to the event params and watch it arrive in GA4 DebugView. Realtime reports show Measurement Protocol events too, usually within a minute.
Common Measurement Protocol Mistakes
- Generating a random
client_id. Every invented value creates a new user with no history, which inflates user counts and strands the conversion in its own session. - Exposing the API secret. It is a write credential, not a security boundary. Keep it server-side and rotate it if it leaks.
- Expecting attribution to appear. A server event does not carry a referrer or campaign of its own. Source and medium come from the session the matching
client_idalready belongs to. - Double counting purchases. If gtag already fires
purchaseon the thank-you page and your webhook sends it too, revenue doubles. Pick one sender per event. - Treating a 204 as success. See the validation endpoint above.
Frequently Asked Questions
What is the Measurement Protocol?
The Measurement Protocol is Google’s HTTP API for sending analytics events to GA4 from outside a browser or app. Any system that can make an HTTPS POST request can use it, including backend services, payment webhooks, and CRMs. It sends events only, and it needs a measurement ID plus an API secret on every request.
How do you get a Measurement Protocol API secret?
Open GA4 Admin, go to Data streams, select the stream you want to send to, then open Measurement Protocol API secrets and click Create. GA4 shows the secret value once it is generated. Each secret belongs to a single data stream, and you can create several so that different systems can be revoked independently.
How do you send events server side to GA4?
POST a JSON payload to https://www.google-analytics.com/mp/collect with measurement_id and api_secret in the query string. The body needs a client_id and an events array, and each event needs a name and its parameters. Include session_id and engagement_time_msec so the event attaches to a session and counts toward engagement.
Why are my Measurement Protocol events not showing in GA4?
The usual cause is a malformed payload, since the live endpoint returns 204 regardless. Send the same request to /debug/mp/collect and read the validationMessages array. If the payload validates but the data still looks wrong, check the client_id: a value GA4 has never seen produces a new user rather than joining the session you expected.
Is the GA4 Measurement Protocol the same as the old one?
No. The Universal Analytics Measurement Protocol used a different endpoint and sent form-encoded hits with a tid of UA-XXXXXXX. It stopped processing data when Universal Analytics was shut down on 1 July 2023. The GA4 version uses a JSON body, requires an API secret, and shares nothing with the old payload format.
To keep campaign click data alongside the events your server sends to GA4, track your tagged links with linkutm.