← All reports
Nourished Natural Health · data pipeline

Triple Whale & BigQuery — five findings

For Kiran (data + engineering) · prepared by Sebastian, 2026-08-26 · window 2026-07-26 → 2026-08-24 (30 days) · all figures USD

Triple Whale is now the CEO-facing source of truth for both business and Meta numbers. Reconciling it against Shopify and the Meta Marketing API to make that safe turned up five pipeline issues. Two of them silently corrupt numbers that are already being quoted.

The good news first, because it matters: TW's revenue agrees with Shopify to within 2.7% (US$162,839 vs US$158,479) and its Meta spend agrees with the Marketing API to within 1.5% once AUD is converted at ~0.72. On the week ending 2026-08-21 the two agree on attributed purchases exactly (20 and 20). The core of the pipeline is sound. What follows are specific, fixable defects around it.

Context: paid spend is not mostly Meta

This came out of the reconciliation and reframes everything downstream. Google Ads is 63% of paid spend — more than twice Meta. It is easy to miss because it is the one channel that does not live in a *Spend column: it is ga_adCost.

Total paid, 30d
US$63,244
Google Ads
63%
Meta
28%
TikTok
9%
0$1,000$2,000$3,000$4,000 2026-07-26 · Google Ads: US$1,2082026-07-26 · Meta: US$7752026-07-26 · TikTok: US$5607-262026-07-27 · Google Ads: US$9132026-07-27 · Meta: US$1,0092026-07-27 · TikTok: US$9472026-07-28 · Google Ads: US$1,0632026-07-28 · Meta: US$7932026-07-28 · TikTok: US$3142026-07-29 · Google Ads: US$1,2282026-07-29 · Meta: US$7882026-07-29 · TikTok: US$2872026-07-30 · Google Ads: US$1,1152026-07-30 · Meta: US$3862026-07-30 · TikTok: US$4042026-07-31 · Google Ads: US$1,1952026-07-31 · Meta: US$8202026-07-31 · TikTok: US$56607-312026-08-01 · Google Ads: US$1,1762026-08-01 · Meta: US$1,0002026-08-01 · TikTok: US$1512026-08-02 · Google Ads: US$1,0652026-08-02 · Meta: US$1,1092026-08-02 · TikTok: US$2942026-08-03 · Google Ads: US$9622026-08-03 · Meta: US$1,0132026-08-03 · TikTok: US$5882026-08-04 · Google Ads: US$1,2842026-08-04 · Meta: US$9382026-08-04 · TikTok: US$1772026-08-05 · Google Ads: US$1,2682026-08-05 · Meta: US$8612026-08-05 · TikTok: US$13208-052026-08-06 · Google Ads: US$1,6392026-08-06 · Meta: US$8162026-08-06 · TikTok: US$1022026-08-07 · Google Ads: US$1,7812026-08-07 · Meta: US$7922026-08-07 · TikTok: US$1362026-08-08 · Google Ads: US$1,7142026-08-08 · Meta: US$5542026-08-08 · TikTok: US$1232026-08-09 · Google Ads: US$1,3072026-08-09 · Meta: US$4412026-08-09 · TikTok: US$1072026-08-10 · Google Ads: US$1,3092026-08-10 · Meta: US$5002026-08-10 · TikTok: US$7008-102026-08-11 · Google Ads: US$1,1522026-08-11 · Meta: US$3482026-08-11 · TikTok: US$462026-08-12 · Google Ads: US$1,2102026-08-12 · Meta: US$3602026-08-12 · TikTok: US$542026-08-13 · Google Ads: US$1,3112026-08-13 · Meta: US$4332026-08-13 · TikTok: US$482026-08-14 · Google Ads: US$1,3942026-08-14 · Meta: US$3812026-08-14 · TikTok: US$1162026-08-15 · Google Ads: US$1,5182026-08-15 · Meta: US$3222026-08-15 · TikTok: US$12908-152026-08-16 · Google Ads: US$1,3402026-08-16 · Meta: US$2702026-08-16 · TikTok: US$712026-08-17 · Google Ads: US$1,1842026-08-17 · Meta: US$3882026-08-17 · TikTok: US$472026-08-18 · Google Ads: US$1,7922026-08-18 · Meta: US$4192026-08-18 · TikTok: US$1122026-08-19 · Google Ads: US$2,0972026-08-19 · Meta: US$3522026-08-19 · TikTok: US$772026-08-20 · Google Ads: US$1,3762026-08-20 · Meta: US$3382026-08-20 · TikTok: US$9808-202026-08-21 · Google Ads: US$1,6622026-08-21 · Meta: US$3652026-08-21 · TikTok: US$902026-08-22 · Google Ads: US$1,4732026-08-22 · Meta: US$3222026-08-22 · TikTok: US$1052026-08-23 · Google Ads: US$1,1942026-08-23 · Meta: US$3812026-08-23 · TikTok: US$632026-08-24 · Google Ads: US$1,0452026-08-24 · Meta: US$4292026-08-24 · TikTok: US$56
Google Ads Meta TikTok
Daily paid spend by channel, 2026-07-26 → 2026-08-24. Google is the largest channel on 27 of 30 days (Meta led on 07-27, 08-02 and 08-03). Hover any segment for its value.
Table view — channel totals
ChannelSpend (30d)ShareTW column
Google AdsUS$39,97563%ga_adCost
MetaUS$17,70328%fb_ads_spend
TikTokUS$5,5669%tiktok_spend
Total paidUS$63,244100%adsSpend reports $0
Table view — daily detail (30 rows)
DayGoogle AdsMetaTikTok
2026-07-26US$1,208US$775US$56
2026-07-27US$913US$1,009US$947
2026-07-28US$1,063US$793US$314
2026-07-29US$1,228US$788US$287
2026-07-30US$1,115US$386US$404
2026-07-31US$1,195US$820US$566
2026-08-01US$1,176US$1,000US$151
2026-08-02US$1,065US$1,109US$294
2026-08-03US$962US$1,013US$588
2026-08-04US$1,284US$938US$177
2026-08-05US$1,268US$861US$132
2026-08-06US$1,639US$816US$102
2026-08-07US$1,781US$792US$136
2026-08-08US$1,714US$554US$123
2026-08-09US$1,307US$441US$107
2026-08-10US$1,309US$500US$70
2026-08-11US$1,152US$348US$46
2026-08-12US$1,210US$360US$54
2026-08-13US$1,311US$433US$48
2026-08-14US$1,394US$381US$116
2026-08-15US$1,518US$322US$129
2026-08-16US$1,340US$270US$71
2026-08-17US$1,184US$388US$47
2026-08-18US$1,792US$419US$112
2026-08-19US$2,097US$352US$77
2026-08-20US$1,376US$338US$98
2026-08-21US$1,662US$365US$90
2026-08-22US$1,473US$322US$105
2026-08-23US$1,194US$381US$63
2026-08-24US$1,045US$429US$56

