Как я пользуюсь API Яндекс Директа в агентстве

14.10.2026 · 8 мин чтения
Тёмный ключ, три карточки и лист отчёта с коралловой кромкой на чёрном фоне
В агентстве я больше не снимаю статистику Директа по одному кабинету. Для отчётов держу один контур: свой OAuth-токен, список клиентов через AgencyClients и заголовок Client-Login, а дальше сервис Reports. Здесь показываю, как получаю доступ, как обхожу логины и что проверяю в файле, прежде чем отдать выгрузку клиенту.

Зачем мне API, если кабинет уже открыт

API Яндекс Директа для меня не вторая кнопка кабинета. В документации версия называется v5, в адресах сервисов я вижу v501. Запрос отправляю методом HTTPS POST, в теле передаю JSON. Тот же интерфейс умеет SOAP и XML. Вход идёт по протоколу OAuth 2.0. Отдельной платы за вызовы нет: в обзоре интерфейс указан как бесплатный.
В веб-кабинете выгрузка отчёта — отдельный клик и только внутри одного логина. Когда рекламодателей несколько, одно и то же занимает утро. Запросом я повторяю операцию по шаблону. В справке Яндекс пишет, что API полезен в первую очередь агентствам и крупным заказчикам со своей отчётностью.

Как я получаю OAuth и токен

Доступ я собираю в одном порядке и не меняю шаги местами. Сначала приложение в Яндекс ID. Потом заявка внутри Директа. Потом свой токен у каждого сотрудника, который реально ходит в API. Общий пароль на команду я не завожу: токен привязан к человеку и к его правам в кабинете.
  1. Создаю приложение в кабинете разработчика и включаю право direct:api. Без этого scope запрос к Директу не проходит.
  2. В веб-интерфейсе завожу хотя бы одну кампанию. Страница «Настройки API» открывается только после неё, и при первом входе нужно принять пользовательское соглашение.
  3. В заявке указываю ClientID приложения. В документации рассмотрение занимает до 7 рабочих дней, по будням с 10:00 до 19:00 по Москве.
  4. Если клиент сидит на паспортной организации, с 2 июня 2025 в том же приложении нужен ещё доступ passport:business.
  5. После одобрения забираю OAuth-токен обычным потоком: редирект на oauth.yandex.ru и обмен кода. Логин и пароль кабинета я при этом никому не отдаю.
Токен принадлежит представителю агентства, а не клиенту. У руководителя, директолога и автора интеграции токены разные. Если доступ отозвали или срок вышел, обновляю его по правилам Yandex OAuth. Чужой токен в скрипт и в переписку не кладу.
Коралловый ключ и тёмная капсула на чёрном подиуме
Токен сотрудника и секрет приложения я храню отдельно друг от друга.

Чем токен отличается от секрета приложения

ClientID и Client Secret говорят только о том, какое приложение обращается к Яндексу. Это не пароль от кабинета и не подпись запроса к данным рекламодателя. В заголовок Authorization я ставлю схему Bearer и OAuth-токен сотрудника. Секрет приложения в этот заголовок не входит. Его же не пишу в таблицу, в чат и в промпт к модели.
  • Токен и секрет лежат в переменных окружения на сервере или в секрет-менеджере.
  • Для отчётов держу контур только на чтение. Запись в кампании идёт отдельно и только после ручной проверки черновика.
  • Подрядчику выдаю OAuth-доступ, а не пароль от кабинета.
  • Первый прогон делаю в песочнице на хосте api-sandbox.direct.yandex.com. Веб-интерфейса там нет, данные хранятся около месяца. Можно выбрать роль агентства: три тестовых клиента и по три кампании у каждого.

Список клиентов я читаю без Client-Login

Список рекламодателей забираю сервисом AgencyClients. В актуальной версии путь выглядит как json/v501/agencyclients. В документации у сервиса три метода: add, update и get. Этот вызов идёт без заголовка Client-Login. Я спрашиваю данные агентства, а не один кабинет. Если заголовок всё-таки поставить, легко получить ответ не про тот контур и решить, что клиентов нет.
С 16 июня 2025 метод AgencyClients.add больше не поддерживается. Нового клиента я регистрирую через addPassportOrganization: доступ открывают по заявке агентствам с большим числом клиентов. С 19 июня 2025 метод addPassportOrganizationMember добавляет сотрудника в паспортную организацию. Старый add в шаблоне онбординга молча перестаёт пополнять список.
Из ответа get мне нужен логин рекламодателя. Длинный список листаю через LimitedBy и Offset. Логин в адрес сервиса не подставляю: он пойдёт отдельным заголовком, когда я запрошу кампании или отчёт.

Куда я ставлю заголовок Client-Login

