Data Streams
A data stream is a source of data flowing into a GA4 property. Every GA4 property needs at least one stream, and most need exactly one per platform: one web stream, one iOS stream, one Android stream.
Unlike Universal Analytics where tracking IDs (UA-XXXXXXXX-X) were tied to the property, GA4 separates the property from the stream. Your property has a property ID (a number like 123456789) and each stream has its own measurement ID (G-XXXXXXXXXX for web) or Firebase app ID for mobile.
Stream types
Section titled “Stream types”Web stream — collects data from browsers via gtag.js or Google Tag Manager. Identified by a measurement ID in the format G-XXXXXXXXXX.
iOS app stream — collects data from iOS apps via the Firebase SDK. Identified by a Firebase App ID.
Android app stream — collects data from Android apps via the Firebase SDK.
A GA4 property can have up to 50 data streams. You can combine web and app streams in the same property — GA4 is designed for cross-platform measurement, and having a single property for all platforms is the recommended approach.
Creating a web stream
Section titled “Creating a web stream”-
Go to Admin → Data Streams → Add stream → Web.
-
Enter your website URL. GA4 will verify this during setup.
-
Enter a stream name — something descriptive like “Website - Production” or “[Brand] - Web”.
-
Toggle Enhanced Measurement on or off. You can change this later and configure individual events independently.
-
Click Create stream.
After creation, you will see:
- Measurement ID — the
G-XXXXXXXXXXID you add to your site tag or GTM - Stream ID — a numeric identifier used in the API
- Enhanced Measurement settings — toggle individual auto-tracking events
Stream-level settings
Section titled “Stream-level settings”From the stream details page:
Tag settings — Additional configuration for the gtag that deploys this stream, including:
- Session timeout (default: 30 minutes; range: 5 minutes to 7 hours 55 minutes)
- Engaged session timeout (default: 10 seconds)
- Default values for cross-domain tracking, cookie settings, and consent mode
Measurement Protocol API secrets — Required for sending data to GA4 via the Measurement Protocol. Each secret is a string added as the api_secret parameter in server-side calls.
Connected site tags — Link other tag IDs (UA or other GA4 property IDs) so a single gtag fires multiple properties.
Enhanced Measurement configuration
Section titled “Enhanced Measurement configuration”Enhanced Measurement auto-collects additional events beyond the base page_view. Toggle individual events in Admin → Data Streams → [stream] → Enhanced Measurement (gear icon).
| Event | What it tracks | Default |
|---|---|---|
| Page views | page_view on each navigation | On |
| Scrolls | scroll at 90% page depth | On |
| Outbound clicks | click on external links | On |
| Site search | view_search_results on query param URLs | On |
| Video engagement | YouTube embedded video events | On |
| File downloads | file_download on file link clicks | On |
| Form interactions | form_start, form_submit (beta) | Off |
Page view measurement in SPAs
Section titled “Page view measurement in SPAs”For Single Page Applications (React, Vue, Angular, etc.), Enhanced Measurement cannot detect route changes automatically. A route change in a SPA does not fire a new page load — the browser URL changes but no HTTP request is made.
Configure your framework’s router to push a history change or fire page_view manually:
// After a route change in your SPAgtag('event', 'page_view', { page_title: document.title, page_location: window.location.href, page_path: window.location.pathname});In GTM, use the History Change trigger and attach a GA4 Configuration or GA4 Event tag to it.
Cross-domain tracking
Section titled “Cross-domain tracking”When users navigate between domains (e.g., from shop.example.com to checkout.example.com), GA4 treats each domain as a separate origin by default. The client_id cookie does not transfer, which breaks the session and creates a (direct) referral.
Configure cross-domain tracking at Admin → Data Streams → [stream] → Configure tag settings → Configure your domains.
Add all domains that should be treated as part of the same property:
example.comshop.example.comcheckout.example.com
GA4 will automatically add a _gl parameter to links between these domains, carrying the client_id across the transition.
iOS and Android app streams
Section titled “iOS and Android app streams”For mobile apps, create separate streams for each platform. Each stream is linked to a Firebase project and app.
-
Go to Admin → Data Streams → Add stream → iOS app (or Android).
-
Enter the app bundle ID (iOS:
com.company.app) or package name (Android:com.company.app). -
Enter the app name.
-
Click Register app.
-
Download the
GoogleService-Info.plist(iOS) orgoogle-services.json(Android) configuration file. -
Add the Firebase SDK and configuration file to your app.
Firebase SDK integration
Section titled “Firebase SDK integration”The GA4 mobile SDK ships as part of Firebase Analytics. Add it to your app:
// In AppDelegate or App structimport FirebaseAnalytics
// Log an eventAnalytics.logEvent(AnalyticsEventPurchase, parameters: [ AnalyticsParameterTransactionID: "T-12345", AnalyticsParameterValue: 49.99, AnalyticsParameterCurrency: "USD"])import com.google.firebase.analytics.FirebaseAnalyticsimport com.google.firebase.analytics.ktx.analyticsimport com.google.firebase.analytics.ktx.logEventimport com.google.firebase.ktx.Firebase
// Log an eventFirebase.analytics.logEvent(FirebaseAnalytics.Event.PURCHASE) { param(FirebaseAnalytics.Param.TRANSACTION_ID, "T-12345") param(FirebaseAnalytics.Param.VALUE, 49.99) param(FirebaseAnalytics.Param.CURRENCY, "USD")}Multiple streams vs. multiple properties
Section titled “Multiple streams vs. multiple properties”Use multiple streams in one property when:
- You want unified cross-platform reporting (web + iOS + Android for the same product)
- Your web and app serve the same users and you want to analyze their behavior together
- You want a single BigQuery export with all platforms
Use separate properties when:
- You have genuinely different products or brands that should be reported separately
- Different teams own different platforms and need separate access controls
- You have different data retention or compliance requirements per platform
Measurement ID vs. Property ID
Section titled “Measurement ID vs. Property ID”These are two different identifiers that are often confused:
Property ID — a numeric ID like 123456789. Used in the Admin API and for identifying the property in Google’s systems.
Measurement ID — a string like G-XXXXXXXXXX. This is what goes in your gtag.js or GTM GA4 Configuration tag. Only exists for web streams.
Stream ID — a numeric ID specific to the stream. Used in the Measurement Protocol API endpoint.
If you are adding GA4 to your site, you need the Measurement ID (G-XXXXXXXXXX), not the property ID.
Common mistakes
Section titled “Common mistakes”Using one property for staging and production
Section titled “Using one property for staging and production”Do not mix staging and production data in the same GA4 property. Create a separate property for your staging environment. Production data contaminated with test data is difficult to clean up retroactively, and GA4 has no view-level filtering like Universal Analytics did.
Use a GA4 Data Filter to exclude developer traffic in your production property as a secondary protection, but the primary isolation should be separate properties.
Not configuring stream domains for cross-domain sites
Section titled “Not configuring stream domains for cross-domain sites”If your checkout is on a separate domain from your marketing site, forgetting to configure cross-domain tracking will cause most of your checkout sessions to appear as direct traffic and your conversion rate to appear much lower than it actually is.
Missing the Measurement ID when using GTM
Section titled “Missing the Measurement ID when using GTM”In GTM, the GA4 Configuration tag requires the Measurement ID (G-XXXXXXXXXX). It is easy to accidentally enter the property ID (a plain number) or the old UA format instead. Double-check the format — it must start with G-.