Привет, Хабр! На связи Владимир Михайлов, Marketing Analyst в Garage Eight. Сегодня хочу поговорить с вами о сервисах конкурентной аналитики. Почти каждая маркетинговая команда пользуется Sensor Tower, AppMagic, Similarweb и не только. Я не исключение. На одном из проектов я как раз работал с таким сервисом и спустя несколько месяцев понял простую вещь: использование аналитической платформы вовсе не означает наличие удобной аналитики.

В статье хочу обсудить, почему так происходит, и поделиться историей о том, как родился проект, в котором я превратил Sensor Tower из инструмента аналитики в источник данных.

Как обычно строились исследования

В команде, где я тогда работал, аналитику всегда собирали примерно одинаково:

Открыть Sensor Tower → Найти приложение → Выбрать страну → Выбрать период → Посмотреть креативы → Переключиться на следующего конкурента → Повторить то же самое еще 15 раз

Одно дело, когда нужно таким образом разово собрать топ креативов конкурентов по показателю Impression Share. Но анализировать рынок нужно регулярно, поэтому одна задача превращается в десятки похожих — работа становится рутиной и в итоге съедает много времени.

Кроме этого, я столкнулся еще с несколькими ограничениями:

  • Лицензий всегда меньше, чем сотрудников, и полный доступ есть только у части команды.

  • Sensor Tower хорошо показывает данные: например, может четко ответить на вопрос «Какие новые видео залетели у конкурентов на прошлой неделе?». Но если нужно разобраться в причинно‑следственных связях и понять, почему одни видео зашли аудитории, а другие — нет, ответ вы не получите.

  • AI нельзя встроить внутрь Sensor Tower. Чтобы модель могла анализировать рынок, данные сначала нужно подготовить, агрегировать и объединить с другими источниками.

Так я понял: Sensor Tower должен стать источником данных, а не инструментом, который закрывает базовые аналитические задачи.

Как решили выстроить работу

На том проекте была гипотеза: мы считали, что часть регулярного анализа конкурентов можно автоматизировать — убрать ручной поиск и фильтрацию в Sensor Tower и отдавать маркетологу уже готовую аналитику прямо в Slack. Решили сделать MVP на одном конкретном кейсе (мониторинге рекламных креативов) и проверить, насколько такой формат будет полезен. 

Мы сознательно не пытались сразу построить полноценную аналитическую платформу. Цель была гораздо скромнее: каждое утро автоматически получать данные, сохранять их в собственном хранилище, формировать аналитический отчет и публиковать его в Slack.

Получилась такая архитектура:

Sensor Tower API

        │

        ▼

Python ETL

        │

        ▼

Transformation Layer

        │

        ▼

BigQuery

        │

        ▼

Slack Bot

Так как это MVP, AI в этой схеме пока отсутствует. Вместо этого мы решили сконцентрироваться на каждом элементе архитектуры, чтобы заложить фундамент для следующих надстроек.

Почему API, а не интерфейс Sensor Tower

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

Хотелось вообще перестать открывать Sensor Tower. Практически всё, что отображается в интерфейсе, доступно через API, а значит, всю эту работу можно переложить на ETL. Так мы вытаскиваем данные из источника, преобразуем их в удобный вид и загружаем в хранилище.

Готово — Sensor Tower превращается в обычный источник данных. Но здесь важно разобрать подробнее несколько наших решений.

Архитектура ETL

Первое решение было архитектурным. Я не хотел писать один большой Python‑файл: он очень быстро превратился бы в кашу — в нём смешивались бы запросы к API, обработка данных, загрузка в хранилище и логика отчетов. Читать это через месяц было бы просто невозможно, а менять — страшно. Именно поэтому я сразу разделил проект на несколько независимых компонентов:

  • config/ хранит настройки: какие приложения загружаем, каких стран и за какой период;

  • sensor_tower_client.py отвечает только за общение с API;

  • transformer.py берет этот сырой ответ и превращает его в аккуратную таблицу;

  • bigquery_loader.py берет готовую таблицу и сохраняет ее в BigQuery;

  • main.py — точка входа, которая по очереди запускает все шаги.

