GA4 BigQuery Export Attribution: The GA4 BigQuery export contains three different traffic-source records, and one field name means two different things depending on which event row you read it from. Here is the scope-match test that stops you misattributing revenue.
Read the full article below for detailed insights and actionable strategies.
The attribution problem
One sale. Four channels. 400% credit claimed.
Reported revenue: €400 · Actual revenue: €100 · Gap: €300
The 60-Second Answer
The GA4 BigQuery export stores attribution at three scopes. Use traffic_source.* on the first_visit event for user acquisition, session_traffic_source_last_click.* for session source, and traffic_source.* on a conversion event for event-scoped last-click. Picking the wrong scope silently misattributes revenue — usually inflating whichever channel a customer first discovered you through.
Most Shopify and DTC teams who pull their own attribution out of BigQuery get a number that looks plausible, ships to a dashboard, and is quietly wrong. Not because their SQL is broken — because they read a correctly-populated field at the wrong scope. This guide gives you a test to prevent that, and then shows why even perfectly-scoped last-click numbers still cannot tell you what to do with your budget.
Why This Field Is So Easy to Get Wrong
Here is the thing almost nobody tells you: traffic_source is scope-polymorphic. The same field name carries a different meaning depending on which event row you happen to be reading it from.
Per Google's traffic attribution documentation, traffic_source.source on a first_visit event is user-scoped — it describes how that user was originally acquired. But traffic_source.source on a conversion event is event-scoped, and reflects Google's cross-channel last-click model for that specific conversion.
Same column path. Two entirely different questions answered.
This is why a query like SELECT traffic_source.source, SUM(purchase_revenue) FROM events_* WHERE event_name = 'purchase' returns a result that is not wrong at the row level but is frequently misread as "the channel that acquired this customer." It is not that. If you then present it as acquisition data, you have built a reporting layer that systematically over-credits discovery channels and under-credits closing channels — or the reverse, depending on which event you filtered to. Nothing errors. Nothing looks broken. The number is just answering a different question than the one on the slide.
This is a close cousin of the problem described in the UTM tagging problem and the reason every attribution dashboard tells a different story.
The Scope-Match Test
Before you write a line of SQL, answer one question in plain language, then let the scope follow from it. We call this the Scope-Match Test, and it has three rungs.
| Your business question | Scope you need | BigQuery field | Read it from |
|---|---|---|---|
| "How did we originally acquire this customer?" | User | traffic_source.source / .medium / .name | The first_visit event only |
| "What drove this particular visit?" | Session | session_traffic_source_last_click.manual_campaign.* and .google_ads_campaign.* | Any event in the session |
| "What gets last-click credit for this purchase?" | Event | traffic_source.source / .medium / .name | The conversion event row |
The test is deliberately blunt: if you cannot state your question in one of those three forms, you are not ready to write the query. Most misattribution in home-built BigQuery reporting comes from skipping this step, not from bad joins.
A practical note on the session rung: session_traffic_source_last_click is a nested record with separate branches for manual tagging and for each Google advertising platform. Manual UTM traffic lands in manual_campaign; Google Ads traffic lands in google_ads_campaign, with parallel branches for SA360, DV360 and CM360. If you only read manual_campaign.source, your Google Ads sessions will look empty — a very common false alarm that sends teams hunting for a tracking bug that does not exist.
Step-by-Step: Building a Defensible Session-Source Query
- Confirm your export is actually complete. Daily export creates
events_YYYYMMDD; streaming export createsevents_intraday_YYYYMMDD. Querying both with aevents_*wildcard double-counts today. Decide which you want and filter_TABLE_SUFFIXexplicitly. - Write your question down first. Use the Scope-Match Test above. Put the sentence in a SQL comment so the next person knows what the query claims to answer.
- Pick the scope, then the field. Do not start from the field you already know exists.
- Coalesce the campaign branches. For session source, read both
manual_campaignandgoogle_ads_campaignand coalesce, rather than assuming one. - Deduplicate to one row per session. The session record repeats on every event in the session. Aggregate to
user_pseudo_id+ga_session_idbefore you join to revenue, or you will multiply revenue by event count. - Reconcile against the GA4 UI. Session-scoped last-click should broadly match your Sessions acquisition report. If it does not, your problem is upstream — see how to troubleshoot data discrepancies.
- Reconcile against Shopify. Expect a gap. Google and Shopify revenue rarely match, and the gap is diagnostic, not noise.
- Write down what the number cannot tell you. This is the step everyone skips, and it is the subject of the rest of this guide.
A Worked Example (Illustrative)
The figures below are a constructed example to show the arithmetic, not data from a specific brand.
A Shopify skincare brand runs a month at €80,000 in ad spend and books €400,000 in revenue. They pull two numbers from the same BigQuery export.
| Query | Scope read | Meta credited | Google Search credited | Email credited |
|---|---|---|---|---|
traffic_source on first_visit | User (acquisition) | €188,000 | €72,000 | €12,000 |
traffic_source on purchase | Event (last click) | €96,000 | €140,000 | €88,000 |
Both queries are correctly written. Both read a correctly-populated field. They disagree by €92,000 on Meta alone — because they answer different questions. Meta is where people discover the brand; branded search and email are where people close.
Now the part that matters. The team's instinct is to average the two, or to pick the one that supports the plan. Neither is defensible, because both numbers are correlational. They describe which touchpoint was present, not which touchpoint caused the purchase. A causal analysis of the same period might find that a large share of that branded-search revenue would have converted anyway — customers who already decided, typing the brand name — meaning the incremental return on that spend is far below the €140,000 it is credited with. That is the difference between incremental and attributed revenue, and no amount of correct field selection surfaces it.
This is the ceiling of the BigQuery export used on its own: it can tell you what happened next to a touchpoint. It cannot tell you what would have happened without it.
Common Mistakes
- Reading
traffic_sourceoff a purchase row and calling it acquisition. The single most expensive error in this guide. - Ignoring the Google Ads campaign branches of
session_traffic_source_last_clickand concluding tracking is broken. - Forgetting to deduplicate sessions, which inflates revenue by the number of events per session.
- Mixing daily and intraday tables with a wildcard, double-counting the current day.
- Treating
(direct)as a real channel. It is mostly a measurement gap — see direct traffic. - Assuming BigQuery precision equals causal truth. Row-level accuracy and causal validity are unrelated properties. Last-click has known structural problems regardless of how cleanly you query it.
- Rebuilding the platforms' own bias. Querying last-click faithfully reproduces the same self-attribution incentives that make Meta and GA4 disagree.
Checklist
- Business question written in one sentence, in the query comments
- Scope selected before field selected
- Daily vs. intraday tables explicitly filtered
- Both
manual_campaignandgoogle_ads_campaignbranches read - Deduplicated to one row per
user_pseudo_id+ga_session_id - Session totals reconciled against the GA4 UI
- Revenue reconciled against Shopify, with the gap explained
- Stated in writing that the output is correlational, not causal
From Correct Queries to Correct Decisions
Getting the scope right upgrades you from wrong to precisely correlational. That is a real improvement, and it is where most in-house ecommerce data warehouse projects stop.
The next upgrade is different in kind. Instead of asking which touchpoint was last, causal attribution asks what each channel actually added — modelling the counterfactual so that spend which would have converted anyway stops being credited. In practice this means Bayesian inference over the same GA4 export you already have, which is why causal inference for marketing attribution and MMM vs MTA vs causal inference are worth reading before you invest another quarter in dashboard-building. If you want to validate the output, incrementality testing is the ground truth.
Usefully, none of this requires new tracking. Your historical GA4 export already contains the raw material — which is the whole premise behind retroactive attribution analysis from your GA4 export and attribution for historical data when the pixel wasn't installed.
Key Takeaways
- The GA4 BigQuery export holds attribution at three scopes: user, session, and event.
traffic_sourcemeans different things on different event rows. Read it onfirst_visitfor acquisition, on a conversion event for last-click.- Use
session_traffic_source_last_clickfor session source, and read all campaign branches, not justmanual_campaign. - Apply the Scope-Match Test — question first, scope second, field third.
- Correct field selection eliminates a class of silent errors. It does not make the number causal.
- Deciding budget requires knowing what each channel added, which is a counterfactual question the export alone cannot answer.
For €99, upload any historical GA4 period and get causal attribution for every channel in 5–10 minutes — no pixel, no migration. Go Pro at €299/mo for continuous attribution, an AI chatbot for your data, and a developer API.
Further reading
Start with the ecommerce analytics stack for 2026, then compare tooling in the best marketing attribution tools and GA4 attribution alternatives. For the limits of the GA4 interface itself, see Google Analytics 4 attribution limitations and how to connect Google Analytics 4. Related: exporting and analysing Shopify marketing data, how to export attribution data, attribution models compared, multi-touch attribution problems, cohort-based attribution, server-side vs client-side attribution, data-driven attribution in Google Ads, SQL dialect differences, one dashboard, one number, and what the €99 analysis includes. Glossary: traffic source, session, attribution window, last-click attribution, first-touch attribution, data warehouse, UTM parameters, Bayesian inference, counterfactual, and incrementality.
Get attribution insights in your inbox
One email per week. No spam. Unsubscribe anytime.
Key Terms in This Article
Attribution Dashboard
An Attribution Dashboard visualizes marketing data to show which touchpoints and channels contribute to conversions. It helps marketers understand campaign effectiveness.
Attribution Window
Attribution Window is the defined period after a user interacts with a marketing touchpoint, during which a conversion can be credited to that ad. It sets the timeframe for assigning conversion credit.
Bayesian Inference
Bayesian Inference updates the probability of a hypothesis based on new evidence. It refines marketing attribution by incorporating prior beliefs about channel effectiveness.
Causal Attribution
Causal Attribution uses causal inference to determine which marketing touchpoints genuinely cause conversions, not just correlate with them.
First-Touch Attribution
First-Touch Attribution gives 100% of conversion credit to the first marketing touchpoint a customer interacted with. This model identifies channels effective at generating initial awareness.
Incrementality Testing
Incrementality Testing measures the additional impact of a marketing campaign. It compares exposed and control groups to determine causal effect.
Marketing Attribution
Marketing attribution assigns credit to marketing touchpoints that contribute to a conversion or sale. Causal inference enhances attribution models by identifying true cause-effect relationships.
Multi-Touch Attribution
Multi-Touch Attribution assigns credit to multiple marketing touchpoints across the customer journey. It provides a comprehensive view of channel impact on conversions.
Related Articles
Sixty-second versions of these ideas: Causality Engine on YouTube Shorts.
Ready to see your real numbers?
Own the budget? Upload your GA4 export and see which channels drive incremental sales, with confidence intervals, in minutes. Have to defend it? Start with the live demo and take the read to your CFO.
Full refund if you don't see value.
Stay ahead of the attribution curve
Weekly insights on marketing attribution, incrementality testing, and data-driven growth. Written for the person who owns the budget and the person who has to defend it.
No spam. Unsubscribe anytime. We respect your data.
Frequently Asked Questions
Which BigQuery field shows the session traffic source in GA4?
Use the `session_traffic_source_last_click` record. It is available on every event in the session and reflects session-scoped last-click attribution. Read both the `manual_campaign` branch (for UTM-tagged traffic) and the `google_ads_campaign` branch (for Google Ads traffic), then coalesce them — reading only one will make half your sessions look untracked.
What is the difference between traffic_source and session_traffic_source_last_click?
`traffic_source` is scope-dependent: on the `first_visit` event it describes user acquisition, and on a conversion event it reflects Google''s cross-channel last-click model for that conversion. `session_traffic_source_last_click` is always session-scoped. Confusing the two is the most common source of silent misattribution in home-built BigQuery reporting.
Why does my BigQuery revenue not match Shopify?
Some gap is normal. GA4 depends on client-side collection subject to consent, ad blockers, and browser privacy restrictions, while Shopify records every order server-side. Cross-device journeys and session deduplication add more divergence. Treat the gap as diagnostic rather than as an error to eliminate — its size and shape tell you where your measurement is leaking.
Does querying the GA4 BigQuery export give me causal attribution?
No. The export gives you accurate observational data about which touchpoints preceded a conversion. Causal attribution asks a different question — what each channel actually added versus what would have happened anyway. Answering that requires modelling the counterfactual, typically via Bayesian inference, on top of the export.
Should I use daily or intraday tables for attribution analysis?
Use daily `events_YYYYMMDD` tables for any reporting you intend to trust. Intraday `events_intraday_YYYYMMDD` tables are populated continuously and are not final. If you query with an `events_*` wildcard while both exports are enabled, you will double-count the current day — filter `_TABLE_SUFFIX` explicitly.
Why do I need to deduplicate sessions in the GA4 BigQuery export?
The `session_traffic_source_last_click` record repeats on every event within a session. If you join revenue to it without first aggregating to one row per `user_pseudo_id` plus `ga_session_id`, you multiply revenue by the number of events in the session — producing dramatically inflated channel totals that still look internally consistent.
Can I do attribution in BigQuery without a tracking pixel?
Yes, if your GA4 property was already exporting to BigQuery. The export contains historical traffic-source, session, and conversion data, so you can analyse periods that have already ended without installing anything new. This is the basis for retroactive attribution analysis on historical GA4 data.