GA4 Explore funnels hit limits fast: 10-step maximum, no custom date comparisons, session timeouts cutting off multi-day journeys, and quota errors on high-traffic properties. BigQuery gives you unlimited funnel steps, arbitrary time windows, and exact control over what counts as a conversion. This guide shows production-quality funnel analysis using GA4 raw data.

GA4 Event Data Structure for Funnels

GA4 exports one row per event. Each row has user_pseudo_id, event_timestamp, event_name, and nested event_params. A funnel query joins these event rows in sequence per user.

-- Basic funnel: page_view to purchase
WITH events AS (
  SELECT user_pseudo_id, event_name, event_timestamp
  FROM `project.analytics_XXXXXXXXX.events_*`
  WHERE _TABLE_SUFFIX BETWEEN '20260401' AND '20260430'
    AND event_name IN ('page_view','add_to_cart','begin_checkout','purchase')
),
funnel AS (
  SELECT user_pseudo_id,
    MAX(CASE WHEN event_name='page_view' THEN 1 ELSE 0 END) AS step1,
    MAX(CASE WHEN event_name='add_to_cart' THEN 1 ELSE 0 END) AS step2,
    MAX(CASE WHEN event_name='begin_checkout' THEN 1 ELSE 0 END) AS step3,
    MAX(CASE WHEN event_name='purchase' THEN 1 ELSE 0 END) AS step4
  FROM events GROUP BY 1
)
SELECT
  COUNTIF(step1=1) AS entered_funnel,
  COUNTIF(step2=1) AS added_to_cart,
  COUNTIF(step3=1) AS began_checkout,
  COUNTIF(step4=1) AS purchased,
  ROUND(COUNTIF(step4=1)/COUNTIF(step1=1)*100,1) AS overall_cvr
FROM funnel WHERE step1=1;

Ordered Funnel: Enforce Step Sequence

-- Strict ordered funnel using timestamps
WITH user_steps AS (
  SELECT user_pseudo_id,
    MIN(CASE WHEN event_name='page_view' THEN event_timestamp END) AS t1,
    MIN(CASE WHEN event_name='add_to_cart' THEN event_timestamp END) AS t2,
    MIN(CASE WHEN event_name='begin_checkout' THEN event_timestamp END) AS t3,
    MIN(CASE WHEN event_name='purchase' THEN event_timestamp END) AS t4
  FROM `project.analytics_XXXXXXXXX.events_*`
  WHERE _TABLE_SUFFIX BETWEEN '20260401' AND '20260430'
    AND event_name IN ('page_view','add_to_cart','begin_checkout','purchase')
  GROUP BY 1
)
SELECT
  COUNT(*) AS step1_users,
  COUNTIF(t2 > t1) AS step2_users,
  COUNTIF(t3 > t2 AND t2 > t1) AS step3_users,
  COUNTIF(t4 > t3 AND t3 > t2 AND t2 > t1) AS step4_users
FROM user_steps WHERE t1 IS NOT NULL;
BigQuery GA4 Funnel Analysis: Build Conversion Funnels Without Explore Limits

Funnel With Time Window Constraint

-- Only count conversions within 7 days of first page_view
SELECT
  COUNT(*) AS step1,
  COUNTIF(t4 > t1 AND t4 < t1 + 7*24*60*60*1000000
          AND t4 > t3 AND t3 > t2 AND t2 > t1) AS step4_7day
FROM user_steps WHERE t1 IS NOT NULL;
-- GA4 timestamps are in microseconds

Segment Funnel by Acquisition Channel

-- Funnel CVR by channel (unlimited segments, unlike GA4 Explore)
WITH first_touch AS (
  SELECT user_pseudo_id,
    (SELECT value.string_value FROM UNNEST(event_params) WHERE key='source') AS source,
    (SELECT value.string_value FROM UNNEST(event_params) WHERE key='medium') AS medium
  FROM `project.analytics_XXXXXXXXX.events_*`
  WHERE _TABLE_SUFFIX BETWEEN '20260401' AND '20260430'
    AND event_name = 'session_start'
  QUALIFY ROW_NUMBER() OVER(PARTITION BY user_pseudo_id ORDER BY event_timestamp) = 1
)
SELECT CONCAT(ft.source,'/',ft.medium) AS channel,
  COUNT(DISTINCT us.user_pseudo_id) AS entered,
  COUNTIF(us.t4 IS NOT NULL) AS purchased,
  ROUND(COUNTIF(us.t4 IS NOT NULL)/COUNT(DISTINCT us.user_pseudo_id)*100,2) AS cvr
FROM user_steps us JOIN first_touch ft USING(user_pseudo_id)
WHERE us.t1 IS NOT NULL GROUP BY 1 ORDER BY entered DESC;

Connect to Looker Studio

Save your funnel query as a BigQuery View, connect it to Looker Studio, and create a bar chart with step labels on X-axis and user counts on Y-axis. Add date range controls and channel filters for a live interactive funnel explorer.

Related: BigQuery Cohort Analysis, GA4 Explorations Quota Limits.

Guide

Leave a Comment