Обложка статьи PPC Rebels про GA4 BigQuery Export для платного трафика в 2026 году

GA4 BigQuery Export для платного трафика 2026: лимиты, схема и 6 SQL-запросов

Интерфейс GA4 отвечает на вопросы, которые придумали за вас. «Сколько конверсий принесла кампания» — отвечает. «Какой процент пользователей, пришедших по платному трафику в понедельник, дошёл до второго шага формы и вернулся в течение 14 дней» — уже нет: вы упираетесь в семплирование, лимит кардинальности и строку (other), которая съедает длинный хвост. Ответ на такие вопросы живёт не в интерфейсе, а в сырых данных, которые отдаёт GA4 BigQuery Export.

GA4 BigQuery Export снимает это ограничение: вы получаете сырые события, одну строку на событие, без агрегации и без семплирования, и спрашиваете у данных то, что нужно вам. Ниже — типы экспорта и лимиты, структура таблиц, шесть готовых SQL-запросов под платный трафик и правила, которые не дают счёту за BigQuery вырасти в неприятный сюрприз.

Чем GA4 BigQuery Export отличается от интерфейса

ВопросИнтерфейс GA4BigQuery Export
ДетализацияАгрегаты по измерениямОдна строка на событие, с параметрами и пользователем
СемплированиеВозможно в исследованиях и при больших объёмахНет
КардинальностьДлинный хвост сворачивается в (other)Все значения как есть
Срок храненияОграничен настройками свойстваСтолько, сколько вы храните таблицы
Логика атрибуцииМодель GoogleЛюбая, которую вы напишете в SQL
Склейка с другими даннымиНетДжойн с CRM, расходами, фидом в одном датасете

Главный выигрыш для платного трафика — последняя строка. Когда данные Google Ads и события GA4 лежат в одном проекте, расходы, клики, лиды и выручка сшиваются одним запросом. Как настроить вторую половину этой связки, разобрано в материале про выгрузку Google Ads в BigQuery.

Типы экспорта и лимиты, которые решают всё

При связке свойства с BigQuery вы выбираете типы экспорта независимо друг от друга:

  • Daily. Один раз в сутки выгружаются полные, несемплированные события за предыдущий день. Обычно к середине дня по таймзоне свойства, иногда позже. Доступен и в стандартных, и в 360-свойствах.
  • Fresh Daily. Только для GA4 360 (свойства Normal и Large). Данные обычно к 5 утра плюс батчевые обновления в течение дня, как правило в пределах часа. Требует включённого биллинга в Google Cloud.
  • Streaming. Почти реальное время, обычно минуты. Работает по принципу best effort: возможны пропуски. Лимита по объёму нет, но из потока исключены данные об источнике трафика для новых пользователей и новых сессий.

Таблицы, с которыми вы будете работать:

  • events_YYYYMMDD — полный суточный экспорт. Для стабильных отчётов запрашивать нужно именно его.
  • events_intraday_YYYYMMDD — промежуточная таблица стриминга за текущий день. Удаляется, когда суточная таблица готова. Удобна для дежурных дашбордов, не годится для отчётности.

Лимит 1 млн событий в сутки

Для стандартных свойств суточный экспорт ограничен 1 миллионом событий в день (для 360 — до 20 миллиардов). Это и есть главное ограничение, в которое упираются проекты со средним трафиком, и о нём лучше узнать заранее, а не в день, когда таблица оказалась обрезанной.

Что с этим делают на практике:

  1. Фильтруют события на уровне экспорта. При настройке связки можно исключить события, которые не нужны в хранилище. Чаще всего это scroll, user_engagement, кастомные события автотрекинга кликов по каждой кнопке. В среднем проекте это 40–70% объёма (ориентир — зависит от того, насколько щедро настроен enhanced measurement).
  2. Не отправляют в GA4 то, что там не нужно. Самое дешёвое сокращение — не собирать мусор вообще, а не фильтровать его на выходе.
  3. Включают streaming как страховку. У стриминга лимита нет, и если суточный экспорт обрезался, дневной поток остаётся полным. Ценой дополнительной платы за ингест.

Сколько это стоит

