Перейти к основному содержимому

Интеграция с WB API: заметки для разработчиков

Как бэкенд «Оптимы» обращается к Wildberries Seller API: домены, категории токена по модулям, лимиты, повторы и обработка 429. Архитектурный обзор интеграции — Архитектура: интеграция с WB.

Весь код интеграции — backend/src/modules/wb-api. Фичевые модули не ходят в WB напрямую: только через WbApiClient.request({ scope, path, apiKey, accountId, … }). API-ключ кабинета хранится зашифрованным (AES-256-GCM, ADR-0005) и расшифровывается непосредственно перед запросом.

Домены (scope)​

Каждая категория WB API — отдельный домен. Таблица WB_API_BASE_URLS в backend/src/modules/wb-api/wb-api-scope.ts; источник — метод /ping в wildberries-api/01-general.yaml.

scopeКатегория токена WBProductionSandbox
commonТарифы, Новости, Информация о продавцеhttps://common-api.wildberries.ru—
contentКонтентhttps://content-api.wildberries.ruhttps://content-api-sandbox.wildberries.ru
analyticsАналитикаhttps://seller-analytics-api.wildberries.ru—
pricesЦены и скидкиhttps://discounts-prices-api.wildberries.ruhttps://discounts-prices-api-sandbox.wildberries.ru
marketplaceМаркетплейсhttps://marketplace-api.wildberries.ru—
statisticsСтатистикаhttps://statistics-api.wildberries.ruhttps://statistics-api-sandbox.wildberries.ru
advertПродвижениеhttps://advert-api.wildberries.ruhttps://advert-api-sandbox.wildberries.ru
feedbacksВопросы и отзывыhttps://feedbacks-api.wildberries.ruhttps://feedbacks-api-sandbox.wildberries.ru
buyer-chatЧат с покупателямиhttps://buyer-chat-api.wildberries.ru—
suppliesПоставкиhttps://supplies-api.wildberries.ru—
returnsВозвраты покупателямиhttps://returns-api.wildberries.ru—
documentsДокументыhttps://documents-api.wildberries.ru—
financeФинансыhttps://finance-api.wildberries.ru—
user-managementУправление пользователями продавцаhttps://user-management-api.wildberries.ru—

Sandbox включается переменной WB_API_USE_SANDBOX=true (нужен токен «Тестовый контур»). Для категорий без sandbox клиент бросает явную ошибку, а не уходит молча в production.

Какие категории токена нужны модулям​

Таблица собрана по фактическим вызовам WbApiClient в коде (константы *_SCOPE и *_PATH модулей). Категории marketplace, buyer-chat, returns, documents и user-management в клиенте описаны, но сейчас ни одним модулем не используются.

Модуль «Оптимы»Модуль ТЗscopeМетоды WBЗапись в WB
Кабинеты (wb-accounts)ОбщиеcommonGET /ping — проверка ключа—
Каталог (catalog)Общиеcontent, pricesPOST /content/v2/get/cards/list, GET /api/v2/list/goods/filter—
Оцифровка (digitization)5.1statistics, financeGET /api/v1/supplier/orders, GET /api/v1/supplier/sales, POST /api/finance/v1/sales-reports/detailed—
Цены и скидки (pricing)5.2pricesPOST /api/v2/upload/taskда
Репрайсер (repricer)5.3pricesPOST /api/v2/upload/taskда
Автобиддер (autobidder)5.4advertGET /adv/v1/promotion/count, /api/advert/v2/adverts, /adv/v3/fullstats, PATCH /api/advert/v1/bids, GET /adv/v0/pauseда
ИИ-агент (ai-agent)5.10advert (через сервисы автобиддера)ставки, бюджеты, пауза кампанийда
Автоответы (autoreply)5.5feedbacksGET /api/v1/feedbacks, GET /api/v1/questions, POST /api/v1/feedbacks/answer, PATCH /api/v1/questionsда
РНП (rnp)5.6analytics; также advert и finance через сервисы автобиддера и оцифровкиPOST /api/analytics/v3/sales-funnel/products/history, fullstats, отчёт реализации—
Бронировщик (booking)5.8common, suppliesGET /api/tariffs/v1/acceptance/coefficients, GET /api/v1/warehouses—
Товарный учёт (inventory)5.11analytics/api/v1/warehouse_remains (создание задачи → статус → скачивание)—
A/B-тесты (ab-test)5.14contentPOST /content/v3/media/file — загрузка фото вариантада
База знаний (knowledge-base)5.15commonGET /api/communications/v2/news—