Каждый модуль отвечает только за одну задачу. Это важно, потому что, если что‑то сломается, сразу понятно, где искать проблему, и, если нужно что‑то поменять, можно исправить только один модуль, не трогая остальные.

SensorTowerClient

Сам клиент оказался удивительно небольшим:

class SensorTowerClient:

    BASE_URL = "https://api.sensortower.com/v1"

    def get_creatives(...):

        response = requests.get(...)

        response.raise_for_status()

        return response.json()

То есть он обращается по адресу API, отправляет запрос с нужными параметрами, проверяет, что ответ успешный, и возвращает результат в виде JSON. Всё. Именно так это и должно выглядеть — без DataFrame, без SQL, без BigQuery. Только HTTP.

Клиент не должен знать, что будет с данными дальше. Его задача — просто получить их и отдать. За остальное отвечают другие модули. 

Какие сложности возникли

Оказалось, что вызов API — это примерно 20% всей работы, остальные 80% — инженерия вокруг него.

Лимиты API

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

Пришлось добавить:

  • retry, чтобы при неудачном запросе система автоматически отправляла его повторно;

  • exponential backoff, чтобы между попытками были паузы и с каждой новой попыткой они увеличивались — так мы даем серверу время восстановиться;

  • обработку HTTP 429, чтобы считывать специальный код ошибки, который говорит о большом количестве запросов;

  • журналирование ошибок, чтобы потом можно было посмотреть, что именно сломалось и когда.

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

Конфигурация

Сначала список приложений выглядел так:

apps = [

    "Company 1",

    "Company 2",

    "Company 3"

]

Через несколько недель компаний стало больше пятнадцати, и было очевидно, что менять Python‑код при каждом новом конкуренте — плохая идея.

Появился обычный справочник:

| Company | App ID | Country | Platform | Active |

Теперь подключение нового приложения занимает одну строку, и следующей ночью ETL сам начинает его загружать.

Transformer

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

Мы с командой проекта сделали отдельный слой трансформации:

class SensorTowerTransformer:

    def transform(self, response):

        ...

        return dataframe

Именно здесь происходят:

  • преобразование дат;

  • нормализация типов;

  • переименование полей;

  • вычисление служебных колонок;

  • подготовка данных для BigQuery.

В результате остальная система ничего не знает о внутреннем формате Sensor Tower. И опять же, если API изменится, достаточно поправить один модуль.

Почему BigQuery

После трансформации данные перестают быть данными Sensor Tower — это уже обычные строки и колонки, с которыми можно делать что угодно: например, писать собственные SQL, объединять несколько источников, хранить историю, использовать BI и AI. Так Sensor Tower становится всего лишь одним из поставщиков данных.

В итоге добавление нового конкурента теперь выглядит как добавление записи в справочник:

| Company | App ID | Country | Platform | Active |

При следующем запуске система автоматически обращается к API, получает данные, преобразует их и сохраняет в BigQuery. Без изменений кода и ручного экспорта.

В чём проблема

Когда данные начали храниться в BigQuery, нужно было превратить сырые записи в полезную аналитику. И здесь выяснилось, что построить правильный рейтинг креативов значительно сложнее, чем написать ETL.

Например, один и тот же креатив может показываться одновременно в разных соцсетях. Если просто отсортировать строки, получаются дубликаты; если использовать AVG(impression_share), рейтинг тоже оказывается неверным.

В итоге пришлось считать креатив единой сущностью (creative_id), а долю показов — суммировать по всем рекламным сетям (SUM(impression_share)). Именно такие решения отличают корректный рейтинг от простой сортировки.

Почему Slack

