Skip to main content
RapidDev - Software Development Agency
bubble-integrationsBubble API Connector

Google Analytics

Connect Bubble to Google Analytics in two ways: send events from your Bubble app to GA4 using the Measurement Protocol API_SECRET (a server-side POST that Bubble's API Connector handles natively), or read report data back into Bubble using the GA4 Data API with a Google service account OAuth token cached in the Bubble database. Both paths run server-side — no external proxy needed.

What you'll learn

  • The difference between the GA4 Measurement ID (G-XXXX, for browser scripts only) and the Measurement Protocol API_SECRET (for server-side event sending)
  • How to configure an API Connector POST call to send custom events to GA4 from any Bubble Workflow
  • How to verify that events arrived in GA4 DebugView — because the Measurement Protocol always returns 200 even for malformed events
  • How to use the refresh_token workaround for reading GA4 report data without JWT signing in Bubble
  • How to chain a token-refresh call and a GA4 Data API call in a Bubble Workflow
  • How to cache report data in the Bubble database to reduce WU costs and API quota usage
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate20 min read2–4 hoursAnalyticsLast updated July 2026RapidDev Engineering Team
TL;DR

Connect Bubble to Google Analytics in two ways: send events from your Bubble app to GA4 using the Measurement Protocol API_SECRET (a server-side POST that Bubble's API Connector handles natively), or read report data back into Bubble using the GA4 Data API with a Google service account OAuth token cached in the Bubble database. Both paths run server-side — no external proxy needed.

Quick facts about this guide
FactValue
ToolGoogle Analytics
CategoryAnalytics
MethodBubble API Connector
DifficultyIntermediate
Time required2–4 hours
Last updatedJuly 2026

Two Directions: Sending Events to GA4 and Reading Reports from GA4

The Bubble–Google Analytics integration covers two completely different workflows that share almost no setup steps.

Direction 1 — SEND events: Your Bubble app fires a server-side POST to GA4's Measurement Protocol endpoint every time a user takes a meaningful action (signup, purchase, feature use). This is simple to configure: one API Connector call, one Private api_secret parameter. GA4 records the event within seconds, visible in DebugView almost immediately (standard reports update within 24–48 hours).

Direction 2 — READ reports: Your Bubble app queries GA4's Data API to display web traffic metrics (page views, sessions, top pages, conversion rates) inside a Bubble dashboard. This path is harder because it requires a Google service account, and Bubble's API Connector cannot sign the RSA JWT that the standard service account flow requires. The workaround is the refresh_token path: use the OAuth Playground to generate a long-lived offline refresh_token, store it in Bubble's API Connector as a Private parameter, and exchange it for a short-lived access_token via a two-call chain. This requires a paid Bubble plan for the scheduled token refresh Backend Workflow.

Unlike Retool (which has a pre-built GA4 resource connector) or FlutterFlow (which has a native Firebase Analytics toggle for sending events), Bubble uses the API Connector for both paths — giving you full control but requiring manual configuration.

Integration method

Bubble API Connector

Bubble's API Connector sends events to GA4 via the Measurement Protocol (API_SECRET in a Private header) and reads report data via the GA4 Data API (service account Bearer token cached in the Bubble database).

Prerequisites

  • A GA4 property with at least one data stream set up (go to GA4 → Admin → Data Streams to confirm)
  • The API Connector plugin installed in your Bubble app (Plugins tab → Add plugins → search 'API Connector' by Bubble → Install)
  • For sending events: a Measurement Protocol API secret (GA4 Admin → Data Streams → your stream → Measurement Protocol API secrets → Create)
  • For reading report data: a Google Cloud project with the Google Analytics Data API enabled, a service account created in that project, and the service account granted Viewer access to your GA4 property
  • For reading report data on a paid Bubble plan: an offline refresh_token generated via the Google OAuth Playground (https://developers.google.com/oauthplayground)

Step-by-step guide

1

Create a Measurement Protocol API Secret in GA4

The Measurement Protocol uses a dedicated api_secret credential that is separate from any OAuth token. It is a low-privilege key tied to a specific GA4 data stream — it can only send events to that stream, it cannot read any data. To create one, go to your GA4 property → Admin (gear icon at the bottom-left) → Data Streams → click your web stream → scroll down to 'Measurement Protocol API secrets' → click Create. Name the secret (for example, 'Bubble App') and copy the generated Secret Value. While you are here, also copy two other values you will need: - The Measurement ID: shown at the top of the stream detail page, format G-XXXXXXXXXX. This goes in the API Connector as a query parameter, NOT as an auth credential. - The Property ID: a numeric ID shown in GA4 Admin → Property Settings. You will need this for the Data API in the read path (it is different from the Measurement ID). Important distinction to bookmark: the Measurement ID (G-XXXX) goes into the browser gtag.js snippet for client-side tracking. It does NOT authenticate Measurement Protocol requests. The api_secret is what authenticates Measurement Protocol calls. Confusing these two is the most common setup mistake.

ga4_credentials_reference.json
1{
2 "ga4_values_needed": {
3 "measurement_id": "G-XXXXXXXXXX (from Data Streams → stream detail page)",
4 "api_secret": "[from Measurement Protocol API secrets — copy the Secret Value]",
5 "property_id": "123456789 (numeric, from Admin → Property Settings → Property ID)"
6 },
7 "note": "Measurement ID is NOT used to authenticate API calls — it only identifies which stream events are sent to. The api_secret authenticates the Measurement Protocol POST."
8}

Pro tip: Create a separate api_secret for each environment (development, staging, production) so you can filter test events out of your production GA4 data using GA4's DebugView and internal traffic filters.

Expected result: A Measurement Protocol API secret value copied to your clipboard, along with your Measurement ID and Property ID noted for use in the next steps.

2

Configure the API Connector to Send Events via Measurement Protocol

Open your Bubble app's Plugins tab → click on API Connector → click 'Add another API'. Name this group 'Google Analytics Events'. Add a shared header: you can leave this group without shared headers since the credentials go in the URL query string. Click 'Add another call'. Name it 'Send GA4 Event'. Set the method to POST. Set the endpoint to: https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_SECRET In the API Connector URL field, you can either hardcode the measurement_id and api_secret directly into the URL, or add them as individual URL parameters. If you add api_secret as a URL parameter in the API Connector's parameter section, check the 'Private' checkbox — this prevents Bubble from logging the value in the API Connector response panel and in the Logs tab. Set the Content-Type to application/json. In the Body section, set the body type to JSON and enter the event body. At minimum, every Measurement Protocol event requires a client_id (a user identifier — in a Bubble app, use the Current User's unique ID or email as a pseudo client_id, formatted as a string) and an events array with at least one event object. Click 'Initialize call' with a real client_id value. Bubble will POST to GA4. GA4 always returns HTTP 200 even for malformed events — do NOT judge success by the API response code. Instead, open GA4 → Admin → DebugView → select your device/app and confirm the test event appears within 1-2 seconds. Set 'Use as' to 'Action'. The call is now available in your Bubble Workflows.

ga4_send_event_call.json
1{
2 "method": "POST",
3 "url": "https://www.google-analytics.com/mp/collect",
4 "query_params": [
5 { "key": "measurement_id", "value": "G-XXXXXXXXXX", "private": false },
6 { "key": "api_secret", "value": "<your_api_secret>", "private": true }
7 ],
8 "headers": [
9 { "key": "Content-Type", "value": "application/json" }
10 ],
11 "body": {
12 "client_id": "{{client_id}}",
13 "events": [
14 {
15 "name": "{{event_name}}",
16 "params": {
17 "currency": "{{currency}}",
18 "value": "{{order_value}}",
19 "transaction_id": "{{transaction_id}}"
20 }
21 }
22 ]
23 },
24 "use_as": "Action"
25}

Pro tip: GA4 event names must use only letters, numbers, and underscores, and must not start with a number. GA4 has reserved event names (purchase, sign_up, page_view) with predefined parameter schemas — use these for standard ecommerce and acquisition events so GA4 recognizes them automatically.

Expected result: A 'Send GA4 Event' action visible in your Bubble Workflow editor, ready to fire with dynamic client_id and event_name values.

3

Trigger GA4 Events from Bubble Workflows

Now that the API Connector call is configured, you can trigger it from any Bubble Workflow. The most common trigger points are: - User signup: Workflow event → 'When user signs up' → action: 'Plugins → Google Analytics Events - Send GA4 Event' with event_name = 'sign_up' and client_id = 'Current User's unique ID'. - Button click: Workflow event → 'When Button [Submit Order] is clicked' → action: Send GA4 Event with event_name = 'purchase' and value parameters from the order form. - Page load: Workflow event → 'When page is loaded' → action: Send GA4 Event with event_name = 'page_view' and a page_location parameter. For client_id, use the Current User's unique ID (a UUID assigned by Bubble). This gives you user-level attribution in GA4. For anonymous users (not logged in), generate a random UUID and store it in a Bubble local storage cookie or custom state — or use the Current User's anonymous ID if your Bubble app allows anonymous users. To track all page loads across your app, create a reusable element that fires the page_view event and include it on every page. This is Bubble's equivalent of placing the gtag.js snippet in the HTML head. After setting up your first event Workflow, go to GA4 → Admin → DebugView and trigger the action in your Bubble preview. You should see the event appear in DebugView within seconds. Remember: standard GA4 reports have a 24–48 hour lag, but DebugView is near real-time and is the right place to verify your setup.

bubble_ga4_workflow_examples.txt
1// Example Workflow: When user signs up
2// Event: User is signed up
3// Action 1: Create a new User record (standard Bubble signup)
4// Action 2: Plugins → Google Analytics Events - Send GA4 Event
5// client_id = Current User's unique ID
6// event_name = sign_up
7// (no other params needed for sign_up event)
8
9// Example Workflow: Purchase completed
10// Event: Button 'Complete Order' is clicked
11// Action 2: Plugins → Google Analytics Events - Send GA4 Event
12// client_id = Current User's unique ID
13// event_name = purchase
14// currency = USD
15// value = Input 'Order Total's value
16// transaction_id = Current Order's unique ID

Pro tip: Add a condition to your event-sending Workflows: 'Only when Current User's email doesn't contain your-domain.com' — this filters out internal test traffic from your GA4 data without needing to set up IP filters in GA4.

Expected result: Custom events appearing in GA4 DebugView within seconds of being triggered in your Bubble app. Standard GA4 reports will reflect the events within 24–48 hours.

4

Set Up a Google Service Account for Reading GA4 Report Data

Reading GA4 report data requires server-to-server authentication with a Google service account. This is a more involved setup than the Measurement Protocol send path. Step 1: Create a Google Cloud project (or use an existing one). Go to console.cloud.google.com → APIs & Services → Library → search 'Google Analytics Data API' → Enable it. Step 2: Go to APIs & Services → Credentials → Create Credentials → Service Account. Name it 'Bubble GA4 Reader', skip the optional role assignment, and click Done. Click on the new service account → Keys tab → Add Key → JSON → Create. A JSON key file downloads automatically. This file contains the private key you would need for JWT signing — but you will NOT use this file directly in Bubble (because Bubble cannot sign RSA JWTs). Instead, extract only the client_email from the JSON file — you will need it in the next step. Step 3: Grant the service account access to your GA4 property. Go to GA4 → Admin → Account Access Management (or Property Access Management for property-level access) → click the + button → add the service account's client_email → set role to Viewer → click Add. Step 4: Generate a refresh_token via the OAuth Playground. Go to https://developers.google.com/oauthplayground. In Settings (gear icon), check 'Use your own OAuth credentials' and enter a Google Cloud OAuth 2.0 client ID and secret (create these in your Google Cloud project under APIs & Services → Credentials → OAuth 2.0 Client IDs → Web application, add the OAuth Playground URL as an authorized redirect URI). In the scope selection, find 'Google Analytics Data API v1' and select 'https://www.googleapis.com/auth/analytics.readonly'. Click Authorize → sign in with a Google account that has Viewer access to the GA4 property → Exchange authorization code for tokens. Copy the refresh_token from the response — this is a long-lived credential you will store in Bubble.

ga4_service_account_setup.json
1{
2 "google_cloud_setup": {
3 "step1": "Enable 'Google Analytics Data API' in Google Cloud Console → APIs & Services → Library",
4 "step2": "Create a Service Account → download JSON key file → copy client_email",
5 "step3": "GA4 → Admin → Property Access Management → add service_account_email → Viewer role",
6 "step4": "OAuth Playground: scope = analytics.readonly → get refresh_token"
7 },
8 "credentials_to_store_in_bubble": {
9 "client_id": "from your OAuth 2.0 Client ID",
10 "client_secret": "from your OAuth 2.0 Client Secret [Private]",
11 "refresh_token": "from OAuth Playground [Private]",
12 "property_id": "numeric GA4 Property ID (not the G-XXXX Measurement ID)"
13 }
14}

Pro tip: The OAuth Playground refresh_token approach avoids the need for RSA JWT signing in Bubble. However, refresh tokens generated via the OAuth Playground with a personal Google account can expire if the account's password changes or if the OAuth consent screen is set to 'External' status and the token is older than 7 days. For production, use an account specifically created for this integration and keep the OAuth consent screen in 'Internal' mode (requires a Google Workspace account).

Expected result: A client_id, client_secret, refresh_token, and numeric GA4 property_id — all ready to be added to the Bubble API Connector in the next step.

5

Configure the API Connector for the GA4 Data API Two-Call Token Chain

In the API Connector, create a new API group called 'Google OAuth Token'. Add one call: Call name: 'Refresh Access Token'. Method: POST. URL: https://oauth2.googleapis.com/token. Body type: Form data. Body parameters: - client_id: your Google Cloud client ID (Private: unchecked — it is a public identifier) - client_secret: your client secret (Private: CHECKED) - refresh_token: your refresh_token from the OAuth Playground (Private: CHECKED) - grant_type: refresh_token (Private: unchecked — this is a fixed value) Set 'Use as' to Action. Click 'Initialize call' — you need a real successful response (access_token in the JSON) for Bubble to detect the response shape. If you get an error, double-check that the client_id, client_secret, and refresh_token are correct and that the Google Analytics Data API is enabled in your Google Cloud project. Next, create a second API group called 'GA4 Data API'. Base URL: https://analyticsdata.googleapis.com/v1beta. Add a shared header: Authorization: Bearer [leave empty — will be populated dynamically], Private: CHECKED. Add one call named 'Run Report'. Method: POST. Endpoint: /properties/{{property_id}}:runReport. Body type: JSON. Body: { "dateRanges": [{"startDate": "{{start_date}}", "endDate": "{{end_date}}"}], "dimensions": [{"name": "{{dimension}}"}], "metrics": [{"name": "sessions"}, {"name": "screenPageViews"}], "limit": 100 } Set 'Use as' to Action. Initialize with a real access_token in the Authorization header and real property_id, dates, and dimension values. If you see 'There was an issue setting up your call', verify that the service account has been granted Viewer access to the GA4 property and that you are using the numeric property_id (not the Measurement ID). Note: GA4 Data API data has a 24–48 hour processing lag. Queries for 'today' or 'yesterday' will often return incomplete or zero rows — use date ranges starting at least 3 days ago for accurate results.

ga4_data_api_calls.json
1{
2 "token_call": {
3 "method": "POST",
4 "url": "https://oauth2.googleapis.com/token",
5 "body_type": "form_data",
6 "params": {
7 "client_id": "{{client_id}}",
8 "client_secret": "<private>",
9 "refresh_token": "<private>",
10 "grant_type": "refresh_token"
11 },
12 "use_as": "Action"
13 },
14 "data_api_call": {
15 "method": "POST",
16 "url": "https://analyticsdata.googleapis.com/v1beta/properties/{{property_id}}:runReport",
17 "headers": {
18 "Authorization": "Bearer <dynamic_from_workflow>"
19 },
20 "body": {
21 "dateRanges": [{"startDate": "30daysAgo", "endDate": "3daysAgo"}],
22 "dimensions": [{"name": "pagePath"}],
23 "metrics": [{"name": "sessions"}, {"name": "screenPageViews"}],
24 "limit": 100
25 },
26 "use_as": "Action"
27 }
28}

Pro tip: GA4 Data API supports shorthand date values like 'today', 'yesterday', '7daysAgo', and '30daysAgo'. Use '30daysAgo' and '3daysAgo' as a reliable window that avoids the 2–3 day processing lag.

Expected result: Two initialized API calls: 'Refresh Access Token' returning a real access_token, and 'Run Report' returning a rows array with dimension values and metric values that you can bind to Bubble UI elements.

6

Build a Workflow to Chain Token Refresh and GA4 Report Query, Then Bind to UI

Create a page in Bubble with a Repeating Group to display the GA4 data. Set the Repeating Group's Type of content to 'GA4 Row' (a new data type you will create with fields: dimension_value text, sessions number, page_views number). Alternatively, use a Custom State to hold the list of results without writing to the database. Create a Workflow event: 'When page is loaded' → add actions: Action 1: 'Plugins → Google OAuth Token - Refresh Access Token'. This runs the token exchange call and returns an access_token. Action 2: 'Plugins → GA4 Data API - Run Report'. In the Authorization dynamic field, enter 'Bearer ' + 'Result of Step 1's access_token'. Set property_id to your numeric GA4 Property ID. Set start_date to '30daysAgo' and end_date to '3daysAgo'. Set dimension to 'pagePath'. Action 3: 'Make changes to a list of Things' (or set a Custom State). Parse the rows from 'Result of Step 2's rows'. Each row has a dimensionValues array and a metricValues array — in Bubble, reference them as 'Result of Step 2's rows each item's dimensionValues first item's value' for the page path, and similarly for metric values. Bind the Repeating Group's data source to the Custom State list or the database search. Caching recommendation: data in GA4 changes slowly (24–48 hour lag). Cache the query result in the Bubble database with a fetched_at timestamp. On page load, check if fetched_at is older than 12 hours before making a new API call — this reduces WU costs and avoids hitting the GA4 Data API rate limits on busy Bubble apps. Add privacy rules to the GA4 data type so only users with the correct role can access cached report data.

ga4_report_workflow.txt
1// Workflow: When page is loaded
2//
3// Step 1: Plugins → Google OAuth Token - Refresh Access Token
4// Result → access_token (short-lived, ~1 hour)
5//
6// Step 2: Plugins → GA4 Data API - Run Report
7// Authorization = "Bearer " + Step 1's access_token
8// property_id = 123456789 (your numeric property ID)
9// start_date = 30daysAgo
10// end_date = 3daysAgo
11// dimension = pagePath
12//
13// Step 3: Set Custom State 'ga4_rows' = Step 2's rows
14//
15// Repeating Group data source: Custom State ga4_rows
16// Cell bindings:
17// Text 'Page': Current cell's dimensionValues first item's value
18// Text 'Sessions': Current cell's metricValues first item's value
19// Text 'Page Views': Current cell's metricValues second item's value
20
21// Add privacy rule to GA4 Row data type:
22// Data tab → Privacy → GA4 Row → 'When Current User's role is Admin'

Pro tip: RapidDev's team regularly builds analytics dashboards in Bubble — if you need help mapping the GA4 response rows to your specific UI layout or setting up advanced filtering by dimension, book a free scoping call at rapidevelopers.com/contact.

Expected result: A Bubble Repeating Group displaying live GA4 page path and session data fetched from the Data API, with a two-call server-side token chain that keeps all OAuth credentials private.

Common use cases

Track Bubble App Conversions in GA4

Fire a custom GA4 event whenever a user completes a key action in your Bubble app — sign up, purchase, submit a form — so you can measure conversion funnels and attribute traffic in the same GA4 property as your marketing website.

Bubble Prompt

When a user completes checkout in Bubble, send a 'purchase' event to GA4 with the order value and product name as event parameters.

Copy this prompt to try it in Bubble

SEO Performance Dashboard Inside Bubble

Pull GA4 session and organic traffic data into a Bubble repeating group to display a simple SEO performance dashboard for internal teams, avoiding the need to log into GA4 separately.

Bubble Prompt

Show a table of top landing pages sorted by organic sessions in the last 30 days, queried from the GA4 Data API and displayed in a Bubble repeating group.

Copy this prompt to try it in Bubble

Automated Marketing Report Emails

Use a Bubble Backend Workflow to query GA4 report data on a weekly schedule, format it into a summary, and send it via email — creating automated weekly analytics digests without leaving Bubble.

Bubble Prompt

Every Monday morning, fetch last week's GA4 traffic summary and send it as a formatted email to the team using Bubble's SendGrid or Mailchimp integration.

Copy this prompt to try it in Bubble

Troubleshooting

Measurement Protocol call returns 200 but events do not appear in GA4 DebugView

Cause: The api_secret is wrong, the measurement_id is wrong, or the event body is malformed. The Measurement Protocol always returns HTTP 200 regardless of errors — it does not return error codes for bad credentials or malformed events.

Solution: Use the GA4 Measurement Protocol Validation endpoint instead of the production endpoint for testing: POST to https://www.google-analytics.com/debug/mp/collect (same parameters). This endpoint returns a JSON response with validationMessages describing any issues. Fix all reported issues before switching back to the production endpoint. Also confirm in GA4 → Admin → Data Streams that the Measurement Protocol API secret is still active (secrets can be deleted accidentally).

typescript
1{
2 "debug_endpoint": "https://www.google-analytics.com/debug/mp/collect?measurement_id=G-XXXX&api_secret=YOUR_SECRET",
3 "note": "Same body as production — returns {validationMessages: [...]} with specific error descriptions"
4}

GA4 Data API returns 403 PERMISSION_DENIED when querying the property

Cause: Either the service account or the OAuth user account has not been granted access to the GA4 property, or the wrong property_id is being used. Numeric property IDs and Measurement IDs (G-XXXX) are different — the Data API requires the numeric property ID.

Solution: Go to GA4 → Admin → Property Access Management. Confirm the service account email (or the Google account that generated the refresh_token) appears with at least Viewer role. Also confirm the property_id in your API Connector call is the numeric ID from GA4 → Admin → Property Settings → Property ID (looks like: 123456789), not the Measurement ID (G-XXXX).

Token refresh call returns 'invalid_grant' error

Cause: The refresh_token has expired or been revoked. Refresh tokens generated via the OAuth Playground can expire if: the Google account's password was changed, the token was manually revoked in Google Account security settings, or the OAuth app's consent screen is in 'External' mode and the token is older than 7 days without a refresh.

Solution: Go back to the OAuth Playground and generate a new refresh_token. Update the refresh_token value in your API Connector call. For production stability, move the OAuth app consent screen to 'Internal' status (requires Google Workspace) and use a dedicated service account or admin account that does not have password changes.

'There was an issue setting up your call' when initializing the GA4 Data API call in the API Connector

Cause: The Initialize call is failing because the access_token hardcoded temporarily for initialization has expired (access_tokens last only 1 hour), or because the property_id contains the Measurement ID instead of the numeric property ID.

Solution: Run the 'Refresh Access Token' call first to get a fresh access_token, then use that token in the Authorization header for the Initialize call. Confirm the property_id is a numeric value (not G-XXXX) from GA4 → Admin → Property Settings. Re-initialize the call after correcting both values.

Best practices

  • Always mark your api_secret (Measurement Protocol) and OAuth credentials (client_secret, refresh_token) as Private in the API Connector — this prevents them from appearing in Bubble's Logs tab and from being accessible to admin users who can view API Connector responses.
  • Use the Measurement Protocol validation endpoint (https://www.google-analytics.com/debug/mp/collect) during development to catch malformed events — the production endpoint always returns 200, making silent failures impossible to detect from the API response alone.
  • Cache GA4 Data API results in the Bubble database with a fetched_at timestamp and only refresh when the cache is older than 12 hours — GA4 data has a 24–48 hour lag anyway, so re-fetching on every page load wastes WUs without giving fresher data.
  • Use the numeric GA4 Property ID in Data API calls, never the Measurement ID (G-XXXX). Keep both values documented in your Bubble app notes to avoid confusion.
  • Add Bubble privacy rules to any data type that stores GA4 report data — restrict read access to users with the appropriate role to prevent raw analytics data from leaking through Bubble's Data API.
  • For the send-events path, add a Workflow condition to exclude internal team members (filter by email domain) so that developer testing does not inflate your GA4 event counts.
  • GA4 Data API returns at most 25,000 rows per request — for high-traffic sites with thousands of pages, use dimension filters in the report body to scope your query (e.g., filter to specific page path prefixes) rather than requesting all rows at once.
  • If you need real-time data rather than the 24–48 hour GA4 lag, consider Mixpanel or Amplitude as complementary tools — both support near-real-time event querying and integrate with Bubble through the same API Connector pattern.

Alternatives

Frequently asked questions

What is the difference between the GA4 Measurement ID (G-XXXX) and the Measurement Protocol API_SECRET?

The Measurement ID (G-XXXX) is a public identifier for your GA4 data stream. It goes into the browser-side gtag.js snippet to enable client-side tracking. It does NOT authenticate API calls and should NOT be used in the API Connector's authentication fields. The api_secret is a separate credential that you create manually in GA4 → Admin → Data Streams → Measurement Protocol API secrets. The api_secret is what actually authorizes Measurement Protocol HTTP calls sent from Bubble — without it, GA4 silently rejects events.

Why does GA4 return 200 OK even when my events are malformed or the api_secret is wrong?

This is by design in the Measurement Protocol specification. Google always returns 200 to avoid revealing information about valid vs invalid credentials. Use the debug endpoint (https://www.google-analytics.com/debug/mp/collect) during development — it returns a JSON body with validationMessages describing exactly what is wrong with your event. Always verify successful event delivery in GA4 DebugView, not by checking the HTTP status code.

Can I read GA4 real-time data (like active users right now) in Bubble?

The GA4 Data API has a Realtime endpoint (POST to /properties/{propertyId}:runRealtimeReport) that returns data for the last 30 minutes. It uses the same authentication flow as the standard report API. Realtime data does not have the 24–48 hour lag. However, cache it carefully — polling the Realtime API too frequently from a Bubble page load Workflow will hit GA4's API quotas (200,000 tokens/project/day) quickly on high-traffic apps.

Can free Bubble plan users connect to Google Analytics?

Yes, for sending events via the Measurement Protocol — that path uses only a standard API Connector call which works on all Bubble plans. Reading report data is harder on the free plan because the two-call token chain requires a scheduled Backend Workflow to auto-refresh the access_token before it expires (access tokens last only 1 hour). On the free plan, you can refresh the token manually on each page load, but this adds latency and will fail for sessions that stay on the same page longer than an hour.

How do I filter out internal test traffic from appearing in GA4?

Add a Workflow condition to your event-sending actions: 'Only when Current User's email doesn't contain @yourdomain.com'. This prevents team members testing the Bubble app from polluting your GA4 analytics data. You can also define an Internal Traffic rule in GA4 → Admin → Data Streams → Configure tag settings → Define internal traffic, but the Workflow condition is simpler for Bubble-specific event filtering.

How many GA4 events can I send per day from Bubble?

The Measurement Protocol does not publish a specific per-call rate limit for server-side event sending (it is designed for server-side use at scale). Practically, for most Bubble apps, you will not hit any limits from user-action-triggered events. If you are sending events in bulk (e.g., importing historical data), limit batches to 25 events per API call (the Measurement Protocol accepts up to 25 events per request) and add a small delay between requests.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Integrations are where projects stall

We wire Bubble integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.