GA4 BigQuery Export для платного трафика 2026: лимиты, схема и 6 SQL-запросов
Интерфейс GA4 отвечает на вопросы, которые придумали за вас. «Сколько конверсий принесла кампания» — отвечает. «Какой процент пользователей, пришедших по платному трафику в понедельник, дошёл до второго шага формы и вернулся в течение 14 дней» — уже нет: вы упираетесь в семплирование, лимит кардинальности и строку (other), которая съедает длинный хвост. Ответ на такие вопросы живёт не в интерфейсе, а в сырых данных, которые отдаёт GA4 BigQuery Export.
GA4 BigQuery Export снимает это ограничение: вы получаете сырые события, одну строку на событие, без агрегации и без семплирования, и спрашиваете у данных то, что нужно вам. Ниже — типы экспорта и лимиты, структура таблиц, шесть готовых SQL-запросов под платный трафик и правила, которые не дают счёту за BigQuery вырасти в неприятный сюрприз.
Чем GA4 BigQuery Export отличается от интерфейса
| Вопрос | Интерфейс GA4 | BigQuery 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 миллиардов). Это и есть главное ограничение, в которое упираются проекты со средним трафиком, и о нём лучше узнать заранее, а не в день, когда таблица оказалась обрезанной.
Что с этим делают на практике:
- Фильтруют события на уровне экспорта. При настройке связки можно исключить события, которые не нужны в хранилище. Чаще всего это
scroll,user_engagement, кастомные события автотрекинга кликов по каждой кнопке. В среднем проекте это 40–70% объёма (ориентир — зависит от того, насколько щедро настроен enhanced measurement). - Не отправляют в GA4 то, что там не нужно. Самое дешёвое сокращение — не собирать мусор вообще, а не фильтровать его на выходе.
- Включают 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 можно задать квоту на объём обрабатываемых данных — страховка от случайного запроса по всей истории.
Правило большого пальца: если дашборд обновляется каждые пять минут и читает сырые события, вы платите не за аналитику, а за повторное сканирование одних и тех же данных. Сырые таблицы — для исследований, агрегаты — для регулярной отчётности.
Типичные грабли при внедрении
- Нет стриминга — нет сегодняшнего дня. Команда настраивает только daily, а потом удивляется, что дашборд отстаёт на сутки. Решается включением стриминга при связке.
- Таймзона. Таблицы названы по дате в таймзоне свойства, а
event_timestamp— в UTC. Если не привести к одной зоне, суточные отчёты будут расходиться со интерфейсом на несколько процентов. user_pseudo_idпринимают за человека. Это идентификатор браузера. Любой кросс-девайсный анализ требуетuser_id, то есть передачи вашего собственного идентификатора.- Режим согласия игнорируют. В экспорт попадают и cookieless-пинги; если их не учитывать, часть событий окажется без пользователя и воронка «потеряет» людей. Как устроен этот слой — в материале про Consent Mode v2 и приватность рекламы.
- Ожидают, что экспорт заполнит историю. Экспорт работает только вперёд: данные до момента включения связки в BigQuery не появятся никогда. Поэтому включать его стоит в тот день, когда вы о нём узнали, даже если запросы напишете через полгода.
- Сравнивают сырые данные с интерфейсом «один к одному». Интерфейс применяет свою модель атрибуции и свои правила склейки сессий. Полного совпадения не будет и не должно быть; разницу нужно понимать, а не устранять. Что здесь задаёт модель — разобрано в статье про модели атрибуции 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, и вы получаете две вещи, недоступные в интерфейсе:
- Качество трафика по кампаниям. Не CPL, а стоимость квалифицированного лида и стоимость сделки. Нередко кампания с худшим CPL оказывается лучшей по стоимости сделки — и именно её сокращают первой, не имея этих данных.
- Реальный лаг конверсии. Распределение времени от клика до сделки показывает, какое окно конверсии честно отражает ваш цикл продаж. Если 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: что ИИ-агент реально меняет в аккаунте и где ему нужны рамки