Кампании и отчёт клиента я запрашиваю уже с Client-Login: в заголовке логин рекламодателя. Токен остаётся токеном сотрудника агентства. Если их перепутать, запрос смотрит не в тот кабинет или не смотрит ни в один, хотя код ответа может быть спокойным.
ЗаголовокЧто я в него кладу
AuthorizationСхема Bearer и OAuth-токен представителя агентства
Client-LoginЛогин рекламодателя, а не логин агентства
Use-Operator-UnitsЗначение true, если баллы нужно списать с агентства
processingModeДля Reports: online, offline или auto
Баллы в документации называются Units. Это суточный лимит, свой у кабинета и завязанный на показы, клики и расход. Пересчёт раз в сутки. Каждый час приходит одна двадцать четвёртая часть лимита плюс остаток за двадцать три часа. Запрос с Client-Login по умолчанию тратит баллы клиента. Use-Operator-Units со значением true списывает их с агентства. Так я делаю, если у нового клиента лимит ещё не накопился. Остаток смотрю в заголовке ответа Units.
  • Ошибка вызова метода и ошибка операции с объектом стоят по двадцать баллов. Кривой логин ловлю на одном клиенте.
  • От одного рекламодателя нельзя слать больше пяти параллельных запросов.
  • В очереди у пользователя не больше пяти офлайн-отчётов. Это поле reportsInQueue.
  • В песочнице на один отчёт приходится не больше одной кампании.

Как я забираю отчёт через Reports

Reports — асинхронный сервис: файл редко приходит в том же ответе. В processingMode значение auto оставляет выбор сервису, online просит собрать сразу, offline ставит отчёт в очередь. Сводку по нескольким логинам я собираю в offline и оставляю тяжёлую выгрузку на ночь.
КодЧто это значит для файла
200Отчёт готов, тело можно забирать
201Запрос в очереди, файла ещё нет
202Отчёт формируется, жду повтор
400Ошибка запроса или упёрлись в лимит очереди
Коралловый узел выпускает тёмные листы в лоток на чёрном фоне
Пока код не стал 200, в таблицу клиента я ничего не кладу.
Если пришли 201 или 202, следующий запрос делаю не сразу, а через паузу retryIn из ответа. Пока код не стал 200, строки в сводку не пишу. Боевой адрес держу в конфиге как api.direct.yandex.com/json/v501 и дальше имя сервиса. Для статистики это Reports. Тело, как и у остальных сервисов, содержит method и params. Обход у меня один: AgencyClients.get, затем для каждого логина отдельный Reports с заголовком Client-Login.
Колонки держу в одном шаблоне, иначе сводка разъезжается. Если нужны конверсии, в FieldNames одновременно указываю Goals и Conversions. Одна колонка без второй даёт успешный ответ без нужных чисел. Для денег ставлю returnMoneyInMicros в false, иначе суммы приходят в микроединицах. Период и тип отчёта тоже беру из шаблона.

Что я проверяю в выгрузке

Код 200 значит только, что интерфейс не отклонил операцию. Пустой список ошибок говорит о том же. Перед отправкой сверяю файл с кабинетом на знакомом срезе. В письмо не попадают ни токен, ни секрет приложения, ни заголовок Authorization.
Лупа над тёмным листом с коралловыми метками на чёрном фоне
Сначала логин и период, потом цели, деньги и остаток баллов.
  1. Сверяю Client-Login запроса с логином в шапке файла и с тем клиентом, которому уйдёт письмо.
  2. Проверяю период. Вчерашний день и скользящая неделя легко путаются, если подставился не тот шаблон.
  3. Смотрю, что колонки Goals и Conversions есть обе. Если в кабинете за этот период конверсии есть, в файле они тоже должны быть.
  4. Проверяю деньги. При returnMoneyInMicros false суммы читаются обычными единицами, а не микросуммами.
  5. Если перед отчётом я менял кампании, перечитываю поле State. Иначе в сводку попадает статус, которого уже нет.
  6. Сверяю одну знакомую кампанию с кабинетом. Расхождение по кликам или расходу — повод не отправлять файл, а повторить запрос.
  7. Смотрю заголовок Units. Если остаток тонкий, следующую пачку не запускаю, пока не решу, чьи баллы она съест.
В песочнице чеклист короче: одна кампания на отчёт и тестовые клиенты, а не боевые логины. Когда сверка на тесте сошлась, тот же набор полей переношу на боевой Client-Login. Так я ловлю пустые конверсии и микросуммы до того, как файл увидит клиент.

Где этот контур обычно ломается

  • Заявка на API ещё не одобрена. Пока её нет, токен не заменяет доступ, даже если приложение в Яндекс ID уже создано.
  • В токене нет scope direct:api или для паспортной организации нет passport:business.
  • В запросе к данным клиента нет Client-Login, либо логин набран с опечаткой.
  • AgencyClients вызван с Client-Login, хотя этот сервис как раз вызывается без него.
  • Токен отозван или истёк, а скрипт продолжает слать старую схему Bearer.
  • В FieldNames нет пары Goals и Conversions, и успешный код маскирует пустые конверсии.
  • В очереди уже пять офлайн-отчётов, и новый запрос получает код 400.
Документацию держу на yandex.ru/dev/direct/doc/ru. Свой порядок короче: кампания и заявка, токен представителя, песочница, один Reports на одном Client-Login. После сверки файла включаю цикл AgencyClients.get по остальным логинам. Клиенту уходит сводка из проверенных колонок, а не из первого успешного кода.
SEO

Разберу первый отчёт по API

Посмотрю заявку, токен, Client-Login и выгрузку Reports и скажу, какие поля сверить до отправки клиенту.
SEO-специалист и автор материала
Читайте также