GA4 BigQuery Export: Costs, Limits, and Where to Start
GA4 hides its limits well. Reports sample, thresholds remove rows, and the interface forgets your user data after 14 months by default. The raw event stream behind those reports is available, unsampled and unfiltered, but only through BigQuery. Turning the export on takes five minutes. Living with it is where teams get caught.
Three things separate an export that earns its keep from one that quietly stops running: the 1 million event daily ceiling on standard properties, the billing you now own, and the fact that a missed day cannot be recovered. Here is what Google actually gives you and what it costs to keep.
What lands in BigQuery
The export writes one dataset per property, named analytics_<property_id>. That is the numeric property ID, not the G-measurement ID that sits in your tag. Get that wrong in a query and you will spend twenty minutes wondering why the table does not exist.
Inside the dataset, each day gets a table named events_YYYYMMDD. If you enable streaming as well, an events_intraday_YYYYMMDD table appears for the current day and is deleted once the daily table is complete. Query the daily table for anything you plan to report on. The intraday table is a staging surface and Google describes it that way.
Each row is one event, and the schema is nested. The parts you will touch most often:
event_name,event_date,event_timestampin microseconds UTCevent_params, a repeated record where every parameter is a key plus astring_value,int_value, ordouble_value. There is afloat_valuefield in the schema, but Google notes it is not currently in use.user_pseudo_id, assigned the first time a device or browser shows up, anduser_idwhen you send oneuser_first_touch_timestampfor acquisition timingprivacy_info, which recordsanalytics_storageandads_storageas Yes, No, or Unset, plususes_transient_tokencollected_traffic_source, which carries the manual source, medium, and campaign fields.manual_sourceincludes values parsed from referral parameters, not only UTM values.itemsanditem_paramsfor ecommerceis_active_user, populated only in the daily tables
Subproperties and roll-up properties export too. Each one is a separate link and a separate dataset, which is worth knowing before you promise anyone a single source of truth.
Three export types, and the one most teams should pick
- Daily export (standard and 360). The previous day's raw, unsampled events, once per day. The exact time is not guaranteed. Google says it typically lands mid-afternoon in the property's timezone and may be later or the next day. Attribution is last click observed, with no modelling. Standard properties cap at 1 million events a day. 360 properties reach 20 billion.
- Fresh daily (360 only). Data typically arrives by 5am, then batches update through the day, usually within 60 minutes. Same schema as the daily export, and only available to Normal and Large 360 properties.
- Streaming (standard and 360). The current day within minutes, but a best-effort service with no completeness guarantee. It can contain gaps, it excludes new user and new session traffic source data, and it has no volume limit. It costs 0.05 US dollars per gigabyte, which Google estimates at roughly 600,000 events.
For most properties the daily export is the right answer. Streaming is for operational monitoring rather than reporting, and the per-gigabyte charge adds up quickly on a busy site.
The 1 million event ceiling
Standard properties have a daily export limit of 1 million events. Cross it consistently and the daily export pauses. Google does not reprocess the days you missed, so a pause is not a delay, it is a permanent hole in your history. Editors and administrators get an email each time a property passes the limit, and a large overshoot can pause the export immediately rather than waiting for the day to close.
The control you have is data filtering. Under Admin, then Product Links, then BigQuery Links, you can exclude whole data streams and individual events from the export. You need Editor or above on the account at property level, plus OWNER access to the BigQuery project. One useful detail: you can name events that have not been collected yet, so you can pre-empt a known noisy event before it pushes you over the line.
What it costs, and the sandbox trap
BigQuery bills for storage and query processing. The GA4 side is free, Cloud is not, and Google requires a valid payment method on file. If billing lapses mid-month the export stops, and the missing data is not re-exported later.
The BigQuery sandbox is the free route, and its limits matter more than its price:
- 10 GiB of storage, granted once for the lifetime of the account and not returned when you delete data
- 1 TiB of processed query data per month, the same as the general free tier
- every table, view, and partition expires automatically after 60 days
- no streaming data, no data manipulation statements, and no BigQuery Data Transfer Service
- no intraday import of GA4 events
The 60-day expiry is the one that surprises people. A sandbox is a place to test queries, not to keep history. If you intend to hold a year of events, plan the paid path from the start.
Failure modes worth knowing in advance
- No service account with the Active user role on the Cloud project. Analytics cannot create tables and the export fails.
- The robot service account is deleted after setup. Every export stops.
- An organisation policy forbids export to the United States, or forbids service accounts from your domain. The link creation itself fails.
- The property timezone is changed. Google warns this can cause discrepancies or skip a daily export entirely.
The pattern across all four is the same. Nothing in the GA4 interface tells you the export stopped. You find out when someone opens a dashboard and asks why the numbers end last Tuesday.
Why the numbers will not match your reports
BigQuery holds the raw events Google received. Reports add things on top of that: identity modelling, thresholds, session logic. Google documents the reconciliation in Bridging the gap between Google Analytics UI and BigQuery export, and it is worth reading before you promise a client that the two will agree.
Two specifics catch people out:
- wBRAID and gBRAID are not included in the export. The GCLID is, inside
collected_traffic_source. Any join built on click identifiers therefore has a hole for privacy-restricted traffic. - Product data added through linked products is not exported. BigQuery contains only what Analytics received from the client.
Consent Mode changes the picture as well. Cookieless pings do land in the export, alongside user_id and custom dimensions. The privacy_info fields record the consent state on each row, so you can separate modelled from unmodelled behaviour, but you cannot pretend the consent gap is not there.
The first four queries worth writing
Start with volume, because a silent export failure looks exactly like a zero. Then parameters, then consent.
Daily volume across the last month:
SELECT event_date, COUNT(*) AS events
FROM `project.analytics_123456789.events_*`
GROUP BY event_date
ORDER BY event_date DESC
LIMIT 30
Pulling a parameter out of the nested record:
SELECT event_name,
(SELECT value.string_value FROM UNNEST(event_params)
WHERE key = 'page_location') AS page_location
FROM `project.analytics_123456789.events_20261010`
LIMIT 100
Consent state per day, which is the query the GA4 interface cannot answer:
SELECT event_date,
privacy_info.analytics_storage AS analytics_storage,
COUNT(*) AS events
FROM `project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20261001' AND '20261010'
GROUP BY event_date, analytics_storage
ORDER BY event_date DESC
And the arrival of campaign parameters, which is where imports and tagging problems show up first:
SELECT collected_traffic_source.manual_source AS source,
collected_traffic_source.manual_medium AS medium,
COUNT(*) AS events
FROM `project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX = '20261010'
GROUP BY source, medium
ORDER BY events DESC
LIMIT 50
In that last one, manual_source also carries values parsed from referral parameters, so the source list will not match your UTM source list exactly. Expect referrals mixed in.
The part nobody plans for
Once the export is running, retention and access stop being Google's problem. GA4 keeps user data for 2 or 14 months by default and gives you no way to edit a row. BigQuery keeps whatever you pay to store, and you can delete a single user's events if you need to. That is a real benefit, and it also moves a data protection question onto your desk. BigQuery ACLs decide who can read the dataset. In the UK that access list is part of your data protection story, not an IT footnote.
The export is not a bigger report. It is a second system with its own bill, its own ceiling, and its own failure modes. Turn it on when you have a question the interface genuinely cannot answer, and write down the 1 million event cap and the sandbox expiry before you do.
Do You Need BigQuery, or Just Better Reports?
North Digital works out whether a property actually needs the BigQuery export, and what it will cost to run. If the answer is no, we say so and fix the reports instead. If it is yes, you get the schema, the filters, and the queries set up properly.
Talk to North Digital