Как строить интеграционный слой для Wildberries и Ozon API

Подключиться к API маркетплейса обычно несложно. Сложнее сделать так, чтобы через полгода второй кабинет, новый endpoint или изменение лимитов не заставили переписывать весь сервис.

Документация API ещё не архитектура продукта

Первую интеграцию легко написать прямо внутри бизнес-кода: запросить продажи, распарсить JSON, положить результат в таблицу. Для прототипа это нормально.

Проблема начинается, когда появляются второй маркетплейс, несколько кабинетов и десятки методов. У каждого endpoint свои параметры, пагинация, лимиты и формат ошибок. Если эта специфика протекает во все сервисные слои, любое изменение API затрагивает полпроекта.

Поэтому я отделяю marketplace-адаптер от общего ядра как можно раньше. Бизнес-логика должна работать с понятными объектами и операциями, а не знать URL конкретного метода Wildberries.

Общее ядро стоит вынести до появления третьей копии кода

В одном из проектов я собрал переиспользуемую Python-библиотеку для read-only получения и нормализации данных WB/Ozon. Общее ядро отвечает за конфигурацию, запросы, ошибки и загрузку, а marketplace-адаптеры знают особенности конкретной площадки.

Это не попытка сделать один универсальный API поверх разных продуктов. Различия маркетплейсов остаются различиями. Общими становятся механические вещи, которые не хочется заново решать в каждом сценарии.

В результате новый сервис можно строить на typed-клиентах и loader-слое вместо очередной пачки разрозненных HTTP-вызовов.

Пагинация и rate limit становятся бизнес-проблемой быстрее, чем кажется

Если пользователь нажал «обновить данные», ему всё равно, что маркетплейс отдаёт результат частями или ограничивает частоту запросов. Для продукта это одна операция.

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

Я предпочитаю явную иерархию ошибок. Тогда вызывающий код понимает разницу между временной сетевой проблемой, rate limit, неверной авторизацией и ошибкой входных данных.

Нормализация нужна не ради красивых классов

Wildberries и Ozon могут называть похожие сущности по-разному и отдавать разный набор полей. Попытка сразу свести всё к одной огромной модели обычно теряет полезные детали.

Лучше разделять внешний контракт и внутреннюю модель. Адаптер преобразует данные конкретной площадки в то представление, которое действительно нужно текущему продукту.

Так seller-кабинет или аналитика не зависят от формы сырого ответа API, а смена версии endpoint остаётся локальной задачей адаптера.

Не все данные нужно тащить в одну базу

В data-core хранение подключается опционально через ClickHouse или Redis. Это сознательный выбор: библиотека получения данных не должна требовать конкретную инфраструктуру только потому, что одному потребителю нужен кэш, а другому аналитическая история.

Операционное состояние кабинета удобно держать в транзакционной базе. Большие исторические выборки и агрегаты могут жить в аналитическом хранилище. Короткий кэш запросов - в Redis.

Разделение проще сделать на старте, чем позже вытаскивать HTTP-клиент из слоя, который одновременно пишет таблицы, считает метрики и управляет UI-состоянием.

Когда API превращается в seller-платформу

В аналитической платформе для маркетплейсов уже есть полноценный seller-кабинет, административная консоль, FastAPI backend, финансовая отчётность и регулярная загрузка данных.

PostgreSQL хранит операционную модель, Airflow управляет ETL, ClickHouse обслуживает аналитические витрины и расчёты. Здесь marketplace API - только вход в более длинный процесс.

Пользователю важнее не сам ответ Wildberries или Ozon, а путь от подключения кабинета до понятного финансового показателя. Поэтому интеграция должна быть встроена в состояния загрузки, качество данных и отчётность.

Что получать онлайн, а что грузить фоном

Не каждый endpoint нужно вызывать в момент открытия страницы. Данные, которые меняются редко или требуют много запросов, логичнее обновлять фоном и показывать из своего слоя.

Онлайн-вызов полезен, когда пользователю критична текущая операция и ответ небольшой. Исторические продажи, большие отчёты и пересчёты обычно лучше помещаются в ETL.

Так интерфейс не зависит напрямую от скорости внешнего API, а повторная загрузка и обработка становятся контролируемым процессом.

Browser automation оставляю для тех случаев, где API действительно не хватает

У маркетплейсов есть данные и сценарии, которые публичный или seller API не всегда закрывает. Тогда может понадобиться работа с веб-страницами. Но я бы не начинал с браузерной автоматизации там, где официальный API уже даёт нужный контракт.

В отдельном сервисе мониторинга товаров браузер используется для разбора страниц и проверки продавцов, а поиск кандидатов дополнительно проходит локальную проверку сходства изображений. Это другой класс задачи, и его не стоит смешивать с обычным seller data layer.

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

Что обычно ломает marketplace-интеграцию уже после запуска

Главные проблемы начинаются не на первом успешном запросе, а когда интеграция работает регулярно: токен истёк, лимит изменился, пагинация вернула неполный набор, один кабинет отдал данные позже другого или повторный запуск пришёл после частичной ошибки. Если эти состояния не выделены явно, продукт начинает тихо показывать неполную картину.

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

Отдельный риск — смешать получение данных и бизнес-расчёт в одном слое. Тогда изменение endpoint или формата ответа затрагивает финансовую логику и интерфейс одновременно. Адаптеры и внутренняя модель нужны прежде всего для локализации таких изменений, а не ради архитектурной красоты.

  • Истечение токена и различия прав между кабинетами.
  • Rate limit и повторные запросы после временной ошибки.
  • Пагинация, курсоры и проверка полноты полученного периода.
  • Идемпотентность загрузки и защита от дублей при retry.
  • Изоляция внешнего API-контракта от внутренней финансовой и продуктовой модели.

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

Первым делом я бы выбрал один сквозной пользовательский сценарий: подключить кабинет, получить конкретный набор данных и довести его до полезного действия или отчёта.

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

  • Отделить credentials и конфигурацию кабинета.
  • Собрать typed-клиент для одного нужного сценария.
  • Нормализовать ошибки, пагинацию и rate limits.
  • Определить внутреннюю модель данных.
  • Разделить online-запросы и фоновые загрузки.
  • Только после этого выносить действительно общие части в data-core.

Вопросы

Нужно ли делать один общий клиент для Wildberries и Ozon?

Общее ядро полезно для конфигурации, запросов, ошибок и загрузки, но специфические методы и модели площадок лучше оставлять в отдельных адаптерах.

Как обрабатывать rate limit маркетплейса?

На уровне интеграционного слоя: различать тип ошибки, ограничивать повторные запросы, учитывать правила конкретного endpoint и не перекладывать эту механику на UI.

Что лучше хранить в ClickHouse?

Большие исторические выборки и аналитические витрины. Операционные сущности кабинета обычно удобнее хранить в транзакционной базе.

Когда нужен Airflow для маркетплейсов?

Когда есть регулярные зависимые загрузки, большие отчёты, пересчёты и необходимость контролировать состояние ETL-процесса.

Можно ли обойтись без browser automation?

Если официальный API закрывает нужный сценарий - лучше да. Браузер полезен для публичных данных и сценариев, которых нет в API, но требует больше сопровождения.

Продолжить по теме

Другие статьи