Модули без обращений к WB: бухгалтерия (считает по данным оцифровки), ИИ-помощник, генерация контента, досудебные претензии, уведомления.

Практический вывод. Чтобы работали все модули, токен кабинета должен включать категории: Контент, Аналитика, Цены и скидки, Статистика, Продвижение, Вопросы и отзывы, Поставки, Финансы, а также Тарифы, Новости, Информация о продавце. Для модулей, которые пишут в WB (колонка «Запись в WB»), токен должен быть выпущен с правом записи.

Проверка ключа POST /wb-accounts/:id/check вызывает только GET /ping на common-api. Она подтверждает, что ключ действителен, но не проверяет набор категорий: нехватка категории проявится позже, ответом 401/403 от WB в конкретном модуле (клиенту API вернётся WB_AUTH_FAILED).

Лимиты запросов​

Лимиты WB действуют «на аккаунт продавца». «Оптима» соблюдает их сама, до отправки запроса: распределённый token-bucket в Redis (WbRateLimiter, ADR-0041). Ключ бакета — wb:rl:v2:<accountId>:<scope>:<path>, то есть лимит считается отдельно для каждого кабинета и метода и общий для всех реплик и воркеров.

Реестр — backend/src/modules/wb-api/wb-rate-limits.ts. Сначала ищется точное переопределение scope:path, иначе берётся значение по умолчанию для scope.

По умолчанию для scope (запросов за окно):

scopeЛимит
common3 / 30 с
content, marketplace, advert, feedbacks100 / 60 с
analytics, prices, statistics, buyer-chat, supplies, returns, documents, finance60 / 60 с
user-management5 / 1 с

Переопределения для методов:

Метод (scope:path)СкоростьВсплеск
*:/ping (каждый scope отдельно)3 / 30 с—
common:/api/v1/seller-info1 / 60 с—
common:/api/communications/v2/news1 / 60 с—
common:/api/tariffs/v1/acceptance/coefficients6 / 60 с—
supplies:/api/v1/warehouses6 / 60 с—
statistics:/api/v1/supplier/orders1 / 60 с10
statistics:/api/v1/supplier/sales1 / 60 с—
finance:/api/finance/v1/sales-reports/detailed1 / 60 с—
analytics:/api/v1/warehouse_remains (создание задачи)1 / 60 с5
analytics:…/warehouse_remains/tasks/{task_id}/status1 / 5 с—
analytics:…/warehouse_remains/tasks/{task_id}/download1 / 60 с—
analytics:/api/analytics/v3/sales-funnel/products/history3 / 60 с—
prices:/api/v2/upload/task10 / 6 с—
advert:/adv/v3/fullstats1 / 20 с—

«Всплеск» — ёмкость корзины: столько запросов можно выпустить залпом из полной корзины, дальше действует установившаяся скорость. Пути задачных отчётов канонизируются ({task_id} вместо uuid), чтобы все задачи кабинета делили один бакет.

Текущее состояние бакетов кабинета отдаёт GET /wb-accounts/:id/quota (только чтение).

Сверка с заголовками WB​

На каждом ответе WB (любого статуса) клиент читает X-Ratelimit-Remaining, X-Ratelimit-Limit, X-Ratelimit-Reset и X-Ratelimit-Retry и только сужает локальный бакет: при Remaining: 0 корзина обнуляется до сброса окна WB. Расхождение Limit с реестром пишется в лог на уровне verbose — как материал для ручной правки wb-rate-limits.ts (ADR-0066).

Повторы и поведение на 429​

Два уровня повторов: внутри одного HTTP-запроса (WbApiClient, wb-retry.ts) и на уровне фоновой задачи BullMQ.

HTTP-уровень (WB_API_RETRY_ATTEMPTS, по умолчанию 3 попытки, включая первую):

