What is the GA4 Measurement Protocol?
The GA4 Measurement Protocol is the format GA4 uses on the wire. Each hit is a request to `/g/collect` whose parameters describe the event (`en`), the property (`tid`), the client and session, and the device, all as key/value pairs.
Why it matters
GA4 is a black box for most people. You call gtag('event', ...) and data shows up in reports. But between your browser and Google's servers, there's a protocol: a specific wire format that describes what's happening.
Understanding the Measurement Protocol is useful for debugging. When a tag doesn't fire or data looks wrong, you can open DevTools and read the raw request. Or if you're building a server-side tag, you'll write requests in this format by hand.
The protocol is also the contract between you and Google. If you're doing advanced work (server-side tagging, sending data from your backend, customising what your page tracks), you're working directly with the Measurement Protocol.
How it works
Every GA4 hit is an HTTP request to /g/collect. The request carries parameters as key/value pairs, either in the URL query string (GET) or in the POST body.
Core parameters: Every request needs these:
v=2: protocol version (2 is GA4, 1 is old Universal Analytics)tid=G-XXXXXXXXXX: measurement ID (which property this hit belongs to)cid=12345.67890: client ID (which browser/device this is)en=<event_name>: event name (page_view, purchase, custom_event)
Session and timing:
sid=1700000000: session ID (groups events into one visit)sct=1: session countseg=1: engagement flag (0 or 1)_s=<timestamp>: server timestamp when Google receives the hit
Page and document:
dl=<url>: document location (the page URL)dt=<title>: document title (the page title)dr=<referrer>: document referrer (where the user came from)
Device and browser:
uip=<ip>: user IP address (optional, usually omitted for privacy)ua=<user_agent>: user agent string (browser info)
Event-specific parameters: Anything you attach to an event, like value, currency, items. These use prefixes like ep., epn., up., upn. depending on scope and type.
GET versus POST: Small requests use GET (the query string). Larger requests (like a purchase with many items) use POST so the body can be longer. Both work; the parameters decode the same way.
What does not matter
You don't need to memorise all the parameters. Google's servers are forgiving. If you omit optional parameters, the hit still arrives. If you add unknown parameters, they're ignored. GA4 prioritises core fields like tid, cid, and en.
Also, parameter order doesn't matter. Whether you send tid first or last doesn't affect how Google processes it. Same with batched hits: the order of events in a POST body doesn't affect their processing.
And the specific endpoint doesn't vary. There's no different URL for different event types. Everything goes to /g/collect. Google looks at the en parameter to decide what kind of event it is.
Code example
Here's a simple page view request:
GET /g/collect?v=2&tid=G-A1B2C3D4E5&cid=123456789.1700000000&en=page_view&dl=https%3A%2F%2Fexample.com%2F&dt=Home%20Page
Breaking that down:
v=2: GA4 protocol versiontid=G-A1B2C3D4E5: your measurement IDcid=123456789.1700000000: browser's client IDen=page_view: event typedl=https://example.com/: page URL (URL-encoded)dt=Home%20Page: page title
And here's a purchase with custom parameters, sent as POST:
POST /g/collect?v=2&tid=G-A1B2C3D4E5&cid=123456789.1700000000&sid=67890
en=purchase&epn.value=99.99&ep.currency=USD&ep.transaction_id=txn123&up.customer_type=premium
The query string has the core fields. The body has the event name and custom parameters. Google decodes both together into one complete hit.
How Scalpel Tags shows it
Scalpel Tags decodes each parameter into a human-readable table. In the DevTools panel, you'll see groups like:
- Event:
en,event_timestamp - Properties:
tid,v - Session:
cid,sid,sct,seg - Page:
dl,dt,dr - Event parameters: any custom
ep.*orup.*fields - Device:
ua, and other device/browser fields
The raw request tab shows the full query string or body, uninterpreted. That's where you see the actual /g/collect?... URL or POST body.
This makes it easy to debug: you can read the human-readable decoded version, or dig into the raw format if you need to check exact encoding or spot a malformed parameter.