Данные лежали в трёх местах и не сходились
Специалист по контексту заходил в кабинет Яндекс.Директа каждого клиента отдельно. Потом отдельно в Метрику. Выгружал отчёты, сводил их в Excel и глазами искал, где сливается бюджет. Каждый день, по всем клиентам.
Расход жил в Директе. Заявки и выручка в CRM. Позиции в поиске в третьем месте. Вопрос «сколько мы потратили на этот ключ и сколько он принёс денег» ответа не имел в принципе: связать три источника было нечем.
Была и вторая проблема. На старте во всём сервисе работал один общий аккаунт администратора, под которым сидели все сотрудники. Разграничения доступа не было. Показать клиенту его собственные данные без риска было нельзя.
Ночь собирает, день читает
Сервис сам ходит в кабинеты клиентов. Каждую ночь с 01:00 до 05:00 UTC отрабатывают 12 задач по расписанию: они заказывают у Яндекса офлайн-отчёты, дожидаются готовности и раскладывают цифры по своим таблицам. Расход по кампаниям, группам, ключам и объявлениям. Срезы по устройствам, гео и площадкам РСЯ. Позиции в поиске.
Днём интерфейс уже ничего не спрашивает у Яндекса. Он читает готовые числа из своей базы. Поэтому экраны открываются сразу, а смена цели-конверсии или периода работает как обычный фильтр, без пересинхронизации.
Иерархия простая: Клиент → Проект (рекламный кабинет) → Сайт (домен и счётчик Метрики). Один кабинет Директа может обслуживать несколько сайтов с разными счётчиками и разными CRM.
- Расход, клики и конверсии из Директа по всем уровням, вплоть до ключа
- Визиты и цели из Яндекс.Метрики
- Позиции из Яндекс.Вебмастера и Google Search Console с автоподбором ключей
- Частотность из Wordstat и скорость сайта через Google PageSpeed
- Присутствие сайта в ответах нейросетей: ChatGPT, YandexGPT, Яндекс Нейро
- Заявки, сделки и выручка из CRM, звонки из коллтрекинга
Классика, которую мы поймали на себе. Главной целью стоит ручная «Отправка формы». Заявки при этом ловит «Автоцель: отправка формы», помеченная по умолчанию как микро-цель. На одном проекте по главной цели вышла 1 конверсия за 30 дней. По всем целям за тот же период 73. Из-за этого мы сами один раз сделали вывод «Директ не конвертит». Теперь селектор цели вынесен наверх как режим просмотра, добавлен режим «Все значимые», а рядом с каждой целью написано, сколько заявок она собрала за 30 дней и сколько из них пришло с рекламы. Проверить это в своём кабинете можно за пять минут.
Экран «Сквозная»: одна строка на источник
Главный экран это таблица «Все источники трафика». В одной строке стоят визиты из Метрики, расход и клики из Директа, заявки, сделки, выручка и ROMI из CRM. Никаких сопоставлений вручную.
Реклама раскрывается вглубь: кампания, группа, объявление, конкретный ключ. Клик по числу заявок открывает реальные лиды этого узла с карточкой и записью разговора. Видно, какой ключ принёс деньги, а какой только потратил.
Звонки заводятся через единую абстракцию под четыре источника: UIS, Mango, ЛПТрекер и «Из CRM База». Звонок привязывается к лиду по телефону и получает рекламную атрибуцию. Рядом работает SEO- и GEO-модуль: аудит сайта собственным краулером и ежедневный съём позиций, до 500 автоматически заведённых ключей на проект с отсечкой мусора.
Действия из интерфейса и откат в одну кнопку
Из сервиса делается то, ради чего раньше открывали кабинет. Минус-слова, в том числе разом по всему аккаунту. Пауза кампаний и ключей. Блокировка мусорных площадок РСЯ. Автобидер по VCG-аукциону.
Всё проходит через один слой. Перед записью читается текущее состояние, оно сохраняется в журнал, и только потом делается изменение. Поэтому откат работает одной кнопкой, а не разбором «что мы вчера нажали».
Границы мы держим честными. Бюджеты и стратегии через API не меняются, сервис пишет «внесите вручную». Автобидер работает только на ручных кампаниях. Автоприменение разрешено единственному типу действий, минус-словам, ограничено 30 действиями в сутки на проект и по умолчанию выключено.
Доступы переделали полностью: суперадмин, менеджер только по назначенным проектам, клиент строго на просмотр. Запрет на изменение стоит на уровне middleware всего API. Публичной регистрации нет, ключи интеграций шифруются AES-GCM в базе.
Грабли, за которые заплатили
Самая дорогая ошибка это пауза, которой не было. Кампании и ключи ставились на паузу через campaigns.update со Status=SUSPENDED. В API Директа Status означает статус модерации. Включено или выключено живёт в отдельном поле State и меняется методами suspend и resume. Интерфейс честно показывал «пауза», кампания в Директе продолжала крутиться и тратить бюджет клиента. Нашли, когда сверили реальное состояние аккаунта read-only запросом.
Вторая денежная: кнопка «добавить минус-слова» стирала минус-слова клиента. campaigns.update с NegativeKeywords заменяет список целиком. Одно новое слово сносило все фразы, которые специалист набирал месяцами. Переделали на схему «прочитать текущие, объединить без дублей, записать полный набор». Предупреждения по обоим случаям вбиты прямо в код.
- Конверсии всегда показывались нулём. При параметре Goals Директ отдаёт их в колонках вида Conversions_<id цели>_<модель атрибуции>. Голая колонка Conversions в этом режиме пустая.
- Почасового среза в Reports API Директа не существует. Поле HourOfDay даёт ошибку 8000, дейпартинг пришлось переделать через Метрику. Правило: сначала read-only проба реального вызова, потом обещание фичи.
- Офлайн-отчёты асинхронные. Часть данных приезжала со второго клика, а синхронизация рапортовала «Успешно». Добавили ретраи и честный статус «Частично» с причинами.
- Правки из браузера молча терялись: VPN и корпоративные прокси режут метод PATCH. Галочка «сохранено» показывалась, данные не менялись. Все эндпоинты обновления получили POST, это стало правилом команды.
- В ЛПТрекере звонки лежат в поле lead.calls[]. Поле lead.calls_records[] в ответе есть и всегда пустое. Чтобы это увидеть, разобрали 100 боевых вебхуков за три дня.
- Счётчик Метрики нельзя подбирать автоматом. Матч по домену привязал к новому клиенту чужой счётчик, их в кабинете было девять. Теперь счётчик выбирает человек.
- Собственный SEO-аудит врал, пока не проверили его на живом сайте. Разметка внутри @graph не разбиралась, пустой alt считался ошибкой. После фикса оценка того же сайта выросла с 90 до 99.
Как это устроено и где сейчас
Внутри два приложения: бэкенд на Python и FastAPI со своей базой PostgreSQL 16 и фронтенд на Next.js 14. Тяжёлая работа вынесена в фоновые задачи на Celery и Redis. Сейчас это 186 эндпоинтов API, 30 таблиц, 48 миграций, 35 экранов интерфейса и около 35 600 строк кода.
Активная разработка заняла примерно два месяца и 396 коммитов. 14 июля проект передан внутреннему разработчику по документу передачи, дальше ведёт он. Выкатка автоматическая: push в main, сборка образов в GitHub Actions, сервер сам забирает свежие образы по таймеру.
Одно уточнение, чтобы не вводить в заблуждение. Сервис сделан под работу нашего агентства. Наружу мы его не продаём, публичной регистрации в нём нет. Показываем его как пример того, что решается кодом вместо ручного свода таблиц.
Частые вопросы
Почему в Яндекс Директе ноль конверсий, а заявки идут?
Чаще всего дело в выбранной цели. Главной стоит ручная «Отправка формы», а заявки собирает «Автоцель: отправка формы», помеченная по умолчанию как микро-цель. На одном проекте по главной цели за 30 дней вышла 1 конверсия, по всем целям за тот же период 73. Проверить это в своём кабинете можно за пять минут.
Как свести расход на рекламу и выручку в одном отчёте?
На экране «Сквозная» одна строка на источник трафика: визиты из Метрики, расход и клики из Директа, заявки, сделки, выручка и ROMI из CRM. Реклама раскрывается вглубь до кампании, группы, объявления и конкретного ключа. Клик по числу заявок открывает реальные лиды этого узла с карточкой и записью разговора.
Какие источники данных подключаются и как часто они обновляются?
На автосборе 12 внешних сервисов: Директ, Метрика, Вебмастер, Google Search Console, Wordstat, PageSpeed, CRM и коллтрекинг. Каждую ночь с 01:00 до 05:00 UTC отрабатывают 12 задач по расписанию и раскладывают цифры по таблицам. Днём интерфейс читает готовые числа из своей базы, поэтому смена цели или периода работает как обычный фильтр.
Можно ли управлять рекламой прямо из сервиса: минус-слова, пауза, ставки?
Да. Из интерфейса добавляются минус-слова, в том числе разом по всему аккаунту, ставится пауза кампаний и ключей, блокируются мусорные площадки РСЯ, работает автобидер по VCG-аукциону. Перед каждой записью текущее состояние сохраняется в журнал, поэтому откат делается одной кнопкой. Бюджеты и стратегии через API не меняются, автоприменение разрешено только минус-словам, ограничено 30 действиями в сутки на проект и по умолчанию выключено.
Можно ли купить L7 Оптимизатор или получить в нём доступ?
Сервис сделан под работу нашего агентства, наружу мы его не продаём, публичной регистрации в нём нет. Он живёт на optimizator.l7agency.ru: 35 экранов интерфейса, 186 эндпоинтов API, 30 таблиц, около 35 600 строк кода. Показываем его на обезличенных данных и разбираем, что реально собрать в вашем случае.
