| name | ga4-measurement-protocol |
| description | Complete guide to GA4 Measurement Protocol for server-side event tracking including API authentication, request format, validation, and implementation examples. Use when implementing server-side tracking, sending events from backend, working with Measurement Protocol API, integrating CRM with GA4, or tracking offline conversions. Covers API secrets, debug endpoint, Python/Node.js/PHP examples, and rate limits. |
GA4 Measurement Protocol
Overview
The GA4 Measurement Protocol allows server-side event collection, enabling data transmission to GA4 from any HTTP-capable environment including backend servers, mobile apps, kiosks, and IoT devices.
When to Use This Skill
Invoke this skill when:
- Implementing server-side event tracking
- Sending events from backend/server environments
- Tracking offline conversions or transactions
- Integrating CRM systems with GA4
- Sending events from mobile app backends
- Tracking server-to-server transactions
- Implementing purchase tracking from payment processors
- Tracking subscription renewals or recurring payments
- Sending lead generation events from forms backend
- Implementing custom server-side analytics
- Working with headless CMS or API-first architectures
- Debugging Measurement Protocol requests
- Validating event payloads before production
Core Capabilities
API Endpoints
Production Endpoint:
POST https://www.google-analytics.com/mp/collect
Debug Endpoint (Validation):
POST https://www.google-analytics.com/debug/mp/collect
Key Difference: Debug endpoint returns validation messages without storing data.
Authentication Requirements
Two Credentials Required:
Measurement ID (format:
G-XXXXXXXXXX)- Find in: GA4 Admin → Data Streams → Web Stream details
API Secret
- Generate in: Data Streams → Measurement Protocol API secrets → Create
Generating API Secret:
- GA4 Admin → Data Streams
- Click your data stream
- Scroll to "Measurement Protocol API secrets"
- Click "Create"
- Enter nickname (e.g., "Server-side tracking")
- Click "Create"
- Copy secret immediately (cannot retrieve later)
Request Structure
URL Format:
https://www.google-analytics.com/mp/collect?measurement_id={MEASUREMENT_ID}&api_secret={API_SECRET}
Headers:
Content-Type: application/json
Body (JSON):
{
"client_id": "unique_client_identifier",
"user_id": "optional_user_id",
"timestamp_micros": "1234567890123456",
"user_properties": {
"property_name": {
"value": "property_value"
}
},
"consent": {
"ad_storage": "granted",
"analytics_storage": "granted"
},
"events": [
{
"name": "event_name",
"params": {
"parameter_name": "parameter_value",
"value": 123.45,
"currency": "USD"
}
}
]
}
Required Fields
- client_id: Unique identifier for client (UUID recommended)
- events: Array of event objects (max 25 events per request)
- events[].name: Event name (string, ≤40 characters)
Optional Fields
- user_id: User identifier for cross-device tracking
- timestamp_micros: Event timestamp in microseconds (UTC)
- user_properties: User-level properties
- consent: Consent status (ad_storage, analytics_storage)
Common Event Parameters
| Parameter | Type | Description |
|---|---|---|
session_id |
string | Session identifier |
engagement_time_msec |
integer | Engagement time in milliseconds |
page_location |
string | Full URL |
page_title |
string | Page title |
value |
number | Monetary value |
currency |
string | ISO 4217 currency code (USD, EUR) |
transaction_id |
string | Unique transaction ID |
items |
array | E-commerce items array |
Python Implementation
Using Requests Library:
import requests
import json
import uuid
MEASUREMENT_ID = "G-XXXXXXXXXX"
API_SECRET = "your_api_secret"
ENDPOINT = f"https://www.google-analytics.com/mp/collect?measurement_id={MEASUREMENT_ID}&api_secret={API_SECRET}"
def send_event(event_name, params=None):
payload = {
"client_id": str(uuid.uuid4()),
"events": [{
"name": event_name,
"params": params or {}
}]
}
response = requests.post(
ENDPOINT,
headers={"Content-Type": "application/json"},
data=json.dumps(payload)
)
return response.status_code == 204
# Send page view
send_event("page_view", {
"page_location": "https://example.com/page",
"page_title": "Example Page"
})
# Send purchase
send_event("purchase", {
"transaction_id": "T_12345",
"value": 99.99,
"currency": "USD",
"items": [{
"item_id": "SKU_123",
"item_name": "Product Name",
"price": 99.99,
"quantity": 1
}]
})
Using ga4mp Library:
# Install: pip install ga4mp
from ga4mp import GtagMP
ga = GtagMP(
measurement_id="G-XXXXXXXXXX",
api_secret="your_api_secret",
client_id="unique_client_id"
)
# Send event
ga.send_event(
event_name="purchase",
event_parameters={
"transaction_id": "T_12345",
"value": 99.99,
"currency": "USD",
"items": [{
"item_id": "SKU_123",
"item_name": "Product Name",
"price": 99.99,
"quantity": 1
}]
}
)
Node.js Implementation
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');
const MEASUREMENT_ID = 'G-XXXXXXXXXX';
const API_SECRET = 'your_api_secret';
const ENDPOINT = `https://www.google-analytics.com/mp/collect?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}`;
async function sendEvent(eventName, params = {}) {
const payload = {
client_id: uuidv4(),
events: [{
name: eventName,
params: params
}]
};
try {
const response = await axios.post(ENDPOINT, payload, {
headers: { 'Content-Type': 'application/json' }
});
return response.status === 204;
} catch (error) {
console.error('Error sending event:', error);
return false;
}
}
// Send purchase event
sendEvent('purchase', {
transaction_id: 'T_12345',
value: 99.99,
currency: 'USD',
items: [{
item_id: 'SKU_123',
item_name: 'Product',
price: 99.99,
quantity: 1
}]
});
PHP Implementation
<?php
// Using php-GA4-Measurement-Protocol library
// Install: composer require br33f/php-ga4-measurement-protocol
use Br33f\Ga4\MeasurementProtocol\Dto\Event\PurchaseEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Request\MeasurementRequest;
use Br33f\Ga4\MeasurementProtocol\Service;
$measurementId = 'G-XXXXXXXXXX';
$apiSecret = 'your_api_secret';
$service = new Service($apiSecret, $measurementId);
$event = new PurchaseEvent();
$event->setTransactionId('T_12345')
->setValue(99.99)
->setCurrency('USD');
$request = new MeasurementRequest();
$request->setClientId('unique_client_id')
->addEvent($event);
$service->send($request);
?>
Validation with Debug Endpoint
Send to Debug Endpoint:
curl -X POST "https://www.google-analytics.com/debug/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{
"client_id": "test_client",
"events": [{
"name": "test_event",
"params": {
"test_param": "test_value"
}
}]
}'
Response Format:
{
"validationMessages": [
{
"fieldPath": "events[0].name",
"description": "Event name must be 40 characters or fewer",
"validationCode": "NAME_INVALID"
}
]
}
Empty Response = Valid:
- No validationMessages = payload valid
- Status 200 = request processed
Validation Codes
| Code | Description | Fix |
|---|---|---|
NAME_INVALID |
Invalid event/parameter name | Use lowercase, underscores, ≤40 chars |
NAME_RESERVED |
Reserved name used | Check GA4 reserved names list |
VALUE_INVALID |
Invalid parameter value | Check data type, format |
VALUE_REQUIRED |
Required value missing | Add required parameter |
VALUE_OUT_OF_BOUNDS |
Value exceeds limits | Check numeric ranges |
EXCEEDED_MAX_ENTITIES |
Too many events | Max 25 events per request |
Best Practices
Always Validate First:
- Use debug endpoint before production
- Test with sample data
- Verify response is empty (valid)
Use Consistent client_id:
- Same user = same client_id across sessions
- Store in database for logged-in users
- Use UUID format for anonymity
Include session_id:
- Maintain session continuity
- Generate unique session ID
- Keep consistent within session
Batch Events:
- Send up to 25 events per request
- More efficient than individual requests
- Reduces API calls
Handle Errors Gracefully:
- Implement retry logic with exponential backoff
- Log failed requests
- Queue events for retry
Set Proper Timestamps:
- Use
timestamp_microsfor historical data - Convert to microseconds:
timestamp_ms * 1000 - Max 3 days in past, 72 hours in future
- Use
Respect Consent:
- Set consent parameters appropriately
- Match frontend consent status
- Required for GDPR compliance
Integration with Other Skills
- ga4-setup - Initial GA4 property and data stream setup
- ga4-debugview - Testing Measurement Protocol events in DebugView
- ga4-recommended-events - Sending recommended event structures
- ga4-custom-events - Server-side custom event implementation
- ga4-user-tracking - Implementing User ID server-side
- ga4-privacy-compliance - Setting consent parameters correctly
- ga4-bigquery - Analyzing server-side events in BigQuery
References
- references/measurement-protocol-complete.md - Full API reference and examples
- references/authentication-setup.md - API secrets and authentication guide
- references/event-validation.md - Validation patterns and troubleshooting
- references/implementation-examples.md - Real-world implementation patterns (Python, Node.js, PHP)
- references/rate-limits-best-practices.md - Rate limits, batching, and optimization
Quick Reference
Generate API Secret: Admin → Data Streams → Measurement Protocol API secrets → Create
Endpoint:
- Production:
/mp/collect - Debug:
/debug/mp/collect
Required Fields:
- client_id (UUID recommended)
- events array
- event name
Max Limits:
- 25 events per request
- 40 characters per event name
- 25 event parameters per event
- 25 user properties per request
Validation:
- Send to debug endpoint
- Empty response = valid
- Check validationMessages array