pixellint

vendor/google-analytics · vendor documented

GA4 Measurement Protocol wants microseconds

The collect endpoint returns 204. The event does not show up, or it shows up with no revenue, or it shows up attached to nobody. A lot of that is the timestamp.

timestamp_micros is a Unix timestamp in microseconds, sixteen digits. Date.now() is milliseconds. Multiply by 1000. Meta's Conversions API is the inverse clock: seconds, so the same Date.now() is too large rather than too small.

timestamp_micros is 16 digits

A 13-digit value is milliseconds. A 10-digit value is seconds. Multiply a millisecond timestamp by 1000, or a second timestamp by 1_000_000.

Rule: vendor.google-analytics.body.timestamp_micros.invalid

purchase needs ecommerce fields

Google's recommended purchase event needs currency, value, transaction_id, and items. Value without currency is dropped. refund, add_to_cart, and begin_checkout have their own required sets.

Rule: vendor.google-analytics.body.purchase_requires_ecommerce_fields

Events with no client_id never join a user

client_id is recommended rather than required. The collect endpoint will take the hit. Nothing joins it to a browser or a session. Warnings do not fail pixellint validate; this one should fail your run.

Rule: vendor.google-analytics.body.client_id.missing

What this pack matches

Hosts
google-analytics.com
Paths
/mp/collect, /debug/mp/collect
Vendor docs
developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events

Rules

Codes are stable. A finding in CI, MCP, or the playground lands on the same id.

