Когда продаёшь на одном маркетплейсе — всё терпимо. Но три площадки одновременно — это три личных кабинета, три формата цен и три разных подхода к rate limiting. Мы собрали интеграцию со всеми тремя и задокументировали каждые грабли.
Три API — три философии
Вот что обнаружилось при реальной работе с каждым API:
| Ozon | Wildberries | Яндекс Маркет | |
|---|---|---|---|
| Авторизация | Client-Id + Api-Key (2 заголовка) | Authorization: <token> (1 заголовок) | Api-Key: <token> (1 заголовок) |
| Base URL | Один на всё | Несколько поддоменов по категориям | Один на всё |
| Формат цен | "1500.00" (строка, рубли) | 150000 (число, копейки) | 1500 (число, рубли) |
| Код валюты | RUB | RUB | RUR (не RUB!) |
| Rate limit | HTTP 429 | HTTP 429 + 409 | HTTP 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. Если хотите автоматизировать работу с маркетплейсами — пишите, обсудим задачу.