The five findings

1. adsSpend returns $0 on every daycorrupts live numbers

The per-channel fields are populated, but the aggregate field is not. Anything computed off total ad spend — blended ROAS, MER, blended CAC — is therefore dividing by zero or falling back to something undocumented. These are exactly the metrics a CEO dashboard leads with, which makes this the one to fix first.

adsSpend: US$0US$0adsSpendwhat TW reports Google+Meta+TikTok: US$63,244US$63,244Google+Meta+TikTokwhat the channels sum to
What adsSpend reports, against what the channel fields sum to, over the same 30 days.

Ask: is adsSpend mapped in the connector at all? If it is meant to be derived, deriving it as ga_adCost + fb_ads_spend + tiktok_spend would land within ~7% of blendedAds (US$67,412), which does look correct.

2. blendedSales is 27% above totalSalesneeds a definition

blendedSales sums to US$206,705 against totalSales of US$162,839 over the same 30 days — and above Shopify's gross sales for the period, so it is not simply gross-vs-net. It may well be a legitimate TW definition, but it is undocumented, and until someone can say what it counts neither figure is safe to put in front of Gabe.

Ask: what does blendedSales include that totalSales does not?

3 & 4. The two attribution tables are emptyblocks attribution entirely

TableRowsSchemaLast modified (Sydney)
raw.shopify_order_attribution0valid — 6 cols (order_id, session_id, quiz_flow_id, …)2026-08-17 11:32
raw.quiz_events_utm0valid — 17 cols incl. utm_source/medium/campaign/id2026-08-10 15:27

Both have correct schemas and no rows, so this reads as an ingest that never ran rather than a modelling error. With both empty, paid attribution cannot be closed from the warehouse at all.

There is very likely a shared root cause upstream, and it is visible from the Shopify side: Meta ad URLs appear to carry no UTM parameters. Meta sent 8,330 link clicks in this window; Shopify classified only 82 sessions as paid, and logged Meta's traffic as ordinary social referrals instead. If that is right, there is nothing upstream for either table to ingest, and fixing the UTMs fixes the pipeline and the attribution together.

