Yes, link the export now: it’s free to set up, it hands you every raw event, and it only collects data from the day you switch it on. Whether you build anything on top depends on two things: questions the GA4 interface can’t answer, such as revenue by first-touch channel or closed deals by landing page, and someone who will actually write the queries.
What the GA4 interface cannot do
GA4’s reports are built for browsing, not for precise business questions:
| Limit | In the GA4 interface | In the BigQuery export |
|---|---|---|
| Data thresholds | Rows are withheld when user counts are low and Google signals or demographics are involved | No thresholds; every collected event is there |
| Sampling | Explorations over large date ranges can return sampled results | Queries run on the full dataset |
| High cardinality | Long-tail values collapse into an “(other)” row | Every value is kept |
| Retention | Explorations on standard properties reach back 2 or 14 months, depending on your setting | Data stays as long as you keep the tables |
| Custom dimensions | Parameters must be registered, within a capped quota, before you can report on them | Every event parameter is exported, registered or not |
| Joins | Data import covers fixed cases like cost data; no free-form joins with CRM deals, margins or order history | Join anything else you load into BigQuery |
The last two rows matter most. The interface shows how many people started checkout, not which first-touch channel produces customers who are still buying a year later, or which landing pages produce opportunities that actually close.
What the BigQuery export includes and what it costs
The export writes one row per event into daily tables named events_YYYYMMDD. Each row carries the event name and timestamp, a nested array of event parameters, user properties, device and geography fields, the pseudonymous browser ID (user_pseudo_id), your own user_id if you set one, traffic source fields, and ecommerce and item details for commerce events. With streaming turned on, the current day also lands in events_intraday_ tables within minutes.
What it does not include:
- Modeled data. Consent mode modeling and other estimates the interface fills in are not in the export.
- Google signals and demographic data. Those stay in the interface.
- Anything before the link date. There is no backfill. Every day you wait is raw data you can never recover.
- Ad cost. Spend from Google Ads or any other platform has to be loaded separately.
These gaps are part of why BigQuery totals won’t match the interface exactly, much like GA4 and your ad platforms never match. Decide in writing which source each report uses.
Cost
The link and the daily export are free on standard properties. You pay Google Cloud for BigQuery:
- Storage, billed per GB per month. For most small and mid-sized properties, event data is a minor line item.
- Queries, billed by data scanned under on-demand pricing. This is the cost that surprises people.
- Streaming export, if enabled, which adds a per-GB charge.
BigQuery’s free tier covers the first 10 GB of storage and 1 TB of query processing each month, enough for many companies to pay nothing at the start. Standard properties also have a daily export cap of 1 million events. If a property regularly exceeds it, Google can pause the daily export, so exclude noisy events or move to streaming or GA4 360.
Cost blowups follow a pattern: a dashboard selecting every column across all history, refreshed hourly. BigQuery charges for the columns and tables a query touches, so date filters and narrow column lists keep bills small.
Setting up the export correctly from day one
Most problems I find in existing exports were baked in at setup. Get these right when you link it:
- Create the Google Cloud project under a company-owned account, not an agency’s or a freelancer’s
- Attach a billing account; sandbox tables expire after 60 days, which quietly deletes your history
- Choose the dataset location on purpose, because it can’t easily be changed later
- Turn on the daily export; add streaming only if someone needs same-day data
- Exclude high-volume events nobody will analyze if you’re approaching the daily cap
- Set budget alerts in Google Cloud and custom query quotas per user
- Grant access through groups, and give most people modeled tables rather than the raw export
- Set
user_idon signup or login to an internal ID, never an email address or other personal data - Clean up event names and parameters with a tracking plan so you aren’t exporting a mess
- Make sure every purchase event carries a unique
transaction_idso orders can be joined to your backend
Useful queries: user journeys, LTV by source and funnel drop-off
Two things trip up everyone at first. Event parameters are nested, so you pull a value with a subquery over UNNEST(event_params). And there is no session table: a session is user_pseudo_id plus the ga_session_id parameter. Filter dates with _TABLE_SUFFIX so a query scans only the days you need.
Funnel drop-off by session
This counts sessions reaching each step over the last 30 days. It isn’t strictly sequential, which is usually fine for spotting the biggest leak.
WITH e AS (
SELECT
CONCAT(user_pseudo_id, '-', CAST((SELECT value.int_value
FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS STRING)) AS session_key,
event_name
FROM `your_project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN
FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
AND event_name IN ('session_start', 'view_item', 'add_to_cart', 'begin_checkout', 'purchase')
)
SELECT
COUNT(DISTINCT IF(event_name = 'session_start', session_key, NULL)) AS sessions,
COUNT(DISTINCT IF(event_name = 'view_item', session_key, NULL)) AS viewed_item,
COUNT(DISTINCT IF(event_name = 'add_to_cart', session_key, NULL)) AS added_to_cart,
COUNT(DISTINCT IF(event_name = 'begin_checkout', session_key, NULL)) AS began_checkout,
COUNT(DISTINCT IF(event_name = 'purchase', session_key, NULL)) AS purchased
FROM e
Divide each column by the one before it. Start with the largest drop, then split the same query by device or landing page to see where it concentrates.
LTV by first-touch source
The traffic_source fields hold the first source that acquired the user, not the source of each session. That makes them the right fields for lifetime value by acquisition channel:
SELECT
traffic_source.source,
traffic_source.medium,
COUNT(DISTINCT user_pseudo_id) AS purchasers,
COUNT(DISTINCT ecommerce.transaction_id) AS orders,
ROUND(SAFE_DIVIDE(SUM(ecommerce.purchase_revenue), COUNT(DISTINCT user_pseudo_id)), 2) AS revenue_per_purchaser
FROM `your_project.analytics_123456789.events_*`
WHERE event_name = 'purchase'
GROUP BY 1, 2
HAVING purchasers >= 50
ORDER BY revenue_per_purchaser DESC
Two caveats: user_pseudo_id is per browser, so a customer who buys again on another device looks like a new user unless you set user_id. And the purchase event records revenue at checkout, before refunds or costs, so join to backend orders by transaction_id when you need net revenue.
User journeys
For paths, take page_view events, pull the page_location parameter, and build an ordered string per session with STRING_AGG(page, ' > ' ORDER BY event_timestamp). Count the most common paths in converting and non-converting sessions separately. Where they diverge tells you more than the interface’s path reports, because you define “converting”: a closed deal, not just a form fill.
Joining GA4 data with CRM and ad spend
This is where the export earns its keep. You need a key shared between systems:
user_id: set it to the CRM contact or account ID at signup or login. It then appears on that person’s events in the export.- Client ID in forms: read the client ID (the last two number blocks of the
_gacookie) into a hidden form field and store it on the CRM record. It matchesuser_pseudo_idin the export. transaction_id: joins GA4 purchases to orders in Shopify or your backend, with refunds, margin and repeat orders attached.
For spend, the BigQuery Data Transfer Service loads Google Ads data natively. Meta, LinkedIn, TikTok and most CRMs need a connector tool. Join spend to GA4 sessions on date plus campaign name, which only works if your UTM naming is consistent.
Once the joins work, you can report what the interface never will: cost per qualified opportunity by channel, closed-won revenue by first landing page, or 180-day revenue per customer by acquisition campaign next to what that campaign cost.
Expect match rates below 100%. Visitors who decline consent, block scripts or switch devices won’t link cleanly. Show the match rate next to every joined metric so nobody over-reads the result.
Building these joins and the reports on top of them is a large part of my marketing attribution work.
When BigQuery is overkill
Linking the export is cheap insurance. Building models and dashboards around it is not, and it’s often the wrong use of time.
| Situation | Recommendation |
|---|---|
| Low traffic, one or two channels, no revenue in a CRM | Link the export, build nothing yet |
| Your questions fit in explorations and 14 months of history | Stay in the interface |
| Nobody on the team or on retainer writes SQL | Link it, but don’t buy dashboards that depend on it |
| Paid spend across several platforms plus revenue in a CRM | Worth building |
| Long B2B sales cycles where leads and revenue are months apart | Worth building |
| Ecommerce with repeat buyers and cohort LTV questions | Worth building, joined to order data |
The honest test: name the first three questions you’d answer and who will write the queries. If you can’t, the export just accumulates tables nobody reads.
Tools that sit on top of the export
A practical stack has three layers:
- Modeling. Scheduled queries, Dataform (built into BigQuery) or dbt turn raw events into clean session, user and conversion tables. Open-source packages can model the GA4 export into sessions for you.
- Loading other data. The Data Transfer Service for Google Ads, Search Console’s bulk export to BigQuery, and connector tools such as Fivetran, Airbyte or Supermetrics for other ad platforms and CRMs.
- Reporting. Looker Studio connects to BigQuery natively, and Looker, Power BI, Tableau and Metabase all work too. Point them at modeled tables, never at the raw
events_*tables, which keeps dashboards fast and query costs predictable.
Get it built
If your GA4 data is stuck behind thresholds, or you can’t connect web behavior to revenue, I can set up the export, model the tables and build reports your team will actually open. Start with a Growth Audit, $1,500 fixed and credited if we continue. See pricing or get in touch.