GA4 setup

This runbook creates a Google Analytics 4 property and a web data stream, installs the tag on the site and connects the property to a brand in BoostUp. It follows the setup we ran for boostup.cloud on 2026-11-12, and the read test we ran against the existing prolevelsolutions.com property.

Result for boostup.cloud:

WhatValue
Propertyproperties/563971554 (display name BoostUp, time zone Europe/Sofia, currency EUR)
Web data streamproperties/563971554/dataStreams/16328072699, default URI https://boostup.cloud
Measurement IDG-M1X7479GKJ

The measurement ID is public (it is in every page's HTML), so it is fine in configuration. The OAuth token is not.

Before you start

You need:

  • Editor access to the Google Analytics account that should own the property.
  • The Google Cloud project and OAuth client from the Google Search Console setup, if you use the API or connect the property to BoostUp.

OAuth scopes

ScopeNeeded for
https://www.googleapis.com/auth/analytics.readonlyBoostUp reading reports (Data API) and listing properties (Admin API). The only Analytics scope the token you give BoostUp needs.
https://www.googleapis.com/auth/analytics.editSetup only: creating the property and the data stream.

In Google Cloud Console, under APIs and services, then Library, enable Google Analytics Admin API and Google Analytics Data API.

1. Find the Analytics account

By hand: in Google Analytics, open Admin; the account is shown at the top of the left column.

By API:

GET https://analyticsadmin.googleapis.com/v1beta/accountSummaries

Returned 200 with one account, Pro Level Solutions LTD (accounts/<account id>), holding one property, properties/524312236 (Pro Level Solutions, canEdit: true). We created the BoostUp property in the same account.

2. Create the property

By hand: Admin, Create, Property. Enter the property name, reporting time zone and currency, then the business details (industry, size) and objectives.

By API:

POST https://analyticsadmin.googleapis.com/v1beta/properties
Content-Type: application/json

{
  "parent": "accounts/<account id>",
  "displayName": "BoostUp",
  "timeZone": "Europe/Sofia",
  "currencyCode": "EUR",
  "industryCategory": "BUSINESS_AND_INDUSTRIAL_MARKETS"
}

Returned 200:

{
  "name": "properties/563971554",
  "parent": "accounts/<account id>",
  "displayName": "BoostUp",
  "industryCategory": "BUSINESS_AND_INDUSTRIAL_MARKETS",
  "timeZone": "Europe/Sofia",
  "currencyCode": "EUR",
  "serviceLevel": "GOOGLE_ANALYTICS_STANDARD",
  "propertyType": "PROPERTY_TYPE_ORDINARY"
}

The time zone matters: GA4 days, and so every BoostUp daily report, start at midnight in this zone.

3. Create the web data stream

By hand: in the new property, Admin, Data collection and modification, Data streams, Add stream, Web. Enter https://boostup.cloud and a stream name, keep Enhanced measurement on, and create it. Copy the Measurement ID shown on the stream page.

By API, check first that the property has no stream yet:

GET https://analyticsadmin.googleapis.com/v1beta/properties/563971554/dataStreams

Returned 200 with {}. Google's JSON leaves out empty lists, so an empty object means "no streams". Then:

POST https://analyticsadmin.googleapis.com/v1beta/properties/563971554/dataStreams
Content-Type: application/json

{
  "type": "WEB_DATA_STREAM",
  "displayName": "boostup.cloud",
  "webStreamData": { "defaultUri": "https://boostup.cloud" }
}

Returned 200:

{
  "name": "properties/563971554/dataStreams/16328072699",
  "type": "WEB_DATA_STREAM",
  "displayName": "boostup.cloud",
  "webStreamData": { "measurementId": "G-M1X7479GKJ", "defaultUri": "https://boostup.cloud" }
}

A stream created by API starts with the default enhanced measurement settings. Review them once in the stream page.

4. Install the tag

We do not use Google Tag Manager: GET https://tagmanager.googleapis.com/tagmanager/v2/accounts returned 200 with {} (no Tag Manager accounts). The site loads the Google tag directly.

BoostUp's web app reads the measurement ID from NEXT_PUBLIC_GA4_ID:

NEXT_PUBLIC_GA4_ID=G-M1X7479GKJ

Variables starting with NEXT_PUBLIC_ are inlined into the browser bundle when the app is built, so set it before next build (for the Docker image, as a build argument, the same way as NEXT_PUBLIC_SENTRY_DSN). Changing it on a running container does nothing until the next build. Do not set it for development or staging builds, so test traffic stays out of the property.

An empty value turns GA4 off. The tag loads only after the visitor accepts analytics cookies in the consent banner, so accept them before you test.

After deploying, open the site and check Reports, Realtime in GA4 (or Admin, DebugView with the Google Analytics debugger). Realtime shows hits within a minute; standard reports take up to a day or two.

5. Mark key events

GA4 counts nothing as a conversion until you say so. In Admin, Data display, Key events, mark the events that matter (for example generate_lead or a custom sign_up).

Metric naming. The GA4 interface now calls conversions key events. The Data API still accepted the old metric name conversions in our run (it came back as TYPE_FLOAT); its current name is keyEvents. If a report request starts failing on conversions, switch it to keyEvents. Either way the value stays 0 until key events are marked.

6. Check that data comes back

A new property has no data yet, so we tested the token and the report shape against prolevelsolutions.com:

POST https://analyticsdata.googleapis.com/v1beta/properties/524312236:runReport
Content-Type: application/json

{
  "dateRanges": [{ "startDate": "28daysAgo", "endDate": "yesterday" }],
  "metrics": [{ "name": "sessions" }, { "name": "activeUsers" }, { "name": "conversions" }],
  "dimensions": [{ "name": "sessionDefaultChannelGroup" }]
}

Returned 200 with rowCount: 5, one row per channel (Direct, Organic Search, AI Assistant, Referral, Unassigned), conversions 0 in every row, and metadata with currencyCode: EUR and timeZone: Europe/Sofia. Metric values come back as strings, for example { "value": "100" }; convert them before doing arithmetic.

Note the AI Assistant channel: GA4 groups traffic from AI chat products there, separately from Referral.

Finally, the same accountSummaries call listed both properties, and listing the BoostUp streams again returned the G-M1X7479GKJ stream:

GET https://analyticsadmin.googleapis.com/v1beta/properties/563971554/dataStreams

7. Connect the property to a brand in BoostUp

A platform admin imports the Google credentials with POST /admin/integrations/import-google. One call carries both the Search Console property and the GA4 property; see step 8 of the Google Search Console setup for the full body. The GA4 part:

{ "ga4_property_id": "properties/563971554" }

ga4_property_id accepts properties/524312236 or just 524312236. The refresh token in token must carry analytics.readonly (and webmasters.readonly for Search Console). The token JSON is a secret: send it only to this endpoint, never to a ticket, a chat or the repository.