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:
| What | Value |
|---|---|
| Property | properties/563971554 (display name BoostUp, time zone Europe/Sofia, currency EUR) |
| Web data stream | properties/563971554/dataStreams/16328072699, default URI https://boostup.cloud |
| Measurement ID | G-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
| Scope | Needed for |
|---|---|
https://www.googleapis.com/auth/analytics.readonly | BoostUp 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.edit | Setup 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.