Your GA4 reports show $150,000 in revenue for last month. Your order management system shows $180,000. The $30,000 discrepancy isn’t just a rounding error — it’s caused by specific, identifiable technical issues. This guide explains the most common reasons GA4 revenue diverges from backend revenue, and shows you how to use BigQuery to systematically audit and reconcile the numbers.

Why GA4 Revenue Almost Never Matches Backend

  • Ad blockers: 15-25% of users block GA4 pixels. Their purchases never reach GA4.
  • Test orders: QA team’s test purchases reach the backend but may not fire GA4 events.
  • Refunds: Backend deducts refunds. GA4 may not have refund events configured.
  • Tax and shipping: Backend revenue includes tax + shipping. GA4 value parameter may be subtotal only.
  • Currency: Multi-currency stores may report in different currencies in each platform.

Step 1: Match Orders Between GA4 and Backend

-- Full outer join to find gaps
SELECT
  COALESCE(ga4.transaction_id, backend.order_id) as order_id,
  ga4.event_date as ga4_date,
  backend.order_date,
  ga4.ga4_revenue,
  backend.revenue as backend_revenue,
  CASE
    WHEN ga4.transaction_id IS NULL THEN 'Missing from GA4 (ad blocker)'
    WHEN backend.order_id IS NULL THEN 'In GA4 only (test order?)'
    WHEN ABS(ga4.ga4_revenue - backend.revenue) > 1 THEN 'Revenue mismatch (tax/shipping?)'
    ELSE 'Matched'
  END as reconciliation_status
FROM `project.dataset.ga4_purchases` ga4
FULL OUTER JOIN `project.dataset.backend_orders` backend
  ON ga4.transaction_id = backend.order_id
ORDER BY reconciliation_status, ga4_revenue DESC;

Fixing Missing GA4 Orders: Measurement Protocol

BigQuery GA4 Revenue Reconciliation: Why GA4 Revenue Differs from Your Backend Database
// Server-side purchase via Measurement Protocol fallback
async function sendGA4PurchaseEvent(order, clientId) {
  const payload = {
    client_id: clientId,
    user_id: order.userId,
    events: [{
      name: 'purchase',
      params: {
        transaction_id: order.orderId,
        value: order.subtotal,  // Pre-tax, pre-shipping
        currency: order.currency,
        items: order.items.map(item => ({
          item_id: item.sku,
          item_name: item.name,
          price: item.price,
          quantity: item.quantity
        }))
      }
    }]
  };
  
  await axios.post(
    `https://www.google-analytics.com/mp/collect?measurement_id=${GA4_ID}&api_secret=${API_SECRET}`,
    payload
  );
}
// Call this from backend after order saved, regardless of whether pixel fired

Expected Gap Benchmarks

  • Target: GA4 revenue = 80-90% of backend revenue (the gap is ad blockers and privacy)
  • Alert if gap exceeds 25%: indicates tracking implementation issue
  • Track gap trend over time: if growing, something in your tracking broke

Revenue reconciliation isn’t about making GA4 numbers match your backend exactly — it’s about understanding the systematic reasons for each category of gap, so you can trust your data and explain discrepancies to stakeholders.

Related guides: GA4 Refund Tracking, BigQuery User ID Deduplication, GA4 Event Count Discrepancies.

Guide: BigQuery GA4 Revenue Reconciliation: Why GA4 Reven

Leave a Comment