Pull up a GA4 exploration with a 12-month date range and watch the shield icon in the top corner. If it's yellow, you're looking at an estimate, not your data. I've watched store owners make budget decisions off reports where GA4 quietly sampled a third of the events and hid rows for "privacy." The fix isn't a paid tool. It's a free checkbox most marketers never click.
Why does the GA4 interface show different numbers than my real data?
Because the GA4 reporting UI applies four layers of distortion before you see a number. None are bugs. All are documented behavior.
Sampling. Standard GA4 properties sample any exploration touching more than 10 million events. Run a year-over-year funnel on a busy store and GA4 computes it from a subset, then extrapolates. The shield icon shows the percentage it actually used.
Thresholding. When Google signals or demographics are in play, GA4 withholds rows with low user counts so nobody can be identified. Sounds reasonable, until your landing-page report is missing rows and totals don't add up. The data was collected. GA4 just won't show it.
Cardinality. Dimensions have a daily limit on unique values. Blow past it with query-string-heavy page paths or granular UTMs and everything beyond the limit collapses into one "(other)" row.
Retention. On free properties, event-level data for explorations maxes out at 14 months. Want to compare this Black Friday against two years ago at the event level? Gone.
The interface isn't lying maliciously. It's summarizing, protecting, and truncating. But summarized, protected, truncated data is a bad foundation for spending decisions.
| GA4 reporting UI | BigQuery export |
|---|---|
| Samples explorations past 10M events | One row per event, no sampling |
| Thresholds low-user-count rows | No thresholding — skips Google signals |
| Extra values collapse into (other) | No cardinality limit, no (other) row |
| 14-month event-level retention | Tables stay until you delete them |
What does the BigQuery export actually give you?
The raw feed. Every event GA4 collected, one row per event, with every parameter, delivered into a BigQuery dataset you own. No sampling, because queries run on all the rows. No thresholding, because the export skips Google-signals demographic joins entirely. No "(other)", because a database column has no cardinality limit. No 14-month cliff, because the tables sit in your project until you delete them.
Concretely: a dataset named analytics_123456789 (your property ID) appears in your Google Cloud project, and each day a table like events_20260802 lands inside it. Each row is one event: purchase, session_start, page_view, with nested fields for parameters, device, geo, and traffic source.
- Create a Google Cloud project — The BigQuery sandbox works without a credit card
- GA4 → Admin → Product links → BigQuery — Click Link, pick the Cloud project and a data location
- Choose Daily, not Streaming — Streaming needs a billing account and bills per gigabyte; daily suits most marketers
- Add a billing account early — Sandbox tables auto-expire after 60 days; a card on file removes that and stays free
- First table lands within 24 hours — events_YYYYMMDD inside analytics_<propertyID>, one row per event, from the link date on
How do I set up the GA4 BigQuery export?
One linking flow, no code, no tag changes:
- Create a Google Cloud project at console.cloud.google.com if you don't have one. The BigQuery sandbox works without a credit card.
- In GA4, go to Admin → Product links → BigQuery links and click Link.
- Pick your Cloud project and a data location (EU businesses usually want an EU region).
- Choose your export type: Daily gives you a complete
events_YYYYMMDDtable each morning for the prior day. Streaming adds a near-real-timeevents_intraday_table but requires a billing account and bills a small per-gigabyte fee. Daily is right for almost every marketer. - Optionally exclude noisy events. Most people skip this at first.
- Submit. The first table shows up within about 24 hours.
Is the free tier really enough for a small business?
Usually, yes. Google's documented limits stack in your favor: standard (free) GA4 properties can export up to 1 million events per day via the daily export, and BigQuery's free tier includes 10 GB of storage plus 1 TB of query processing per month. A store doing a few thousand sessions a day generates megabytes, not gigabytes, and you won't hit 1 TB of queries running revenue reports. Check Google's current BigQuery pricing page for the exact free-tier numbers — they've been stable, but they're Google's to change.
What can a marketer actually do with this without becoming an engineer?
Four things, in rising order of ambition.
Exact-count reports. "How many purchases came from email last quarter, exactly?" is one query with a real answer. No shield icon, no missing rows.
Auditing your tracking. The use case I care most about, because it's how accuracy claims get verified instead of asserted. Count purchase events in the export, then line them up against your Shopify or WooCommerce order count and against what Meta and Google Ads report. The export is the neutral referee. When we audit a client's setup, this raw-events-versus-platform comparison is how we measure the gap.
Joining ad clicks to real outcomes. The export carries gclid and campaign parameters at the event level. Export your CRM's closed deals, join on click ID or a user ID you pass into GA4, and you see which campaigns produced revenue, not just form fills. That's a spreadsheet-level join, not a data-engineering project.
Feeding first-party audiences. A query like "viewed a product twice in 30 days, never purchased" becomes a list you push back to ad platforms as a custom audience, built from data you own.
What are some copy-paste starter queries?
Swap in your project and property ID and these run as-is. Purchases by day with revenue:
SELECT
event_date,
COUNT(*) AS purchases,
SUM(ecommerce.purchase_revenue) AS revenue
FROM `your-project.analytics_123456789.events_*`
WHERE event_name = 'purchase'
AND _TABLE_SUFFIX BETWEEN '20260701' AND '20260731'
GROUP BY event_date
ORDER BY event_date;
Sessions by source (distinct user + session ID pairs, which is how GA4 defines a session under the hood):
SELECT
traffic_source.source AS first_touch_source,
COUNT(DISTINCT CONCAT(user_pseudo_id,
(SELECT value.int_value FROM UNNEST(event_params)
WHERE key = 'ga_session_id'))) AS sessions
FROM `your-project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260701' AND '20260731'
GROUP BY first_touch_source
ORDER BY sessions DESC;
One honest caveat: traffic_source in the export is user-scoped, the source that first acquired the user. Session-scoped source lives in event parameters and takes a longer query. For a starter report, first-touch unsampled still beats the UI.
When is the BigQuery export overkill?
If your site does a few hundred sessions a month, the UI's standard reports never sample and rarely threshold. Same if nobody on the team will ever open BigQuery: an export nobody queries is just a tidy archive. Even then I'd flip it on, because it costs nothing and there's no backfill later. But don't feel behind for not writing SQL this quarter.
- Real order placed — Customer checks out and pays
- Browser tag blocked — Blocker, expired cookie or consent ping
- GA4 never collects it — Lost before it leaves the device
- Export mirrors the gap — BigQuery stores only what was collected
- Server-side send — track.yourstore.com sends it instead
Does the export fix my tracking, or just show me it's broken?
Just shows you. BigQuery faithfully stores whatever GA4 managed to collect in the browser, and browser collection is where the losses happen. Ad blockers eat the GA4 tag outright. Safari's ITP caps script-set cookies at seven days, so returning visitors keep getting counted as new. Consent Mode v2, enforced for EEA traffic, means unconsented visitors send at most anonymized pings. If 15% of your purchases never fire a browser event, your beautiful unsampled export is a record of 85% of reality.
That's where first-party server-side tracking comes in. Plain language: instead of every ad platform's script firing from the visitor's browser, events route through a tagging server on your own subdomain (something like track.yourstore.com), which forwards them to GA4, Meta, and Google Ads. Because the server sits on your domain, cookies are first-party and live longer, ad blockers have nothing recognizable to block, and you control exactly what gets shared, per the visitor's consent. Your BigQuery export gets fuller and cleaner at the same time, because one server-side stream feeds GA4 and every ad platform, so the numbers finally agree, with proper deduplication between them.
The two are a pair: the export proves what you're capturing, server-side capture makes sure that's close to everything. We use the first to measure and guarantee the second.
Frequently asked questions
Is the GA4 BigQuery export free?
The link is free on every GA4 property, with daily export up to 1 million events per day on standard properties. BigQuery's free tier (10 GB storage, 1 TB of queries monthly) covers typical SMB volumes, so most small businesses pay nothing.
Does the BigQuery export include historical GA4 data?
No. Export starts the day you create the link and only collects forward. That's why you should link now even if you won't query the data for months.
Is BigQuery export data sampled or thresholded?
No. It's raw event-level data, one row per event, with no sampling, no privacy thresholding, and no "(other)" row. It also excludes Google-signals demographic data entirely.
Should I choose daily or streaming export?
Daily, unless you genuinely need same-day data. Streaming requires a billing account and bills per gigabyte, and most marketing questions are answered fine with yesterday's complete table.
Do I need to know SQL to use the GA4 BigQuery export?
For basic use, barely. A handful of copy-paste queries covers purchases, revenue, and sessions, and you can connect Looker Studio to BigQuery without writing SQL at all.
Why don't my BigQuery numbers match the GA4 interface?
Small differences are normal: the UI applies sampling, thresholding, and its own session and attribution modeling, while BigQuery shows raw events. When they disagree, BigQuery is the ground truth.
Does the BigQuery export replace server-side tracking?
No, they solve different problems. The export gives you full access to whatever was collected; server-side tracking makes sure more of your real conversions get collected in the first place.
- ga4
- bigquery
- data

