De GA4 Measurement Protocol geeft een 2xx-status terug voor bijna alles wat je erheen post: een geldig event, een verkeerd gespeld event, een event zonder client_id, een geheim dat je zelf verzonnen hebt. De statuscode is dus nutteloos om te debuggen. Om GA4 Measurement Protocol-aanroepen te debuggen, stuur je dezelfde body naar de validatieserver op /debug/mp/collect, lees je de validationMessages die terugkomen, repareer je wat ze noemen en bevestig je pas daarna de aankomst in DebugView of Realtime.
Die laatste stap is belangrijker dan mensen verwachten. Waarom? De validatieserver controleert je API-geheim niet, dus een payload kan de validatie doorstaan, een 204 krijgen van het echte endpoint en toch nooit in je property landen. Ik heb een team daar een week aan kwijt zien raken voordat iemand het geheim nog eens kopieerde.
Als je serverevents inzet om campagnes te volgen, behandelt de cornerstone-gids over UTM's end-to-end volgen wat er in de link moet staan voordat dit alles begint. Dit artikel gaat over het moment waarop je het event verstuurt en er niets verschijnt.
Waarom de Measurement Protocol op alles 2xx antwoordt
De protocolreferentie van Google is er duidelijk over: het endpoint geeft een 2xx-statuscode terug als het HTTP-verzoek is ontvangen, en geeft geen fout als de payload misvormd is of de data niet wordt verwerkt. Verzamelen is fire-and-forget. Je server wacht nooit op verwerking.
Dit hebben we zelf gecontroleerd. Een POST naar /mp/collect met de body {"garbage":true}, een nep-measurement-ID en een verzonnen geheim kwam terug als HTTP 204 met een lege body. Precies hetzelfde als bij een perfect event.
Retrylogica op basis van statuscodes vangt netwerkfouten op. Verder niets. Ze kan je niet vertellen dat GA4 je events heeft laten vallen. Daarvoor heb je het tweede endpoint nodig.
De validatieserver op /debug/mp/collect gebruiken
De validatieserver van de measurement protocol staat op dezelfde host. Dezelfde query-string, dezelfde body:
curl -s -X POST \
"https://www.google-analytics.com/debug/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{"events":[{"name":"session_start","params":{"firebase_x":1}}]}'
Hij antwoordt met 200 en een JSON-body in plaats van een lege. Die testpayload heeft drie problemen, en de server meldt het eerste dat hij tegenkomt:
{
"validationMessages": [
{
"fieldPath": "client_id",
"description": "Measurement requires a client_id.",
"validationCode": "VALUE_REQUIRED"
}
]
}
Voeg een client_id toe en hij gaat door naar NAME_RESERVED voor session_start; hernoem het event en het voorvoegsel firebase_ is aan de beurt. Elk bericht heeft een fieldPath, een beschrijving en een code. De codes die Google documenteert zijn onder meer VALUE_INVALID, VALUE_REQUIRED, NAME_INVALID, NAME_RESERVED, VALUE_OUT_OF_BOUNDS, EXCEEDED_MAX_ENTITIES en NAME_DUPLICATED.
Een lege array, "validationMessages": [ ], betekent dat de structuur in orde is. Niets wat naar /debug/mp/collect gaat wordt opgeslagen. Bestook het tijdens de ontwikkeling zoveel je wilt; onthoud alleen dat een debugaanroep nooit bewijst dat data is aangekomen.
Wat de validatieserver niet controleert
De meeste tutorials slaan dit deel over. De pagina over het valideren van events van Google zegt ronduit dat de validatieserver het api_secret niet valideert. In onze test op 22 september 2026 controleerde hij het measurement ID ook niet: G-FAKE123 met het geheim nonsense gaf een lege validationMessages-array terug.
Een fout geheim komt erdoor. Een ingetrokken geheim ook, of een van een andere datastream, of een typefout in G-. Het api_secret van de measurement protocol is de meest voorkomende reden die ik zie voor "geldige payload, geen data", en je kunt het alleen bevestigen door een event te zien aankomen.
Het tweede gat is de validatiemodus. Standaard draait de server in de modus RELAXED, en in die modus liet hij twee dingen door die de eigen limieten van Google verbieden: een event met 26 parameters en een parameterwaarde van 120 tekens. Voeg "validation_behavior": "ENFORCE_RECOMMENDATIONS" toe aan de debugbody en beide falen, met EXCEEDED_MAX_ENTITIES en VALUE_TOO_LONG. Ik valideer altijd in de strikte modus, ook als productie relaxed blijft. Het is het verschil tussen een controle en een rubberen stempel.
Serverevents zien in GA4 DebugView en Realtime
Zodra de payload valideert, stuur je hem naar het echte endpoint en kijk je hoe hij landt. Twee weergaven werken voor serverevents, en standaardrapporten horen daar niet bij, omdat die 24 tot 48 uur kunnen achterlopen.
DebugView vraagt per event om een opt-in. De verificatiegids van Google vraagt om "debug_mode": 1 (of true) in de params van het event plus een positieve engagement_time_msec. Alleen events met de vlag verschijnen, dus een batch waarin één event hem mist ziet er half leeg uit. Open Beheer, dan DebugView, en geef het een minuut.
Realtime heeft niets extra's nodig. Scrol naar de kaart "Event count by Event name" en zoek je event. Google merkt op dat session_id en engagement_time_msec ertoe doen voor gebruikersactiviteit in Realtime, dus als een event valideert maar Realtime leeg blijft, controleer dan eerst die twee.
Nog een kanttekening uit dezelfde gids: voor webstreams staat er dat een geldig event een client_id gebruikt die gtag.js al heeft gebruikt. Synthetische ID's worden nog steeds geteld, elk als een eigen gebruiker, maar ze sluiten nooit aan op een browsersessie, en in een rapport rond sessies verschijnt dat als een lange staart van gebruikers met één event die je niet kunt verklaren tot je weet waar ze vandaan komen. Daarover meer in de volgende sectie. Ontbreekt er campagnedata in plaats van events, dan loopt UTM-parameters verschijnen niet in GA4 de DebugView-kant van dat probleem door.
Veelvoorkomende payloadfouten en wat de validator zegt
De meeste kapotte events vallen in een handvol patronen. Hier staan ze met wat de validatieserver teruggaf toen we ze op 22 september 2026 verstuurden:
| Fout | Antwoord validator (standaardmodus) | Oplossing |
|---|---|---|
Geen client_id in de body | VALUE_REQUIRED op client_id | Stuur de _ga-waarde, of een stabiele eigen ID |
Event met de naam session_start | NAME_RESERVED | Hernoemen; first_visit en user_engagement zijn gereserveerd |
Param met voorvoegsel firebase_ | NAME_RESERVED op events.params | Laat de voorvoegsels _, firebase_, ga_, google_ weg |
Event met de naam Link Click | NAME_INVALID | Letters, cijfers en _; begin met een letter |
| 26 params, of een waarde van 120 tekens | Lege array (strikte modus vangt het) | Houd je aan 25 params en waarden van 100 tekens |
timestamp_micros ouder dan 72 uur | Lege array (strikte modus wijst af) | Relaxed modus herschrijft het naar 72 uur geleden |
engagement_time_msec ontbreekt | Lege array | Zet een positief getal, anders kan Realtime leeg blijven |
Twee rijen verdienen commentaar. Het voorvoegsel ga_ is volgens de referentie gereserveerd, maar de validator accepteerde ga_session_id in beide modi toen we het probeerden, dus beschouw een lege array niet als toestemming. En de limiet van 100 tekens per waarde vangt voortdurend volledige bestemmings-URL's: een landingspagina met vijf UTM-tags is vaak langer.
De client_id zelf is het subtiele geval. Elke string komt door de relaxed validatie, maar de strikte modus wees zowel c1 als elido-12-4711 af met "It should be in _ga-waarde; de gids over GA4 server-side tracking legt dat aansluiten in detail uit.
Hoe de GA4-forwarder en Test connection van Elido dit gebruiken
Elido stuurt kliks op korte links server-side door naar GA4. Je plakt een Measurement ID en een Measurement Protocol API-geheim in de GA4-kaart onder Integraties, en vanaf dan wordt elke klik in die werkruimte één link_click-event:
{
"client_id": "elido-12-4711",
"events": [
{
"name": "link_click",
"params": {
"workspace_id": 12,
"link_id": 4711,
"slug": "spring-26",
"country": "DE",
"device": "mobile",
"destination": "https://shop.example/spring?utm_source=newsletter",
"engagement_time_msec": 100
}
}
]
}
De client_id is elido-<werkruimte>-<link>, dus elke klik op één link leest als dezelfde GA4-gebruiker en geen enkele sluit aan op een browsersessie. Dat is de eerlijke afweging als je dit zonder cookie doet: totalen, en uitsplitsingen per slug, land en apparaat werken; gebruikersaantallen en sessietrechters niet. Land en apparaat zijn gewone eventparameters. Registreer ze eerst als aangepaste dimensies. En een destination van meer dan 100 tekens loopt tegen de limiet uit de tabel hierboven aan, dus rapporteer in plaats daarvan op slug of link_id.
De knop Test connection volgt de volgorde die dit artikel aanraadt:
Eerst stuurt hij een synthetische link_click, gemarkeerd met elido_test: true, naar /debug/mp/collect. Is validationMessages niet leeg, dan faalt de test en toont hij de beschrijvingen van Google woordelijk. Is de array leeg, dan gaat hetzelfde event echt naar /mp/collect. Onder de knop zie je het antwoord van de leverancier (HTTP-status, het debugendpoint met je measurement ID maar nooit het geheim, en de body van Google) plus een opmerking dat de Measurement Protocol het API-geheim niet verifieert.
Groen betekent dus "geldig, en Google heeft het ontvangen". Niet "je property heeft het". Het testevent draagt debug_mode: 1 en elido_test: true, dus het verschijnt in DebugView als link_click; Realtime werkt ook. Wil je klikevents in GA4 zonder tag op elke landingspagina, start dan een werkruimte en wijs de GA4-kaart eerst naar een testproperty.
Een GA4 Measurement Protocol-debugvolgorde die werkt
Als GA4-events niet verschijnen, loop dit dan in deze volgorde af en stop bij de eerste fout:
- Stuur de body naar
/debug/mp/collectmetvalidation_behavioropENFORCE_RECOMMENDATIONS. Los elk bericht op. - Kopieer het API-geheim opnieuw uit Beheer, Datastreams, je webstream, Measurement Protocol API-geheimen. Controleer dat het van dezelfde stream is als het
G--ID. - Stuur één event naar
/mp/collectmetdebug_mode: 1en kijk twee minuten naar DebugView. - Haal de vlag weg en controleer Realtime, geef standaardrapporten dan een dag of twee.
Stap 2 is waar het meeste van mijn eigen debugwerk eindigt. De probleemoplossingspagina van Google opent met dezelfde drie vragen: het juiste geheim, nog geldig, exact gekopieerd. Als de cijfers eindelijk stromen en nog steeds niet met je linktellingen overeenkomen, legt klikken op korte links versus GA4-sessies het verschil uit, en server-side conversietracking behandelt de conversie-events die meestal volgen. Dezelfde gewoonte van eerst valideren en dan verifiëren geldt voor de andere bestemmingen op de pagina conversietracking.
Gerelateerd op de blog
- GA4 server-side tracking via redirects - inloggegevens, payloadvorm en het aansluiten van client_id.
- Mixpanel vs GA4 voor linkanalytics - welke tool je klikdata moet bewaren.
- Mixpanel-linktracking - dezelfde forwarder gericht op Mixpanel, met zijn eigen eigenaardigheid van 200 voor alles.
- UTM-parameters verschijnen niet in GA4 - de campagnedata-versie van dit debugprobleem.
- Server-side conversietracking - GA4, Meta CAPI en TikTok naast elkaar.
Veelgestelde vragen
Hoe debug ik GA4 Measurement Protocol-events?
Stuur dezelfde payload naar https://www.google-analytics.com/debug/mp/collect in plaats van naar /mp/collect. De validatieserver antwoordt met een validationMessages-array die het veld noemt, het probleem beschrijft en een code geeft zoals NAME_RESERVED of VALUE_REQUIRED. Een lege array betekent dat de structuur geldig is. Stuur daarna het echte event met debug_mode op 1 en kijk hoe het in DebugView binnenkomt.
Waarom verschijnen mijn Measurement Protocol-events niet in GA4?
De gebruikelijke oorzaken zijn een verkeerd of ingetrokken API-geheim, een Measurement ID van een andere stream, een ontbrekende client_id, of te vroeg in de standaardrapporten kijken. Het endpoint geeft voor al deze gevallen 2xx terug, dus de statuscode zegt niets. Valideer de payload, controleer het geheim met de hand en kijk dan in Realtime of DebugView in plaats van in rapporten, die een dag of langer kunnen achterlopen.
Controleert de GA4-validatieserver het API-geheim?
Nee. In de documentatie van Google staat dat de validatieserver het api_secret niet valideert, en in onze eigen test op 22 september 2026 accepteerde hij ook een Measurement ID dat bij geen enkele property hoort. Een lege validationMessages-array zegt alleen dat de JSON goed gevormd is. Of het geheim bij de stream past, kun je alleen bevestigen door het event in GA4 te zien aankomen.
Verschijnen events die naar /debug/mp/collect gaan in GA4-rapporten?
Nee. De validatieserver controleert de payload en gooit hem weg, dus niets wat je daarheen stuurt bereikt rapporten, Realtime of DebugView. Om een event in DebugView te zien, stuur je het naar het gewone /mp/collect-endpoint met een debug_mode-parameter van 1 en een positieve engagement_time_msec, zoals de verificatiegids van Google beschrijft.
Welke client_id moet ik meesturen met de GA4 Measurement Protocol?
Voor een webstream wil Google de client_id die de GA4-tag op je site genereert, de waarde in de _ga-cookie, zodat serverevents aansluiten op de browsersessie. Elke string slaagt in de standaardvalidatie, maar de strengere modus ENFORCE_RECOMMENDATIONS wijst ID's af die niet het formaat getal.getal hebben. Een verzonnen ID telt events nog steeds mee; hij sluit alleen nooit aan op een browsersessie.
Hoeveel parameters mag een Measurement Protocol-event hebben?
Vijfentwintig parameters per event en 25 events per verzoek, met namen tot 40 tekens en waarden tot 100 tekens op een standaardproperty, of 500 bij GA4 360. De standaardvalidatiemodus markeerde in onze test geen 26e parameter en geen waarde van 120 tekens; validation_behavior op ENFORCE_RECOMMENDATIONS zetten bij de debugaanroep deed dat wel.
Probeer Elido
Plak een URL, krijg een werkende korte link
Geen aanmelding nodig. Link blijft 30 dagen actief. Meld je aan om hem voor altijd te bewaren.
Geen account nodig. Meld je gratis aan voor 50 links per maand.