После того как данные и SQL были готовы, возник следующий вопрос: как донести результаты до команды? Можно, конечно, использовать Tableau или сделать внутренний веб‑портал, но это не решило бы главную проблему — аналитикам всё равно пришлось бы заходить туда самостоятельно, и не каждый бы помнил об этом. Мы решили обхитрить систему и сделать так, чтобы аналитика сама приходила специалистам в руки.

Так мы перешли к следующему этапу и начали работу над Slack‑ботом. Сначала всё выглядело просто. Я добавил несколько slash‑команд, которые неплохо работали:

/creatives-week

/creatives-month

/creatives-weekly-leaders

Но пользоваться ими было неудобно: приходилось запоминать команды и четко знать, какая из них за что отвечает. Чтобы упростить себе жизнь, мы с командой проекта создали полноценный интерфейс внутри Slack.

Интерфейс вместо команд

Я сделал меню на базе Slack Block Kit, который позволяет строить внутри мессенджера почти полноценный UI. Пользователь открывает меню и видит три действия:

📊Country TOP Creatives

[Weekly Report]

[Monthly Report]

[Weekly Leaders]

Те же кнопки появляются:

  • в личных сообщениях с ботом — для тех, кто предпочитает такой формат;

  • по команде /menu — для тех, кто привык к командам;

  • внизу каждого сформированного отчета — для тех, кто хочет сразу переключиться на другой отчет, не возвращаясь в меню.

Как настраивали Slack

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

Пришлось:

  • регистрировать slash‑команды, чтобы Slack знал, что такие команды существуют;

  • включать Interactive Components, чтобы кнопки и меню вообще работали;

  • настраивать OAuth Scopes, чтобы бот понимал, что именно он может делать в рабочем пространстве;

  • подключать Socket Mode, чтобы можно было запускать бота на внутреннем сервере, а не выкладывать веб‑приложение в интернет;

  • настраивать события приложения, чтобы было понятно, какие события Slack должен присылать боту;

  • получать необходимые разрешения (permissions) для работы в рабочих пространствах, чтобы бот в принципе мог что‑либо делать.

Но на практике наличие обработчика в коде еще ничего не гарантирует. Главное — должны совпасть три вещи:

Slack App Configuration

        │

        ▼

OAuth + Permissions

        │

        ▼

Python Listener

Если отсутствует хотя бы один уровень, функциональность работать не будет. И самое неприятное: ошибки тут не всегда очевидны, иногда просто ничего не происходит — и надо долго копаться в настройках, чтобы понять, какой именно слой отвалился.

Как избежать дублирования логики

Очень не хотелось получить три независимые реализации одного отчета, иначе при любом изменении (новой формуле, новом SQL‑запросе, дополнительном блоке) пришлось бы менять несколько мест. Легко получить ситуацию, когда /creatives‑week показывает одно, а кнопка Weekly Report — уже другое. Именно поэтому и slash‑команды, и кнопки у нас использовали один и тот же обработчик. Цепочка простая:

Slash Command / Button

        │

        ▼

Dispatcher

        │

        ▼

Report Builder

        │

        ▼

BigQuery

        │

        ▼

Slack Blocks

Так SQL существует только в Report Builder, и, если надо поменять формулу или добавить поле, я могу сделать это в одном месте — и значения применятся везде. Плюс интерфейс меняется независимо от логики аналитики: можно переделать кнопки, добавить новые, убрать старые — и это никак не затронет SQL и логику отчетов.

Однако сам по себе автоматический сбор данных из Sensor Tower решает только первую часть задачи. Пайплайн регулярно получает рекламные креативы конкурентов, сохраняет историю и позволяет строить отчеты. Но в ежедневной аналитической работе постоянно возникают вопросы, ради которых не хочется каждый раз писать SQL или собирать новый дашборд.

Например, какие новые креативы появились у конкурентов за последний месяц, какие рекламные форматы сейчас активнее всего использует компания в регионе или в каких рекламных сетях конкуренты увеличили активность?

