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

Интеграция с Wildberries

Всё общение с Seller API Wildberries идёт только из бэкенда через один компонент — WbApiClient (backend/src/modules/wb-api). Он берёт на себя базовые адреса доменов, локальный rate-limit, повторы, таймауты и обработку ошибок, поэтому функциональные модули (оцифровка, цены, автобиддер и др.) не знают деталей протокола. Решения — ADR-0006, ADR-0041, ADR-0048, ADR-0049, ADR-0051, ADR-0057, ADR-0066. Справка по самим методам WB — WB API.

Клиент и домены​

WbApiClient.request({ scope, apiKey, path, method, body, query, timeoutMs, accountId }) реализован на нативном fetch (Node 22) с AbortSignal.timeout, без axios. Токен кабинета передаётся в запросе: сервисы расшифровывают его перед вызовом (Безопасность). scope выбирает базовый адрес:

ScopeБазовый URL (prod)Лимит по умолчанию
commoncommon-api.wildberries.ru3 запроса / 30 с
contentcontent-api.wildberries.ru100 / мин
analyticsseller-analytics-api.wildberries.ru60 / мин
pricesdiscounts-prices-api.wildberries.ru60 / мин
marketplacemarketplace-api.wildberries.ru100 / мин
statisticsstatistics-api.wildberries.ru60 / мин
advertadvert-api.wildberries.ru100 / мин
feedbacksfeedbacks-api.wildberries.ru100 / мин
buyer-chatbuyer-chat-api.wildberries.ru60 / мин
suppliessupplies-api.wildberries.ru60 / мин
returnsreturns-api.wildberries.ru60 / мин
documentsdocuments-api.wildberries.ru60 / мин
financefinance-api.wildberries.ru60 / мин
user-managementuser-management-api.wildberries.ru5 / с

Реестр адресов — wb-api-scope.ts (WB_API_BASE_URLS); для части доменов есть песочница (WB_API_USE_SANDBOX=true). Точные лимиты методов заданы в wb-rate-limits.ts (WB_ENDPOINT_RATE_LIMITS, ключ scope:path) и перекрывают scope-дефолт — например, common:/ping — 3 за 30 с, common:/api/v1/seller-info — 1 в минуту, справочник складов supplies:/api/v1/warehouses — 6 в минуту; фиды заказов и продаж (statistics) — по 1 запросу в минуту (у заказов допустим всплеск до 10). Реестр дополняется по мере появления новых методов.

Token-bucket лимитер​

Лимитер WbRateLimiter (ADR-0041) — token-bucket в Redis, отдельный на кабинет и метод: ключ wb:rl:v2:{accountId}:{scope}:{path} (для запросов без кабинета — _global).

  • Параметры бакета: tokens на окно intervalMs, опциональный burst (ёмкость всплеска). Токены восполняются плавно.
  • Захват атомарен: Lua-скрипт зеркалит чистую функцию-эталон evaluateTokenBucket (wb-token-bucket.ts) — арифметика тестируется на TS, атомарность даёт Redis. Время now передаётся из приложения.
  • Состояние ключа живёт до полного восполнения пустой корзины и истекает само.
  • Потолок ожидания: rateLimitWaitCeilingMs = max(WB_API_RATE_LIMIT_MAX_WAIT_MS, N × окно + 5 с) (по умолчанию 30 с). Если ждать дольше — клиент бросает WbApiRateLimitedError локально, не обращаясь к WB.
  • WbQuotaService читает состояние корзин без списания (used, capacity, resetsInMs) (GET /wb-accounts/:id/quota).

Обратная связь от WB (429 и заголовки)​

Локальная модель всегда чуть оптимистичнее счётчика WB, поэтому 429 — штатная обратная связь, а не авария (ADR-0048, ADR-0066):

  • Штраф бакета (penalize): после реального 429 корзина осушается и получает notBefore — момент, раньше которого токен не выдаётся даже если он «восполнился». Штраф монотонен и записывается в том числе на последней попытке, чтобы следующий прогон не бился в то же закрытое окно.
  • Пауза на 429 = min( max(Retry-After, восполнение одного токена, экспонента), потолок ): для statistics это 60 с, для prices — 0,6 с. Короткий Retry-After поднимается до окна.
  • Сверка по заголовкам (parseObservedRateLimit): на каждом ответе читаются X-Ratelimit-Limit, -Remaining, -Reset, -Retry; бакет только сужается до Remaining, а при Remaining: 0 удерживается до сброса окна. Сбой сверки на запрос не влияет.
  • Прочие ошибки: 5xx и сетевые сбои повторяются короткой экспонентой с jitter (WB_API_RETRY_ATTEMPTS=3, WB_API_RETRY_BASE_MS=500); 401/403/404 не повторяются.
Ошибка клиентаКогда
WbApiUnauthorizedError / WbApiForbiddenError401 / 403 — токен отозван, истёк, нет прав
WbApiNotFoundError404
WbApiRateLimitedError429 от WB или локальный потолок ожидания
WbApiServerError5xx
WbApiNetworkErrorDNS/TCP/обрыв соединения
WbApiResponseTimeoutErrorтаймаут чтения (общий дефолт WB_API_TIMEOUT_MS=10 000; тяжёлые методы задают свой timeoutMs, например отчёт о реализации — ADR-0049)

На 401 WbApiEvents публикует событие «WB отверг ключ»: пользователь получает уведомление «Проверьте API-ключ Wildberries» (Уведомления).

Перенос джобы по лимиту (reschedule)​

Ждать в воркере минуты — значит занимать слот concurrency, пока соседние кабинеты стоят. Поэтому процессоры длинных синков (digitization-sync, rnp-ads-sync, rnp-finance-sync, rnp-sections-sync, inventory-sync, autobidder-sync) оборачивают работу в WbRateLimitRescheduleService.run(...) (ADR-0051):

  1. Внутри AsyncLocalStorage-контекста acquire при ожидании дольше порога (5 с) бросает WbRateLimitDeferError(waitMs) вместо сна.
  2. Процессор переносит джобу в delayed на нужную паузу без расхода попытки и с сохранением прогресса (курсор/страница), в прогрессе джобы отображается ожидание.
  3. Короткие очереди (pricing-bulk, booking-slot-monitor) не переносятся: у них вызовы не многостраничные, а бронировщику важна частота опроса.

Gate кабинета​

Ограничения WB действуют на токен, а не на джобу, поэтому на один кабинет в «общие» бакеты одновременно ходит одна джоба (WbAccountJobGate, ADR-0057): ключ wb:gate:{accountId}, lease 90 с с heartbeat 30 с. Пользовательские запуски (ручной импорт, ручной синк) имеют приоритет над плановыми; плановые не копят хвост и пропускают тик со статусом account-busy. При переносе по лимиту владелец продлевает lease на паузу + 60 с, чтобы соседняя джоба не вклинилась в окно. Повторный POST импорта при уже живой джобе возвращает alreadyRunning с её jobId. Подробнее — Фоновые задачи.

Переменные окружения​

ПеременнаяПо умолчаниюНазначение
WB_API_USE_SANDBOXfalseиспользовать песочницы доменов, где они есть
WB_API_TIMEOUT_MS10 000таймаут запроса по умолчанию
WB_API_RETRY_ATTEMPTS3число попыток
WB_API_RETRY_BASE_MS500база экспоненциального backoff
WB_API_RATE_LIMIT_MAX_WAIT_MS30 000базовый потолок ожидания лимитера
SYNC_QUEUE_CONCURRENCY5параллельные кабинеты в sync-очередях

Полный перечень — Переменные окружения.