GA4 Measurement Protocol, server-side.
Credentials
%{measurement_id: "G-XXXXXXXXXX", api_secret: "…"}The secret comes from GA4 Admin → Data Streams → Measurement Protocol API secrets. It is not the same thing as an API key and cannot be found anywhere else.
GA4 is the odd one out, in three ways
No PII hashing. There is no API for it. Sending an email address here
would be both useless and a policy violation, so user_data is dropped
entirely except for the client_id.
Identity is a client_id, not a match key. Normally the browser's _ga
cookie. Server-side it has to be forwarded by the caller; when it is not,
this derives a stable pseudo-id from the event_id so retries collapse onto
the same synthetic client. Those events record, but they will not stitch to
the visitor's browser session the way Meta's and TikTok's event_id dedup
does. That is a real limitation of the Measurement Protocol, not of this
client — forward client_id whenever you have it.
Success is 204, with no body. A 200 with an HTML body means the
request went somewhere else.
Custom event names are fine here
Unlike the ad platforms, GA4 accepts any event name and reports on it. Where
there is no recommended equivalent — contact, schedule — the canonical
name is sent in snake_case rather than dropped.