Экспорт сам по себе бесплатен, платите вы BigQuery за хранение и обработку. Отдельная строка — стриминг: 0,05 $ за гигабайт ингеста, где один гигабайт — это примерно 600 000 событий (ориентир: зависит от размера события). Бесплатный sandbox BigQuery подойдёт для теста, но выходы за его лимиты уже тарифицируются.

Иначе говоря, для проекта на миллион событий в сутки стриминг — это порядка 50 $ в месяц плюс хранение и запросы. Это дешевле любого внешнего BI, но только если запросы написаны аккуратно — об этом ниже.

Схема: что где лежит

Строка таблицы — одно событие. Самое неочевидное: параметры лежат во вложенных массивах, и к ним нельзя обратиться как к колонкам — нужен UNNEST.

ПолеЧто внутриЧто помнить
event_name, event_timestampИмя события и время в микросекундах UTCДелить на 1 000 000 перед конвертацией в дату
event_paramsМассив key + value (string/int/float/double)Тип значения нужно выбирать явно: value.string_value и т.д.
user_pseudo_idИдентификатор устройства/браузераНе человек: новый браузер — новый id
user_idВаш собственный идентификаторПоявляется только если вы его передаёте. Основа для честного LTV
user_propertiesПользовательские свойстваЗначение на момент события, не текущее
traffic_sourceИсточник первого визита пользователяНе меняется потом. В стриминге для новых пользователей отсутствует
collected_traffic_sourceЧто реально пришло в URL, включая gclidЕсли traffic_source пуст — gclid можно искать здесь. gbraid и wbraid в экспорт не попадают
session_traffic_source_last_clickИсточник по последнему клику внутри сессииСамое близкое к «кампании, которая привела эту сессию»
itemsМассив товаров для e-commerce событийНужен UNNEST; здесь живут цена, категория, item_id
privacy_infoПризнаки режима согласияВ экспорт попадают и cookieless-пинги

Важная деталь для тех, кто сверяет цифры: атрибуция существующих пользователей досчитывается примерно в течение 24 часов, поэтому для атрибуционных отчётов корректно брать суточную таблицу, а не стриминг. Если вас интересует, почему цифры в GA4 и Google Ads всё равно не совпадают до клика, это разобрано отдельно в статье про расхождение конверсий между Google Ads и GA4.

Шесть запросов, с которых стоит начать

1. Сессии и конверсии по кампании из gclid

Базовый отчёт, которого не хватает в интерфейсе: кампания берётся не из UTM, а из идентификатора клика.

SELECT
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key='campaign') AS campaign,
  COUNT(DISTINCT CONCAT(user_pseudo_id, '-',
    (SELECT value.int_value FROM UNNEST(event_params) WHERE key='ga_session_id'))) AS sessions,
  COUNTIF(event_name='purchase') AS purchases,
  SUM(CASE WHEN event_name='purchase' THEN ecommerce.purchase_revenue END) AS revenue
FROM `project.analytics_XXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20261001' AND '20261007'
  AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key='medium') = 'cpc'
GROUP BY campaign
ORDER BY revenue DESC

2. Воронка по шагам для платного трафика

Считает людей, а не события, и позволяет увидеть, на каком шаге рвётся именно платный поток.

WITH paid AS (
  SELECT user_pseudo_id, event_name
  FROM `project.analytics_XXXXXX.events_*`
  WHERE _TABLE_SUFFIX BETWEEN '20261001' AND '20261007'
    AND session_traffic_source_last_click.google_ads_campaign.campaign_name IS NOT NULL
)
SELECT
  COUNT(DISTINCT user_pseudo_id) AS visitors,
  COUNT(DISTINCT IF(event_name='view_item', user_pseudo_id, NULL)) AS viewed,
  COUNT(DISTINCT IF(event_name='begin_checkout', user_pseudo_id, NULL)) AS checkout,
  COUNT(DISTINCT IF(event_name='purchase', user_pseudo_id, NULL)) AS purchased
FROM paid

3. Поиск дублей транзакций

Классическая причина завышенной выручки — повторная отправка purchase при перезагрузке страницы «спасибо».

SELECT
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key='transaction_id') AS tx,
  COUNT(*) AS hits
FROM `project.analytics_XXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20261001' AND '20261007'
  AND event_name='purchase'
GROUP BY tx
HAVING hits > 1
ORDER BY hits DESC

Если дубли нашлись, чинить нужно не отчёт, а сбор событий — методы разобраны в материале про дубли конверсий и дедупликацию.