Meta link clicks (Meta API): 8,330Meta link clicks (Meta API)8,330Sessions Shopify sees as social: 5,902Sessions Shopify sees as social5,902Sessions Shopify sees as PAID: 82Sessions Shopify sees as PAID82Purchases TW ties to paid: 88Purchases TW ties to paid88New customers Shopify recorded: 513New customers Shopify recorded513
From ad click to recorded customer, same 30-day window. Sources: Meta Marketing API, Shopify sessions, Triple Whale, Shopify customers.

5. The daily series is undeduped, gapped, and shortbreaks comparisons

2026-06-23: present2026-06-24: present2026-06-25: present2026-06-26: present2026-06-27: present2026-06-28: present2026-06-29: MISSING2026-06-30: present2026-07-01: DUPLICATED (and 07-02 restated)2026-07-02: DUPLICATED (and 07-02 restated)2026-07-03: present2026-07-04: present2026-07-05: present2026-07-06: present2026-07-07: present2026-07-08: present2026-07-09: present2026-07-10: present2026-07-11: present2026-07-12: present2026-07-13: present2026-07-14: present2026-07-15: present2026-07-16: present2026-07-17: present2026-07-18: present2026-07-19: present2026-07-20: present2026-07-21: MISSING2026-07-22: present2026-07-23: present2026-07-24: present2026-07-25: present2026-07-26: present2026-07-27: present2026-07-28: present2026-07-29: present2026-07-30: present2026-07-31: present2026-08-01: present2026-08-02: present2026-08-03: present2026-08-04: present2026-08-05: present2026-08-06: present2026-08-07: present2026-08-08: present2026-08-09: present2026-08-10: present2026-08-11: present2026-08-12: present2026-08-13: present2026-08-14: present2026-08-15: present2026-08-16: present2026-08-17: present2026-08-18: present2026-08-19: present2026-08-20: present2026-08-21: present2026-08-22: present2026-08-23: present2026-08-24: present06-2307-0108-01
Present Duplicated Missing
One cell per calendar day, 2026-06-23 → 2026-08-24. Hover any cell for its status.

Duplicated: 2026-07-01 and 2026-07-02 each appear twice, and 07-02 was restated between loads (US$6,108 → US$5,682) with no dedup on loaded_at. Any consumer that does not dedup will double-count those days.
Missing: 2026-06-29 and 2026-07-21 are absent entirely.
Short: the series begins 2026-06-23, so there is no quarter-on-quarter comparison, let alone year-on-year.

Ask: can the loader dedup on latest loaded_at, backfill the two gaps, and is a longer history available from the TW API?

Reproducing this

On rounding: the daily table rounds each day to the dollar, so its column sums land within ~$2 of the unrounded aggregates (US$63,246 total paid, US$39,977 Google). Query the warehouse directly for exact figures.

Every figure above comes from lucky-nature-498502-h2.raw.triple_whale_summary unless stated otherwise, deduped on latest loaded_at. Run these to check any of it.

Channel spend — note Google is `ga_adCost`, not a `*Spend` column

SELECT ROUND(SUM(ga_adCost),0)   AS google,
       ROUND(SUM(fb_ads_spend),0) AS meta,
       ROUND(SUM(tiktok_spend),0) AS tiktok,
       ROUND(SUM(adsSpend),0)     AS ads_spend_field,
       ROUND(SUM(blendedAds),0)   AS blended_ads
FROM `lucky-nature-498502-h2.raw.triple_whale_summary`
WHERE DATE_DIFF(DATE(date_end), DATE(date_start), DAY) = 0
  AND DATE(date_start) BETWEEN '2026-07-26' AND '2026-08-24';

Empty attribution tables

SELECT table_id, row_count,
       TIMESTAMP_MILLIS(last_modified_time) AS last_modified
FROM `lucky-nature-498502-h2.raw.__TABLES__`
WHERE table_id IN ('shopify_order_attribution','quiz_events_utm');

Duplicated + restated days

SELECT DATE(date_start) AS day, COUNT(*) AS n,
       STRING_AGG(CAST(ROUND(totalSales,0) AS STRING), ' | ') AS variants
FROM `lucky-nature-498502-h2.raw.triple_whale_summary`
WHERE DATE_DIFF(DATE(date_end), DATE(date_start), DAY) = 0
GROUP BY day HAVING COUNT(*) > 1;

What I am asking for

In priority order: (1) fix or derive adsSpend — it is breaking metrics already on screen. (2) confirm the UTM situation on ad URLs, which I believe is the root cause of findings 3 and 4. (3) define blendedSales. (4) dedup and backfill the daily series.

Happy to take any of this to the Triple Whale side rather than yours if that is the faster path — I mainly need to know which numbers I can safely put in front of Gabe in the meantime.

Generated 2026-08-26 by database/tw_data_quality_report.py. Timestamps Australia/Sydney; BigQuery stores UTC.