Как Claude интегрируется в рекламную разведку

Следующим слоем системы стал разговорный аналитический интерфейс. Я подключил Claude через MCP к данным, которые уже собирает пайплайн Sensor Tower. Так Claude получает read‑only инструменты для работы с подготовленными данными:

  • поиск креативов по компании, стране, рекламной сети и периоду;

  • получение новых креативов за выбранный интервал;

  • сравнение рекламной активности нескольких конкурентов;

  • анализ используемых форматов и рекламных сетей;

  • получение истории активности конкретного креатива;

  • анализ недельного impression share;

  • поиск изменений рекламной стратегии во времени.

Принципиальный момент архитектуры: Claude не обращается к Sensor Tower API самостоятельно и не занимается сбором данных. Эту работу по‑прежнему выполняет детерминированный ETL:

Sensor Tower API → Python ETL → BigQuery

Пайплайн отвечает за API‑запросы, пагинацию, retries, дедупликацию, backfill и сохранение daily/weekly history. Claude работает уже поверх подготовленного слоя данных.

Это разделение оказалось особенно важным после перехода от обычного snapshot к исторической модели. В BigQuery сохраняются ежедневные факты обнаружения креативов, а impression_share хранится отдельно на недельном уровне для ad unit. Именно поэтому MCP может предоставить LLM уже правильно определенные аналитические сущности, вместо того чтобы заставлять модель самостоятельно интерпретировать сырой ответ API.

В результате LLM становится не частью ETL, а интерфейсом к ETL.

Аналитику больше не обязательно знать структуру таблиц, писать SQL или вручную сопоставлять выгрузки. Он задает вопрос обычным языком, MCP преобразует его в вызовы ограниченного набора read‑only инструментов, а Claude получает фактические данные и формирует бизнес‑ответ.

При необходимости накопленную аналитику и отчеты можно дополнительно синхронизировать с Google Drive и подключить к отдельному Claude Project. Тогда структурированные данные из BigQuery отвечают на вопрос «Что произошло?», а документы, исследования и предыдущие отчеты добавляют контекст для вопроса «Что это может означать для бизнеса?».

В итоге у одной системы появляется несколько способов использования:

  • Tableau → регулярная аналитика и дашборды.

  • Slack → push‑мониторинг значимых изменений.

  • Claude → pull‑аналитика и ad‑hoc вопросы на естественном языке.

Таким образом, пайплайн постепенно превращается из интеграции Sensor Tower → BigQuery в небольшой AI Competitive Advertising Intelligence Assistant. Sensor Tower остается источником данных, Python/Airflow — детерминированным слоем сбора, BigQuery — историческим хранилищем, а Claude — разговорным интерфейсом для аналитика.

Что в итоге

На проекте появилась система, в которой маркетолог вообще не взаимодействует с Sensor Tower напрямую. Каждое утро он просто открывает Slack и сразу получает готовую аналитику: изменения долей рынка, самые сильные видеокреативы, новые рекламные кампании конкурентов. Весь путь от получения данных до публикации отчета выполняется автоматически.

И немного личных наблюдений. Интересно, что самой простой частью проекта оказался вызов Sensor Tower API. Гораздо больше времени заняли задачи, вокруг которых в итоге и строится настоящий инженерный проект:

  • проектирование ETL;

  • стратегия хранения данных;

  • трансформация JSON;

  • конфигурация конкурентов;

  • подготовка данных для аналитики;

  • построение корректных SQL;

  • разработка удобного интерфейса.

Так MVP стал фундаментом для следующего этапа. Во второй части статьи я подробнее погружусь в тему AI и в деталях расскажу, как начал использовать LLM для анализа рекламных кампаний конкурентов, автоматического поиска изменений и генерации аналитических выводов вместо обычных таблиц.

А как вы собираете аналитику? Может, тоже что‑то автоматизировали? Или считаете это лишним?