GAQL в 2026: запросы к Google Ads API без кривых цифр
Вы выгрузили расходы за месяц через Google Ads API, свели их в отчёт — и сумма не сошлась с кабинетом. На 4%, на 11%, иногда вдвое. Баг ищут в коде, в токенах, в BigQuery. Почти всегда он в одной строке: запросы к Google Ads API выглядят как обычный SQL, но ведут себя иначе, и ломается всё в одном месте — в SELECT попал лишний сегмент, или фильтр по дате отрезал не то окно, или метрику уровня кампании сложили со строками уровня ключевого слова.
GAQL (Google Ads Query Language) выглядит как SQL и ведёт себя иначе. Этот текст — разбор того, как язык устроен на самом деле: какие сущности можно запрашивать вместе, почему строки размножаются, где теряются кампании без показов и как за десять минут сверить выгрузку с интерфейсом. Если вы пишете отчётность на API или принимаете её от разработчика — после статьи вы сможете читать чужой запрос и видеть, где он врёт.
GAQL — это не SQL, и в этом вся проблема
Синтаксис знакомый: SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT .... Дальше сходство заканчивается, и каждое отличие — источник ошибки в отчёте.
- Нет JOIN. В
FROMуказывается ровно один ресурс. Связанные сущности доступны не потому, что вы их присоединили, а потому что Google заранее решил, какие атрибуты «видны» с этого ресурса. Изad_groupможно взятьcampaign.name, но нельзя произвольно подтянуть данные другого аккаунта или несвязанный объект. - Нет агрегатных функций.
SUM(),COUNT(),GROUP BYв GAQL отсутствуют. Агрегацию выполняет сервер: уровень детализации задаётся ресурсом вFROMи набором сегментов вSELECT. Складывать строки вы будете уже у себя. - Нет подзапросов и вычисляемых выражений. Нельзя написать
metrics.cost_micros / 1000000 AS cost. Деление, конвертация, производные метрики — всё на вашей стороне. - Запятые в SELECT имеют цену. Каждый добавленный сегмент меняет количество строк в ответе, а значит и смысл всех метрик в этих строках.
Практический вывод: GAQL — не язык аналитики, а язык выборки, и запросы к Google Ads API нужно проектировать под эту логику. Аналитика начинается после того, как данные легли в вашу витрину. Как эту витрину строить, мы подробно разобрали в материале про выгрузку Google Ads в BigQuery — там как раз показано, какие таблицы складывать и как не дублировать строки при ежедневной догрузке.
Анатомия запроса: четыре типа полей
Любое поле в GAQL относится к одной из четырёх групп, и от группы зависит поведение строки.
| Группа | Префикс | Что это | Влияет на число строк |
|---|---|---|---|
| Ресурс | campaign., ad_group. |
Сам объект и его атрибуты: имя, статус, тип | Да — определяет уровень |
| Атрибуты связанных ресурсов | campaign.name из ad_group |
Поля «родителя», доступные с текущего ресурса | Нет |
| Сегменты | segments. |
Разрезы: дата, устройство, сеть, тип клика | Да — размножают строки |
| Метрики | metrics. |
Показы, клики, расход, конверсии | Нет, но пересчитываются под разрез |
Минимальный честный запрос расхода по кампаниям за период выглядит так:
SELECT
campaign.id,
campaign.name,
campaign.status,
metrics.impressions,
metrics.clicks,
metrics.cost_micros,
metrics.conversions
FROM campaign
WHERE segments.date BETWEEN '2026-10-01' AND '2026-10-20'
AND campaign.status != 'REMOVED'
ORDER BY metrics.cost_micros DESC
Обратите внимание: segments.date стоит в WHERE, но не в SELECT. Это и есть разница между «расход по кампании за период» и «расход по кампании по дням». Первый запрос вернёт одну строку на кампанию, второй — двадцать.
Шесть ошибок, из-за которых цифры расходятся с кабинетом
1. Сегмент в SELECT размножил строки
Самая частая. Разработчик добавил segments.device «чтобы было», и теперь на каждую кампанию приходится до четырёх строк — MOBILE, DESKTOP, TABLET, CONNECTED_TV. Если потом взять первую строку как «данные кампании», расход упадёт в несколько раз. Если сложить metrics.ctr по строкам — получится бессмыслица.
Правило: сегмент в SELECT = ещё одно измерение таблицы. Сегменты, которые чаще всего ломают сведение: segments.date, segments.device, segments.ad_network_type, segments.conversion_action, segments.click_type. Какие разрезы и когда действительно меняют выводы, разобрано в статье про сегменты в отчётах Google Ads — логика там та же, что и в API, просто в интерфейсе её прячет кнопка «Сегменты».
2. Кампании без показов исчезли
Если в SELECT есть хотя бы одна метрика, API возвращает только строки, по которым за период была статистика. Кампания на паузе, группа без показов, ключ, который не сработал — их в ответе не будет. Отчёт «все кампании аккаунта» так не построить.
Решение — два запроса. Первый без метрик, чисто за структурой (SELECT campaign.id, campaign.name, campaign.status FROM campaign), второй со статистикой. Соединяете по campaign.id на своей стороне, пустые заполняете нулями. Иначе каждый отчёт будет тихо прятать всё, что не откручивалось, — а именно там обычно и живут проблемы.
3. Даты: часовой пояс аккаунта и «недозревшие» конверсии
Все даты в GAQL — в часовом поясе рекламного аккаунта, а не вашего сервера и не UTC. Если аккаунт в America/New_York, а скрипт запускается по Москве, граница суток уедет, и ежедневная выгрузка будет систематически недобирать или перебирать вечерний трафик.
Второй слой — конверсии. Константы вроде DURING LAST_30_DAYS считают окно до вчера включительно, сегодняшний день в них не входит. А метрика metrics.conversions атрибутируется к дате клика, а не к дате конверсии: вчерашняя строка будет дозаполняться ещё неделями по мере того, как пользователи возвращаются. Выгрузка «за вчера», снятая один раз и больше не обновляемая, занижает конверсии по определению. Это ровно та же механика, из-за которой расходятся кабинет и аналитика, — подробный разбор в материале про расхождение конверсий Google Ads и GA4.
Рабочая практика: перезаписывать последние 14–30 дней при каждой выгрузке, а не только вчерашний день. Окно выбирайте по своему реальному лагу конверсий, а не «на глаз».
4. Конверсии посчитаны дважды
metrics.conversions — только действия, отмеченные как основные для оптимизации. metrics.all_conversions — все, включая вторичные. Это разные числа, и в кабинете они лежат в разных столбцах. Половина «расхождений» объясняется тем, что в отчёте взяли одну метрику, а в интерфейсе смотрят на другую.
Отдельная ловушка — segments.conversion_action. Этот сегмент разбивает строку по типам конверсий, и если вы потом просуммируете metrics.conversions вместе с ещё одним сегментом, часть действий посчитается повторно. Если нужен разрез по действиям — делайте его отдельным запросом и не смешивайте с общим отчётом по расходу.
5. Метрики разных уровней сложили в одну таблицу
Расход с ресурса campaign, расход с keyword_view и расход с search_term_view не обязаны совпадать. Поисковые запросы не покрывают трафик PMax и Demand Gen; ключевые слова не покрывают динамические форматы; часть показов не атрибутируется на уровень ключа вовсе. Складывать такие выгрузки в одну таблицу «расходов» — гарантированная ошибка.
И отдельно: средние метрики не складываются. metrics.ctr, metrics.average_cpc, metrics.cost_per_conversion нужно пересчитывать из сырых компонентов (клики / показы, расход / клики), а не усреднять по строкам.
6. Микроединицы и валюта
Все денежные поля приходят в микроединицах: metrics.cost_micros, campaign_budget.amount_micros, ставки. Делить на 1 000 000. Валюта — та, что задана в аккаунте, и API её не конвертирует: мультиаккаунтный отчёт без приведения к одной валюте показывает сумму, которой не существует. Валюту отдаёт customer.currency_code, часовой пояс — customer.time_zone. И то и другое меняется только при пересоздании аккаунта, о чём мы писали в разборе лимитов аккаунта Google Ads.
Ресурсы, которые закрывают 90% отчётности
| Ресурс | Что отдаёт | Нюанс |
|---|---|---|
customer |
Аккаунт: валюта, часовой пояс, статус | Первое, что нужно запросить перед любой выгрузкой |
campaign |
Кампании и их статистика | Базовый уровень большинства отчётов |
campaign_budget |
Бюджеты, в том числе общие | Суммы в микроединицах |
ad_group / ad_group_ad |
Группы и объявления, статусы модерации | Статус policy-проверки лежит здесь |
ad_group_criterion |
Ключи, аудитории, плейсменты, минус-слова | Разные типы критериев в одном ресурсе |
keyword_view |
Статистика по ключевым словам | Только там, где есть ключи |
search_term_view |
Реальные поисковые запросы | Покрывает не весь трафик; агрегируется с задержкой |
asset_group / asset_group_asset |
Структура Performance Max | Отчётность PMax собирается из нескольких ресурсов |
geographic_view / user_location_view |
География: «физическое присутствие» и «интерес» | Это разные ресурсы, не путать |
change_event |
История изменений в аккаунте | Глубина ограничена; требует фильтра по дате |
click_view |
Отдельные клики с GCLID | Только один день за запрос и ограниченная глубина — не для исторических выгрузок |
Ресурс change_event заслуживает отдельного упоминания: это та же история изменений, по которой диагностируют просадки, только машиночитаемая. Собрав её в свою базу, вы получаете алерт «кто и что поменял за час до падения CPA» вместо ручного копания. Сам процесс диагностики разобран в статье про историю изменений Google Ads.
Фильтры, которые экономят квоту и нервы
GAQL поддерживает =, !=, >, <, IN, NOT IN, LIKE, CONTAINS ANY, REGEXP_MATCH, BETWEEN и константы DURING (TODAY, YESTERDAY, LAST_7_DAYS, LAST_30_DAYS, THIS_MONTH, LAST_MONTH).
Несколько практичных приёмов:
- Фильтруйте на сервере, а не в коде. Выгрузить всё и отфильтровать у себя — значит потратить квоту и время на строки, которые выбросите.
- Для view-ресурсов дата обязательна. Многие «отчётные» ресурсы просто не отдадут данные без ограничения по
segments.date. - Исключайте удалённое.
campaign.status != 'REMOVED'и аналогично для групп — иначе в отчёт попадут годы мусора. - Используйте
searchStreamдля больших выгрузок. Потоковый метод отдаёт результат без постраничной навигации и заметно быстрее на объёмах; обычныйsearchудобнее для маленьких точечных запросов. - Помните про
PARAMETERS.include_draftsподтягивает черновики и эксперименты,omit_unselected_resource_namesубирает служебные поля из ответа и уменьшает объём.
Если запрос отдаёт сотни тысяч строк — почти наверняка в SELECT лишний сегмент, а не в аккаунте слишком много сущностей. Уберите сегменты по одному и смотрите, на каком число строк падает на порядок.
Как проверить запросы к Google Ads API до продакшена
Три инструмента, которые экономят часы отладки:
- Query Builder и валидатор в документации Google Ads API. Собирают запрос мышкой и сразу подсвечивают несовместимые поля — а совместимость в GAQL нетривиальна: не каждый сегмент разрешён с каждым ресурсом.
- Метаданные через сам API. Запрос к
google_ads_fieldотдаёт по каждому полю, с какими ресурсами и сегментами оно совместимо и является ли оно выбираемым, фильтруемым и сортируемым. Это надёжнее, чем угадывать по документации. - Сверка на одном дне. Прежде чем запускать выгрузку за год, выгрузите один день по одной кампании и сравните четыре числа с интерфейсом: показы, клики, расход, конверсии. Сошлось — масштабируйте.
Процедура сверки за десять минут
- Возьмите один аккаунт, один день, одну кампанию со стабильным трафиком.
- В кабинете выставьте тот же день и снимите четыре метрики без сегментов.
- Выполните запрос без единого сегмента в
SELECT, только с фильтром по дате. - Сравните показы и клики. Расходятся — проблема в дате или часовом поясе.
- Сравните расход. Расходится при совпавших кликах — проблема в микроединицах или валюте.
- Сравните конверсии. Расходятся — смотрите
conversionsпротивall_conversionsи лаг конверсий. - Только после этого добавляйте сегменты — по одному, сверяя сумму с общей строкой на каждом шаге.
Три запроса к Google Ads API, которые стоит держать под рукой
Ниже — заготовки, которые закрывают три регулярных задачи. Подставьте свои даты и пороги.
Поисковые запросы, на которые ушли деньги без конверсий
SELECT
campaign.name,
ad_group.name,
search_term_view.search_term,
segments.keyword.info.match_type,
metrics.impressions,
metrics.clicks,
metrics.cost_micros,
metrics.conversions
FROM search_term_view
WHERE segments.date DURING LAST_30_DAYS
AND metrics.clicks > 5
AND metrics.conversions = 0
ORDER BY metrics.cost_micros DESC
LIMIT 500
Это основа автоматизированной работы с минус-словами: вместо ежедневного ручного просмотра отчёта вы получаете короткий список кандидатов, отсортированный по потраченным деньгам. Порог по кликам обязателен — без него в выборку попадут тысячи запросов с одним кликом, по которым статистически ничего сказать нельзя. Сама методика отбора разобрана в материале про минус-слова и анализ поисковых запросов.
Кампании, упирающиеся в бюджет
SELECT
campaign.id,
campaign.name,
campaign_budget.amount_micros,
campaign.primary_status,
campaign.primary_status_reasons,
metrics.impressions,
metrics.cost_micros
FROM campaign
WHERE segments.date DURING YESTERDAY
AND campaign.status = 'ENABLED'
ORDER BY metrics.cost_micros DESC
Поля primary_status и primary_status_reasons — недооценённая пара. Именно они объясняют, почему кампания включена, но не откручивается: ограничение бюджетом, отсутствие активных объявлений, проблемы с оплатой, отклонённые ассеты. Это готовая основа для ежедневного алерта «что сегодня не работает».
Изменения в аккаунте за последние сутки
SELECT
change_event.change_date_time,
change_event.user_email,
change_event.change_resource_type,
change_event.resource_change_operation,
change_event.changed_fields,
campaign.name
FROM change_event
WHERE change_event.change_date_time DURING LAST_14_DAYS
ORDER BY change_event.change_date_time DESC
LIMIT 1000
Обратите внимание: этот ресурс фильтруется по собственному полю времени, а не по segments.date, и требует явного LIMIT. Поле user_email отвечает на главный вопрос при разборе просадки — кто внёс правку. В аккаунтах с несколькими подрядчиками это первая вещь, которую стоит автоматизировать.
И про обход аккаунтов в MCC
Отдельного «запроса по всему MCC» не существует: каждый запрос выполняется в контексте одного клиентского аккаунта. Схема всегда одна — сначала получить список подчинённых аккаунтов, затем пройти по ним циклом, аккуратно обрабатывая ошибки на уровне отдельного аккаунта. Один заблокированный или закрытый аккаунт не должен ронять всю ночную выгрузку: ошибка логируется, цикл идёт дальше. И не забывайте приводить суммы к одной валюте перед агрегацией — иначе сводный отчёт по группе аккаунтов покажет число, не имеющее физического смысла.
Версии API: почему рабочий запрос перестаёт работать
GAQL привязан к версии Google Ads API, а версии выходят регулярно и так же регулярно отключаются. Поля переименовывают, ресурсы объединяют, отдельные сегменты исчезают вместе с устаревшими типами кампаний. Запрос, написанный год назад, может молча отдавать не то или падать с ошибкой несуществующего поля.
Минимальная гигиена: фиксируйте версию API в коде явно, держите список запросов в репозитории, а не в головах, и проверяйте release notes при каждом обновлении. Подробный календарь релизов и сансетов — в материале про версии Google Ads API. Если вы только подступаетесь к интеграции, начните с получения доступа к Google Ads API: developer token, проект в Cloud Console и уровни доступа — отдельный квест до первой строчки GAQL.
Когда GAQL избыточен
Честный ответ: чаще, чем кажется. Если задача — «раз в неделю прислать расход по кампаниям на почту», это делается встроенными средствами кабинета без единой строки кода. Граница между «хватит интерфейса» и «пора в API» разобрана в статье про Google Ads API вместо скриптов.
API оправдан там, где нужны история без ограничений интерфейса, сведение нескольких аккаунтов в одну витрину, свои производные метрики и алерты на собственной логике. Для всего остального сначала посмотрите на кастомные столбцы и редактор отчётов — они закрывают больше, чем принято думать.
И последнее. Хорошая отчётность не делает рекламу прибыльной — она лишь показывает правду быстрее. Сама экономика кампаний собирается на уровне структуры, ставок и качества трафика. Если вы разбираетесь с Google Ads системно, посмотрите гайд по Google Ads и формат обучения работе с Google Ads от команды PPC Rebels — там разобрана практическая часть, которой в документации API принципиально нет.
Два соседних материала этой недели: изменения Google Ads за октябрь 2026 — там часть правок напрямую меняет метрики, которые вы выгружаете, и поддержка Google Ads и аккаунт-менеджер — на случай, если ошибка всё-таки не на вашей стороне.
FAQ
Чем GAQL отличается от обычного SQL?
Нет JOIN, нет агрегатных функций, нет подзапросов и вычисляемых выражений. В FROM указывается ровно один ресурс, а уровень агрегации задаётся этим ресурсом и набором сегментов в SELECT. Складывать и считать производные метрики нужно уже на своей стороне.
Почему сумма расхода из API не совпадает с кабинетом?
Три главные причины: в SELECT попал сегмент, который размножил строки; период посчитан в другом часовом поясе; расход не поделён на 1 000 000, потому что приходит в микроединицах. Проверяйте в этом порядке.
Куда делись кампании, у которых нет показов?
Если в SELECT есть хотя бы одна метрика, строки без статистики не возвращаются. Структуру нужно запрашивать отдельным запросом без метрик и соединять с данными на своей стороне.
В чём разница между metrics.conversions и metrics.all_conversions?
Первая считает только действия, помеченные как основные для оптимизации, вторая — все отслеживаемые, включая вторичные. В интерфейсе это разные столбцы, и сравнивать нужно одноимённые.
Почему вчерашние конверсии растут задним числом?
Конверсии атрибутируются к дате клика. Пользователь кликнул вчера, а купил через неделю — цифра за вчера изменится. Поэтому при каждой выгрузке последние две-четыре недели нужно перезаписывать, а не догружать только новый день.
Можно ли одним запросом выгрузить данные по всем аккаунтам MCC?
Нет. Запрос выполняется в контексте конкретного клиентского аккаунта. Список подчинённых аккаунтов берётся отдельно, дальше цикл по ним. И не забудьте привести валюты к одной, если аккаунты в разных.
Как узнать, какие поля совместимы между собой?
Через метаданные самого API: запрос к google_ads_field отдаёт для каждого поля списки совместимых ресурсов и сегментов и признаки «выбираемое / фильтруемое / сортируемое». Это точнее, чем перебирать варианты руками.
Что использовать — search или searchStream?
searchStream для больших выгрузок: отдаёт поток без постраничной навигации и работает быстрее на объёмах. Обычный search удобнее для небольших точечных запросов, где нужен понятный постраничный ответ.
Можно ли через GAQL что-то изменить в аккаунте?
Нет. GAQL — язык чтения. Изменения выполняются отдельными mutate-операциями соответствующих сервисов API.
Почему запрос к click_view возвращает ошибку?
Этот ресурс требует фильтра ровно на один день и имеет ограниченную глубину хранения. Для исторических выгрузок он не предназначен — собирать GCLID нужно ежедневно и складывать в свою базу.
Нужен ли GAQL, если есть встроенная отчётность и скрипты?
Для большинства задач — нет. API оправдан, когда нужна длинная история, сведение многих аккаунтов, свои производные метрики или алерты на собственной логике. Для регулярных отчётов по одному аккаунту возможностей кабинета обычно достаточно.
Как часто придётся переписывать запросы?
Привязывайтесь к конкретной версии API и проверяйте release notes при каждом обновлении: поля переименовываются, ресурсы объединяются, отдельные сегменты исчезают вместе с устаревшими типами кампаний. Запросы стоит держать в репозитории с версионированием, а не в разрозненных скриптах.