4. Когорты LTV по первому источнику

Главный запрос для оценки платного трафика по-настоящему: сколько приносит пользователь, пришедший из конкретной кампании, за 30/60/90 дней — а не за один визит.

WITH first_touch AS (
  SELECT user_pseudo_id,
         MIN(event_timestamp) AS first_ts,
         ANY_VALUE(traffic_source.name) AS first_campaign
  FROM `project.analytics_XXXXXX.events_*`
  WHERE _TABLE_SUFFIX BETWEEN '20260701' AND '20261007'
  GROUP BY user_pseudo_id
),
rev AS (
  SELECT user_pseudo_id, event_timestamp, ecommerce.purchase_revenue AS r
  FROM `project.analytics_XXXXXX.events_*`
  WHERE _TABLE_SUFFIX BETWEEN '20260701' AND '20261007' AND event_name='purchase'
)
SELECT f.first_campaign,
       COUNT(DISTINCT f.user_pseudo_id) AS users,
       SUM(IF(r.event_timestamp <= f.first_ts + 30*86400*1000000, r.r, 0)) AS ltv_30d,
       SUM(IF(r.event_timestamp <= f.first_ts + 90*86400*1000000, r.r, 0)) AS ltv_90d
FROM first_touch f LEFT JOIN rev r USING (user_pseudo_id)
GROUP BY f.first_campaign
ORDER BY ltv_90d DESC

5. Расхождение платных сессий по дням

Ежедневный счётчик сессий google / cpc, который сверяется с кликами из выгрузки Google Ads. Если расхождение прыгает в конкретный день — ищите изменение в схеме ссылок, а не в данных.

SELECT PARSE_DATE('%Y%m%d', _TABLE_SUFFIX) AS day,
       COUNT(DISTINCT CONCAT(user_pseudo_id, '-',
         (SELECT value.int_value FROM UNNEST(event_params) WHERE key='ga_session_id'))) AS paid_sessions
FROM `project.analytics_XXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260901' AND '20261007'
  AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key='medium')='cpc'
GROUP BY day ORDER BY day

Чаще всего источник проблемы — именно слой URL-параметров: потерянный query string, дубль UTM, чужой шаблон отслеживания на уровне кампании. Разбор этого слоя — в статье про parallel tracking и URL-параметры в Google Ads.

6. Лендинги платного трафика по конверсии

SELECT
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key='page_location') AS lp,
  COUNT(DISTINCT user_pseudo_id) AS users,
  COUNT(DISTINCT IF(event_name='generate_lead', user_pseudo_id, NULL)) AS leads,
  SAFE_DIVIDE(COUNT(DISTINCT IF(event_name='generate_lead', user_pseudo_id, NULL)),
              COUNT(DISTINCT user_pseudo_id)) AS cvr
FROM `project.analytics_XXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20261001' AND '20261007'
  AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key='medium')='cpc'
GROUP BY lp HAVING users > 100 ORDER BY cvr DESC

Как не сжечь бюджет на запросах

BigQuery берёт деньги за просканированные байты, а не за время выполнения. Отсюда правила:

  • Всегда ограничивайте _TABLE_SUFFIX. Запрос по events_* без фильтра по дате сканирует всю историю. Это самая дорогая опечатка в GA4-аналитике.
  • Никогда не пишите SELECT *. Колоночное хранение означает, что вы платите только за выбранные поля — и за все, если указали звёздочку.
  • Делайте агрегатные таблицы. Один ночной запрос, который складывает сессии, расходы и конверсии по дням и кампаниям, превращает дашборд из тысячи сканирований сырых событий в одно чтение маленькой таблицы.
  • Используйте превью и dry run. Консоль показывает объём сканирования до запуска запроса. Привычка смотреть на эту цифру экономит больше, чем любые оптимизации.
  • Держите лимит на проект. В Cloud можно задать квоту на объём обрабатываемых данных — страховка от случайного запроса по всей истории.

Правило большого пальца: если дашборд обновляется каждые пять минут и читает сырые события, вы платите не за аналитику, а за повторное сканирование одних и тех же данных. Сырые таблицы — для исследований, агрегаты — для регулярной отчётности.

