/8 мин

Ozon, WB и Яндекс Маркет: подводные камни API при автоматизации

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

Три API — три философии

Вот что обнаружилось при реальной работе с каждым API:

OzonWildberriesЯндекс Маркет
АвторизацияClient-Id + Api-Key (2 заголовка)Authorization: <token> (1 заголовок)Api-Key: <token> (1 заголовок)
Base URLОдин на всёНесколько поддоменов по категориямОдин на всё
Формат цен"1500.00" (строка, рубли)150000 (число, копейки)1500 (число, рубли)
Код валютыRUBRUBRUR (не RUB!)
Rate limitHTTP 429HTTP 429 + 409HTTP 420
SandboxЕстьЧастичныйНет
← прокрутите →

Каждая строчка этой таблицы — потенциальный баг в проде. Разберём подробнее.

WB: цены в копейках и зоопарк URL'ов

Цены. WB хранит все суммы в минимальных единицах. Получаете заказ, видите "salePrice": 504600 — это 5046.00₽, а не полмиллиона. Забудьте про это при обновлении цены — и товар улетит за 25₽ вместо 2500₽.

# Конвертация обязательна в обе стороны
kopecks_to_rubles() {
  echo "$1" | awk '{printf "%.2f", $1/100}'
}
 
rubles_to_kopecks() {
  echo "$1" | awk '{printf "%.0f", $1*100}'
}

URL'ы. Единственная из трёх площадок, где API разнесён по поддоменам:

content-api.wildberries.ru        # каталог
marketplace-api.wildberries.ru    # заказы
statistics-api.wildberries.ru     # статистика
discounts-prices-api.wildberries.ru  # цены
seller-analytics-api.wildberries.ru  # аналитика

Хотите получить заказы? Один хост. Обновить цены? Другой. У Ozon и Яндекса — один URL на все эндпоинты.

Двойной статус заказа. У Ozon один статус. У WB — два параллельных: supplierStatus (управляете вы) и wbStatus (управляет WB). Показывать нужно оба, иначе непонятно, где заказ на самом деле.

409 Conflict. WB может вернуть 409 при конкурентном доступе, и по отзывам разработчиков это может негативно влиять на rate limit. Безопаснее делать агрессивный cooldown (3+ секунды) при получении 409.

Яндекс Маркет: HTTP 420 и ценовой карантин

HTTP 420. Все знают, что rate limit — это 429 Too Many Requests. Все, кроме Яндекса. Они возвращают 420 Enhance Your Calm. Любая стандартная retry-логика его пропустит:

# Стандартный подход — не сработает:
if [[ "$http_code" == "429" ]]; then retry; fi
 
# Для Яндекс Маркета нужно:
if [[ "$http_code" == "429" || "$http_code" == "420" ]]; then retry; fi

Ценовой карантин. Уникальная особенность: если меняете цену слишком резко (обычно >5%), она не применяется сразу, а попадает в карантин. Вы думаете, что обновили цену — а покупатели видят старую. Ни Ozon, ни WB ничего подобного не имеют. Нужна автоматическая проверка после каждого обновления.

RUR, не RUB. API использует "currencyId": "RUR" — старый код рубля, формально заменённый на RUB в 2004 году. Отправите "RUB" — получите ошибку валидации.

businessId vs campaignId. Двухуровневая система: businessId (кабинет) — для каталога и цен, campaignId (магазин) — для заказов и остатков. Перепутали — получите 404. Причём эндпоинт существует, просто вы обратились не с тем ID. Ошибка неочевидная и долго дебажится.

Ozon: эталон для сравнения

На фоне WB и Яндекса, Ozon API выглядит предсказуемо:

  • Один base URL на всё
  • Цены в рублях (не в копейках и не с RUR)
  • Стандартный 429 для rate limit
  • Консистентная пагинация через cursor token

Это не реклама — просто факт: Ozon проще всего интегрировать. WB и Яндекс требуют значительно больше защитного кода.

Типичные баги при мультиплатформенной интеграции

Из нашего опыта — три категории ошибок, которые встречаются чаще всего:

1. Сдвиг данных при парсинге. Если у вас общий формат логов или хранения для всех площадок, но у платформ разное количество полей — данные сдвигаются. Пример: audit log с 9 полями через разделитель, но парсер ожидает 8. Всё начиная с четвёртого поля содержит чужие значения. Откат цены молча не работает.

2. Жёсткая привязка к одной платформе. Функция отката цены вызывает update_price(), которая зашита на Ozon API. Откатываете цену на WB — а запрос уходит в Ozon. Для каждого действия нужен platform dispatch:

case "$platform" in
  ozon)    update_price "$sku" "$old_value" ;;
  wb)      wb_update_price "$sku" "$(rubles_to_kopecks "$old_value")" ;;
  ymarket) ym_update_prices "$sku" "$old_value" ;;
esac

Обратите внимание: для WB при откате нужна конвертация обратно в копейки, потому что в логах хранятся рубли, а WB API принимает копейки.

3. Неправильные пути к данным в ответах. Каждая площадка по-своему вкладывает данные в JSON. Остатки на Яндексе — в .stocks[0].count, не в .count. Универсальный jq-запрос ".stock // .count // 0" вернёт 0 для всех товаров на Яндексе.

Что с этим делать

Если управляете продажами на нескольких площадках и тратите время на ручное переключение между кабинетами — автоматизация решает. Но делать это на коленке — путь к багам с реальными финансовыми последствиями (неправильная цена в проде — это убытки).

Мы уже прошли через эти грабли и знаем подводные камни каждого API. Если хотите автоматизировать работу с маркетплейсами — пишите, обсудим задачу.

Есть задача?

Обсудим ваш проект.

Написать в Telegram