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.
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.
What this pack matches
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.missingvendor.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.emptyvendor.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.missingvendor.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.emptyvendor.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 |
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.missingvendor.google-analytics.body.name.emptyvendor.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.emptyvendor.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 | |
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 |
Validate a payload
pixellint validate json @payload.json --rulepack vendor/google-analytics
Or paste it into the playground. Same engine, in the browser, nothing sent anywhere.
cargo install pixellint
·
npm install pixellint