Типичные грабли при внедрении

  1. Нет стриминга — нет сегодняшнего дня. Команда настраивает только daily, а потом удивляется, что дашборд отстаёт на сутки. Решается включением стриминга при связке.
  2. Таймзона. Таблицы названы по дате в таймзоне свойства, а event_timestamp — в UTC. Если не привести к одной зоне, суточные отчёты будут расходиться со интерфейсом на несколько процентов.
  3. user_pseudo_id принимают за человека. Это идентификатор браузера. Любой кросс-девайсный анализ требует user_id, то есть передачи вашего собственного идентификатора.
  4. Режим согласия игнорируют. В экспорт попадают и cookieless-пинги; если их не учитывать, часть событий окажется без пользователя и воронка «потеряет» людей. Как устроен этот слой — в материале про Consent Mode v2 и приватность рекламы.
  5. Ожидают, что экспорт заполнит историю. Экспорт работает только вперёд: данные до момента включения связки в BigQuery не появятся никогда. Поэтому включать его стоит в тот день, когда вы о нём узнали, даже если запросы напишете через полгода.
  6. Сравнивают сырые данные с интерфейсом «один к одному». Интерфейс применяет свою модель атрибуции и свои правила склейки сессий. Полного совпадения не будет и не должно быть; разницу нужно понимать, а не устранять. Что здесь задаёт модель — разобрано в статье про модели атрибуции DDA и last click.

Агрегатная таблица: один запрос, который экономит всё остальное

Девять из десяти дашбордов отвечают на один и тот же набор вопросов: сколько потратили, сколько получили сессий, лидов и выручки — по дням и кампаниям. Если дашборд каждый раз сканирует сырые события, вы платите за одно и то же сканирование десятки раз в день. Решение — ночной запрос, который складывает факты в маленькую таблицу.

CREATE OR REPLACE TABLE `project.marts.paid_daily`
PARTITION BY day AS
WITH sessions AS (
  SELECT PARSE_DATE('%Y%m%d', _TABLE_SUFFIX) AS day,
         (SELECT value.string_value FROM UNNEST(event_params) WHERE key='campaign') AS campaign,
         COUNT(DISTINCT CONCAT(user_pseudo_id,'-',
           (SELECT value.int_value FROM UNNEST(event_params) WHERE key='ga_session_id'))) AS sessions,
         COUNTIF(event_name='generate_lead') AS leads,
         SUM(CASE WHEN event_name='purchase' THEN ecommerce.purchase_revenue END) AS revenue
  FROM `project.analytics_XXXXXX.events_*`
  WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 400 DAY))
                          AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
    AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key='medium')='cpc'
  GROUP BY day, campaign
)
SELECT * FROM sessions

Дальше к этой таблице джойнятся расходы и клики из выгрузки Google Ads по дате и кампании — и получается единственный источник правды, который читается мгновенно и стоит копейки. Три правила, которые делают такую таблицу надёжной:

  • Партиционирование по дате. Иначе любой запрос «за последние 30 дней» будет сканировать всю таблицу.
  • Пересчёт с запасом. Пересобирайте не только вчерашний день, а последние 3–7 дней: атрибуция существующих пользователей досчитывается, и цифра за позавчера может измениться.
  • Один набор определений. Что считается сессией, что лидом, какое событие — выручкой: эти определения живут в одном SQL, а не в каждом дашборде по-своему. Половина споров «а почему у нас две разные цифры» рождается именно здесь.

Склейка с CRM: зачем это нужно платному трафику

GA4 знает, что форма отправлена. Знает ли она, что лид оказался квалифицированным и принёс деньги через шесть недель — нет. Именно эта часть превращает экспорт из аналитической игрушки в инструмент управления бюджетом.

Схема простая: в таблицу CRM-сделок выгружаются gclid (или ваш внутренний идентификатор сессии), дата, стадия и сумма. В BigQuery это джойнится с событиями GA4, и вы получаете две вещи, недоступные в интерфейсе:

  1. Качество трафика по кампаниям. Не CPL, а стоимость квалифицированного лида и стоимость сделки. Нередко кампания с худшим CPL оказывается лучшей по стоимости сделки — и именно её сокращают первой, не имея этих данных.
  2. Реальный лаг конверсии. Распределение времени от клика до сделки показывает, какое окно конверсии честно отражает ваш цикл продаж. Если 40% сделок закрываются на 25–45 день, окно в 30 дней систематически недооценивает половину кампаний.