Field Required What it checks Rule ids Source
api_secret required It authenticates the request and is created in the GA4 admin UI. Fix: Create a Measurement Protocol API secret for the stream and send it as `api_secret`. vendor.google-analytics.param.api_secret.missing
vendor.google-analytics.param.api_secret.empty
docs
measurement_id optional Web stream measurement IDs start with `G-`. Fix: Use the web stream measurement ID, or send `firebase_app_id` for an app stream. vendor.google-analytics.param.measurement_id.empty
vendor.google-analytics.param.measurement_id.invalid
docs
firebase_app_id optional It identifies an app stream and replaces `measurement_id`. vendor.google-analytics.param.firebase_app_id.empty docs
stream.identifier_missing required The request identifies no GA4 stream. Web streams send `measurement_id` and app streams send `firebase_app_id`. Fix: Add `measurement_id` for a web stream or `firebase_app_id` for an app stream. vendor.google-analytics.stream.identifier_missing docs
stream.identifier_ambiguous required The request sends both `measurement_id` and `firebase_app_id`. One request targets one stream type. Fix: Keep the identifier that matches the stream this event belongs to and drop the other. vendor.google-analytics.stream.identifier_ambiguous docs
client_id recommended It identifies the browser or device the event belongs to, and events without it are not joined to a user. Fix: Send the `_ga` cookie's client id, or a stable id of your own. vendor.google-analytics.body.client_id.missing
vendor.google-analytics.body.client_id.empty
docs
events optional Google documents the events array as the one required field of the request body. An empty array sends no events at all. Fix: Send an `events` array holding at least one event. docs
timestamp_micros optional Google documents this as a Unix timestamp in microseconds, not milliseconds. A 13-digit value is milliseconds and a 10-digit value is seconds. Fix: Multiply a millisecond timestamp by 1000, or a second timestamp by 1000000. vendor.google-analytics.body.timestamp_micros.empty
vendor.google-analytics.body.timestamp_micros.invalid
docs
non_personalized_ads deprecated Google marks it deprecated in the Measurement Protocol reference. Fix: Use the `consent` object instead. vendor.google-analytics.body.non_personalized_ads.deprecated docs
user_id optional It is a stable user identifier. Google documents `user_id` on the Measurement Protocol envelope. Fix: Send a stable `user_id` when you have one, or omit the field. vendor.google-analytics.body.user_id.empty docs
consent.ad_user_data optional It is consent for sending user data to Google for advertising. Google documents `GRANTED` or `DENIED`. Fix: Set `consent.ad_user_data` to `GRANTED` or `DENIED`. vendor.google-analytics.body.consent.ad_user_data.empty
vendor.google-analytics.body.consent.ad_user_data.invalid
docs
consent.ad_personalization optional It is consent for personalized advertising. Google documents `GRANTED` or `DENIED`. Fix: Set `consent.ad_personalization` to `GRANTED` or `DENIED`. vendor.google-analytics.body.consent.ad_personalization.empty
vendor.google-analytics.body.consent.ad_personalization.invalid
docs
validation_behavior optional It selects how strictly the Measurement Protocol validates the payload. Google documents `RELAXED` or `ENFORCE_RECOMMENDATIONS`. Fix: Send `RELAXED` or `ENFORCE_RECOMMENDATIONS`, or omit the field. vendor.google-analytics.body.validation_behavior.empty
vendor.google-analytics.body.validation_behavior.invalid
docs
ip_override optional It is the IP address Google Analytics uses to derive geography. Google documents it as an IP, not a digest. Fix: Send the visitor IP as `ip_override`, or send `user_location` instead. vendor.google-analytics.body.ip_override.empty docs
user_location.country_id optional It is the country in ISO 3166-1 alpha-2 form. Google documents `US` as the example. Fix: Send a two-letter country code such as `US`. vendor.google-analytics.body.user_location.country_id.empty
vendor.google-analytics.body.user_location.country_id.invalid
docs
user_properties.user_id forbidden Google reserves `user_id` as a user property name. Send `user_id` on the envelope instead. Fix: Move the identifier to the top-level `user_id` field. vendor.google-analytics.body.user_properties.user_id.forbidden docs
body.hashed_plaintext_field required `ip_override` looks like a SHA-256 digest, but Google documents it as an IP address. Fix: Send the raw IP address. vendor.google-analytics.body.hashed_plaintext_field docs
name required Google documents event names as 40 characters or fewer. Fix: Use a short name of letters, digits, and underscores, starting with a letter. vendor.google-analytics.body.name.missing
vendor.google-analytics.body.name.empty
vendor.google-analytics.body.name.invalid
docs
params.currency optional It is the ISO 4217 currency code, and Google requires it whenever `value` is set. Fix: Use the three-letter code, such as `USD`. vendor.google-analytics.body.params.currency.empty
vendor.google-analytics.body.params.currency.invalid
docs
params.value optional It is the monetary value of the event. Fix: Set it to the sum of price times quantity across the items, excluding shipping and tax. docs
params.transaction_id optional It identifies the transaction, and Google requires it for purchase and refund events. docs
params.items optional It lists the items the event covers. docs
params.session_id recommended It identifies the user session. Google documents `session_id` as a positive number, and events without it do not contribute correctly to session reports. Fix: Send `session_id` as digits, matching the Analytics session for this user. vendor.google-analytics.body.params.session_id.missing
vendor.google-analytics.body.params.session_id.empty
vendor.google-analytics.body.params.session_id.invalid
docs
params.engagement_time_msec recommended It is engagement duration in milliseconds since the preceding event. Google documents it so Realtime and average engagement time stay accurate. Fix: Send `engagement_time_msec` as milliseconds of engagement since the last event. vendor.google-analytics.body.params.engagement_time_msec.missing
vendor.google-analytics.body.params.engagement_time_msec.empty
vendor.google-analytics.body.params.engagement_time_msec.invalid
docs
body.value_requires_currency required The event carries a `value` with no `currency`. Google documents currency as required whenever value is set, and drops the revenue otherwise. Fix: Add `currency` as an ISO 4217 code alongside the value. vendor.google-analytics.body.value_requires_currency docs
body.purchase_requires_ecommerce_fields required A `purchase` event is missing fields Google documents as required for it, so it will not report revenue correctly. Fix: Add the required ecommerce parameters to the event. vendor.google-analytics.body.purchase_requires_ecommerce_fields docs
body.refund_requires_ecommerce_fields required A `refund` event is missing fields Google documents as required for it, so it will not report revenue correctly. Fix: Add the required ecommerce parameters to the event. vendor.google-analytics.body.refund_requires_ecommerce_fields docs
body.cart_requires_ecommerce_fields required Google documents `currency`, `value`, and `items` as required for this ecommerce event. Fix: Add `currency`, `value`, and the `items` array to the event. vendor.google-analytics.body.cart_requires_ecommerce_fields docs
body.view_item_requires_ecommerce_fields required Google documents `currency`, `value`, and `items` as required for this item event. Fix: Add `currency`, `value`, and the `items` array to the event. vendor.google-analytics.body.view_item_requires_ecommerce_fields docs

Validate a payload

pixellint validate json @payload.json --rulepack vendor/google-analytics

Try this failing payload in the playground. GA4 Measurement Protocol with a hashed ip_override.

{"client_id":"1234567.7654321","ip_override":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","events":[{"name":"page_view","params":{"session_id":"1710438591","engagement_time_msec":100}}]}

cargo install pixellint · npm install pixellint