Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:32:40 +08:00
commit 0ea8352871
72 changed files with 30043 additions and 0 deletions

View File

@@ -0,0 +1,479 @@
# GA4 Measurement Protocol Complete API Guide
## API Overview
**Base URL:** `https://www.google-analytics.com/mp/collect`
**Debug URL:** `https://www.google-analytics.com/debug/mp/collect`
**Method:** POST
**Content-Type:** application/json
## Authentication
### API Secret Creation
1. **GA4 Admin****Data Streams**
2. Select your data stream
3. **Measurement Protocol API secrets** section
4. Click **Create**
5. Name: "Server tracking" (or descriptive name)
6. Click **Create**
7. **Copy secret value** (shown only once!)
**Storage:**
- Store secret securely (environment variables, secrets manager)
- Never commit to version control
- Regenerate if exposed
### Measurement ID
**Format:** `G-XXXXXXXXXX`
**Find in:**
- GA4 Admin → Data Streams → Web Stream
- "Measurement ID" field at top
## Request Format
### Complete URL
```
POST https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_API_SECRET
```
**Query Parameters:**
- `measurement_id`: GA4 Measurement ID
- `api_secret`: API secret from GA4
### Request Body Structure
```json
{
"client_id": "CLIENT_ID_HERE",
"user_id": "USER_123",
"timestamp_micros": "1234567890123456",
"user_properties": {
"user_tier": {
"value": "premium"
},
"lifetime_value": {
"value": 1250.50
}
},
"consent": {
"ad_storage": "granted",
"analytics_storage": "granted",
"ad_user_data": "granted",
"ad_personalization": "granted"
},
"events": [
{
"name": "purchase",
"params": {
"transaction_id": "T_12345",
"value": 99.99,
"currency": "USD",
"tax": 8.00,
"shipping": 5.00,
"items": [
{
"item_id": "SKU_123",
"item_name": "Product Name",
"price": 99.99,
"quantity": 1
}
]
}
}
]
}
```
## Field Specifications
### client_id (Required)
**Type:** String
**Format:** UUID recommended
**Purpose:** Unique client identifier
**Recommendations:**
- Use UUID v4 format
- Persist for same user across sessions
- Store in database for logged-in users
- Never use PII (email, name, etc.)
**Example:**
```python
import uuid
client_id = str(uuid.uuid4())
```
### user_id (Optional)
**Type:** String
**Purpose:** Cross-device tracking
**When to Use:**
- User is logged in
- Want to track across devices
- Have user identifier
**Important:**
- Don't use email or PII directly
- Hash or use internal ID
- Must also send client_id
### timestamp_micros (Optional)
**Type:** String (integer as string)
**Format:** Microseconds since Unix epoch
**Purpose:**
- Send historical events
- Backfill data
- Correct timing for delayed events
**Limits:**
- Max 3 days in past
- Max 72 hours in future
**Example:**
```python
import time
timestamp_micros = str(int(time.time() * 1_000_000))
```
### user_properties (Optional)
**Type:** Object
**Purpose:** Set user-level properties
**Format:**
```json
{
"property_name": {
"value": "property_value"
}
}
```
**Limits:**
- Max 25 user properties per request
- Property name ≤24 characters
- Value ≤36 characters
**Example:**
```json
{
"user_tier": {"value": "premium"},
"signup_date": {"value": "2024-01-15"},
"total_purchases": {"value": 12}
}
```
### consent (Optional)
**Type:** Object
**Purpose:** Set consent status
**Fields:**
- `ad_storage`: "granted" | "denied"
- `analytics_storage`: "granted" | "denied"
- `ad_user_data`: "granted" | "denied"
- `ad_personalization`: "granted" | "denied"
**Example:**
```json
{
"ad_storage": "denied",
"analytics_storage": "granted"
}
```
### events (Required)
**Type:** Array
**Limits:** Max 25 events per request
**Event Structure:**
```json
{
"name": "event_name",
"params": {
"parameter": "value"
}
}
```
**Event Name Rules:**
- Lowercase letters, numbers, underscores
- Max 40 characters
- Cannot start with number
- Avoid reserved names
**Event Parameters:**
- Max 25 parameters per event
- Parameter name ≤40 characters
- String value ≤100 characters
## Common Event Examples
### Page View
```json
{
"client_id": "client_123",
"events": [{
"name": "page_view",
"params": {
"page_location": "https://example.com/products",
"page_title": "Products Page",
"page_referrer": "https://google.com"
}
}]
}
```
### Purchase
```json
{
"client_id": "client_123",
"events": [{
"name": "purchase",
"params": {
"transaction_id": "T_12345",
"value": 99.99,
"currency": "USD",
"tax": 8.00,
"shipping": 5.00,
"items": [{
"item_id": "SKU_123",
"item_name": "Blue T-Shirt",
"price": 99.99,
"quantity": 1,
"item_category": "Apparel"
}]
}
}]
}
```
### Lead Generation
```json
{
"client_id": "client_123",
"events": [{
"name": "generate_lead",
"params": {
"currency": "USD",
"value": 50.00,
"form_name": "contact_form",
"lead_source": "website"
}
}]
}
```
### Sign Up
```json
{
"client_id": "client_123",
"user_id": "user_456",
"events": [{
"name": "sign_up",
"params": {
"method": "email"
}
}]
}
```
### Custom Event
```json
{
"client_id": "client_123",
"events": [{
"name": "subscription_renewed",
"params": {
"subscription_tier": "premium",
"renewal_amount": 29.99,
"currency": "USD",
"subscription_id": "SUB_789"
}
}]
}
```
## Debug Endpoint Validation
### Using cURL
```bash
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_123",
"events": [{
"name": "purchase",
"params": {
"transaction_id": "T_TEST",
"value": 99.99,
"currency": "USD"
}
}]
}'
```
### Valid Response
**Status:** 200 OK
**Body (Valid):**
```json
{
"validationMessages": []
}
```
**Empty array = No validation errors**
### Invalid Response
**Body (Invalid):**
```json
{
"validationMessages": [
{
"fieldPath": "events[0].name",
"description": "Event name must be 40 characters or fewer.",
"validationCode": "NAME_INVALID"
},
{
"fieldPath": "events[0].params.value",
"description": "Event parameter value is invalid.",
"validationCode": "VALUE_INVALID"
}
]
}
```
## Rate Limits and Quotas
**No Official Published Limits**
**Recommendations:**
- Batch events when possible (max 25/request)
- Implement exponential backoff for retries
- Monitor for rate limit responses
- Don't exceed ~1 request per second per client
**Best Practices:**
- Queue events on server
- Batch send periodically
- Retry failed requests with backoff
## Error Handling
### Python Example
```python
import requests
import time
def send_with_retry(payload, max_retries=3):
for attempt in range(max_retries):
try:
response = requests.post(ENDPOINT, json=payload)
if response.status_code == 204:
return True # Success
if response.status_code == 429: # Rate limited
wait = (2 ** attempt) + random.random()
time.sleep(wait)
continue
# Other error
print(f"Error: {response.status_code}")
return False
except requests.RequestException as e:
print(f"Request failed: {e}")
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
return False
```
### Node.js Example
```javascript
async function sendWithRetry(payload, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await axios.post(ENDPOINT, payload);
if (response.status === 204) {
return true; // Success
}
if (response.status === 429) { // Rate limited
const wait = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, wait));
continue;
}
console.error('Error:', response.status);
return false;
} catch (error) {
console.error('Request failed:', error.message);
if (attempt < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
}
}
}
return false;
}
```
## Testing Checklist
- [ ] API secret generated and stored securely
- [ ] Measurement ID correct format (G-XXXXXXXXXX)
- [ ] client_id generated (UUID format)
- [ ] Events validated using debug endpoint
- [ ] No validation errors in debug response
- [ ] Events appear in GA4 DebugView (with debug_mode=true)
- [ ] Parameters have correct data types
- [ ] No PII in parameters
- [ ] Error handling implemented
- [ ] Retry logic with exponential backoff
- [ ] Events batched when appropriate
- [ ] Consent parameters set correctly
## Common Issues
**Events Not Appearing:**
- Wrong Measurement ID
- Wrong API secret
- Events not validated first
- Network/firewall blocking requests
**Validation Errors:**
- Event name too long (>40 characters)
- Reserved event name used
- Invalid parameter values
- Wrong data types
**Missing Data:**
- client_id not consistent
- timestamp_micros out of range
- Parameters missing or misspelled