Практический минимум: gclid в заявке, ночная выгрузка сделок, один джойн. Что писать в CRM и где параметры чаще всего теряются, разобрано в статье про parallel tracking и URL-параметры в Google Ads.

Чек-лист внедрения

  • Связка GA4 → BigQuery включена, выбраны daily и streaming.
  • Лишние события исключены из экспорта; суточный объём с запасом ниже 1 млн (для стандартного свойства).
  • Датасет лежит в том же проекте, что выгрузка Google Ads — чтобы джойнить без федеративных запросов.
  • Задана квота на объём обрабатываемых данных.
  • Есть один ночной запрос, собирающий агрегатную таблицу «день × кампания × сессии × конверсии × выручка».
  • Дашборды читают агрегаты, исследования — сырые таблицы.
  • user_id передаётся туда, где он известен (авторизованная зона, оформление заказа).
  • Прописано, кто и когда проверяет расхождение с Google Ads.

Если инфраструктура только собирается, а бюджеты уже идут — порядок такой: сначала корректный сбор и ссылки, затем экспорт, затем аналитика поверх него. Обратный порядок даёт красивые запросы к плохим данным. Пошагово разложить этот путь под свой проект помогает обучение по Google Ads, а если вопрос в стабильном кабинете под масштабирование — есть агентские аккаунты Google Ads.

FAQ

Сколько стоит GA4 BigQuery Export?

Сам экспорт бесплатен. Платите BigQuery: хранение, обработка запросов и, если включён стриминг, 0,05 $ за гигабайт ингеста (примерно 600 000 событий на гигабайт — ориентир, зависит от размера событий).

Какой лимит у стандартного свойства?

1 миллион событий в сутки для суточного экспорта. У 360-свойств — до 20 миллиардов. У стриминга лимита по объёму нет.

Что делать, если упираюсь в миллион событий?

Исключить ненужные события при настройке связки (чаще всего это автотрекинг прокруток и кликов), перестать собирать мусор в самом GA4 и включить стриминг как страховку.

Можно ли получить исторические данные?

Нет. Экспорт начинает работать с момента включения и наполняет таблицы только вперёд. Это главный аргумент включать его как можно раньше.

Чем events_intraday_ отличается от events_?

Первая — промежуточная таблица стриминга за текущий день, удаляется после формирования суточной. Вторая — полный суточный экспорт, именно её нужно использовать для стабильной отчётности.

Почему traffic_source пустой у части строк?

В стриминге данные об источнике для новых пользователей и новых сессий не передаются, а атрибуция существующих досчитывается около суток. Для источников нужно брать суточную таблицу; gclid при этом можно найти в collected_traffic_source.

Есть ли в экспорте gbraid и wbraid?

Нет. В экспорт попадает gclid, идентификаторы gbraid и wbraid в нём отсутствуют — это нужно учитывать при склейке приложений и iOS-трафика.

Нужен ли платный Google Cloud?

Для начала достаточно бесплатного sandbox, но у него есть ограничения по хранению и возможностям. Fresh Daily (только 360) требует включённого биллинга. Для рабочего контура биллинг практически обязателен.

Как связать данные GA4 и Google Ads?

Положить обе выгрузки в один проект BigQuery и джойнить по дате, ID кампании и gclid. Так расходы и клики из Google Ads сопоставляются с событиями и выручкой из GA4 в одном запросе.

Будут ли цифры совпадать с интерфейсом GA4?

Не полностью, и это нормально: интерфейс применяет собственную модель атрибуции и правила склейки сессий. Разницу нужно объяснить и зафиксировать, а не пытаться свести к нулю.

Нужен ли BI-инструмент поверх BigQuery?

Для регулярных отчётов удобен — но подключать его следует к агрегатным таблицам, а не к сырым событиям, иначе каждое обновление дашборда стоит денег.

Можно ли удалять старые таблицы?

Да, и это основной способ контролировать расходы на хранение. Практичный подход: сырые события держать 13–14 месяцев (чтобы была база год к году), а агрегаты — бессрочно, они занимают доли процента объёма.

Читайте также: Ask Advisor в Google Ads 2026: что ИИ-агент реально меняет в аккаунте и где ему нужны рамки

Похожие записи