ОшибкаПовторПауза
429даmin(max(Retry-After, восполнение одного токена, экспонента), потолок)
5xx, сетевой сбой, таймаутдаэкспонента WB_API_RETRY_BASE_MS × 2^(n−1) + jitter
таймаут чтения тела с явно заданным бюджетомнет—
401, 403, 404нет—
  • Пауза на 429 никогда не короче восполнения одного токена бакета (ceil(интервал / токены): 60 с для фидов продаж, 600 мс для цен). Короткий Retry-After от WB поднимается до этого значения (ADR-0048).
  • После реального 429 от WB бакет штрафуется: корзина осушается, и до момента notBefore токены не выдаются никому, включая другие воркеры. Штраф ставится и на последней попытке.
  • Потолок ожидания — max(WB_API_RATE_LIMIT_MAX_WAIT_MS, N × окно + 5 с), где N = 2 для лимитов 1 запрос/окно, иначе 1. Он общий для ожидания токена и паузы ретрая. Если ожидание токена превышает потолок, клиент сразу получает WbApiRateLimitedError с Retry-After без похода в WB. Для HTTP-клиента «Оптимы» это ответ 429 TOO_MANY_REQUESTS с details.retryAfterSeconds.

Уровень задач.

  • Перенос вместо сна (ADR-0051). В длинных синках (digitization-sync, rnp-ads-sync, rnp-finance-sync, rnp-sections-sync, inventory-sync, autobidder-sync) ожидание токена дольше 5 секунд не «спит» в воркере: задача переносится в delayed и освобождает слот, а попытка не расходуется. Короткие очереди (pricing-bulk, booking-slot-monitor) не оборачиваются.
  • Минутная лестница у пользовательских импортов (import-user-feeds, import-user-report, autobidder-sync-user): 5 попыток, экспонента от 60 с (60 → 120 → 240 → 480 с). Ошибки, которые повтором не лечатся (401/403/404), завершают задачу сразу (UnrecoverableError).
  • Gate кабинета (ADR-0057). По одному кабинету одновременно работает одна WB-тяжёлая задача (lease wb:gate:<accountId> в Redis, TTL 90 с, heartbeat 30 с). Пользовательские задачи приоритетнее плановых. Плановая задача, не дождавшаяся gate, завершается с skipped: account-busy, и кабинет подхватит следующий тик.
  • Фаза ожидания видна клиенту в поле progress.phase статуса задачи (waiting-account, rate-limited, retrying) — см. Обзор API, раздел «Длительные операции».

Предыстория решений — ADR-0006 (исходная политика лимитов и повторов) и ADR-0040 (параллелизм sync-очередей, SYNC_QUEUE_CONCURRENCY).

Как добавить вызов нового метода WB​

  1. Найдите метод в базе знаний (см. ниже): домен, путь, лимит «на один аккаунт продавца».
  2. Если лимит метода отличается от значения по умолчанию для его scope, добавьте переопределение в WB_ENDPOINT_RATE_LIMITS (wb-rate-limits.ts) с комментарием-источником.
  3. Вызывайте метод через WbApiClient.request с accountId кабинета: без него лимит считается на глобальном ключе _global.
  4. Если метод нужен длинному синку, оберните обработку задачи в WbRateLimitRescheduleService.run и, при необходимости, подключите gate кабинета.
  5. Обновите таблицу «Какие категории токена нужны модулям» на этой странице.

База знаний WB API​

  • wildberries-api/ (в корне монорепо) — исходные OpenAPI YAML WB по разделам (01-general.yaml … 13-finances.yaml), сводки разделов в knowledge/*.md и машинные индексы indexes/ (methods.jsonl, documents.jsonl, auth.jsonl, domains.jsonl).

  • Навык wb-api-knowledge (.claude/skills/wb-api-knowledge) — поиск по методам, доменам, авторизации и лимитам для AI-агентов и разработчиков:

    cd .claude/skills/wb-api-knowledge
    python3 scripts/search_knowledge.py "<запрос>" [--kind documents] [--domain <name>]

    Нужен Python 3. Приоритет источников: indexes/methods.jsonl → documents.jsonl → auth.jsonl → domains.jsonl → knowledge/*.md → исходные YAML.

Связанные страницы​