Ошибки API: единый контракт

HTTP-код обозначает категорию отказа, а машинный code уточняет причину. Выберите единый формат ошибок, чтобы обрабатывать ответы разных парсеров одним способом.

Выбор формата

Заголовок запроса X-BHAPI-Error-Format
ЗначениеПоведение
Без заголовкаПрежние статусы и формат ошибок: legacy.
legacyЯвный выбор прежнего формата.
problem-v1Единый контракт ниже: application/problem+json.
Пустое, неизвестное или повторённое значение400 в прежнем формате; при превышении IP-лимита — 429. Передавайте заголовок один раз.

Значения чувствительны к регистру. Параметры URL, JSON и Accept не переключают формат. Заголовок применяется к API парсеров и аналитики; веб-страницы, кабинетные токены, биллинг и /api/v1/jobs сохраняют свой договор.

Пример запроса

cURL
curl -i -X POST "https://bhapi.ru/ozon/api/v1/item/detail-by-url" \
  -H "X-API-Token: ВАШ_API_TOKEN" \
  -H "X-BHAPI-Error-Format: problem-v1" \
  -H "Content-Type: application/json" \
  --data '{"url":"https://www.ozon.ru/product/primer-tovara-123456789/"}'

Успешный ответ сохраняет прежние тело и Content-Type. Ошибки нового формата имеют Content-Language: ru, Cache-Control: no-store и X-Request-ID. На ответах API обеих версий присутствует Vary: X-BHAPI-Error-Format.

Структура ответа

Основа — Problem Details (RFC 9457). Поля code, request_id и retry — обязательные расширения BHAPI.

JSON · HTTP 429 · синтетический пример
{
  "type": "https://bhapi.ru/docs/errors#rate_limit_exceeded",
  "title": "Превышена частота запросов",
  "status": 429,
  "detail": "Увеличьте интервал между запросами.",
  "code": "rate_limit_exceeded",
  "request_id": "798c170a130d4c4bb4a3cc1c5e546e44",
  "scope": "ip_rate",
  "retry": {"action": "wait", "after_seconds": 30}
}

Здесь Retry-After: 30 совпадает с retry.after_seconds. 30 секунд — пример известной паузы, а не значение по умолчанию.

Поля объекта ошибки problem-v1
ПолеТипСмысл
typestringИдентификатор причины: https://bhapi.ru/docs/errors#<code>.
titlestringКраткий русский заголовок.
statusintegerФактический HTTP-статус.
detailstringБезопасное пояснение. В новом формате это строка.
codestringМашинный код из каталога ниже.
request_idstringСерверный UUID: 32 шестнадцатеричных символа, совпадает с X-Request-ID.
retryobjectДействие клиента и, если известен, интервал ожидания.
scopestringНеобязательная область известного лимита.
issuesarray<object>Необязательные сведения об ошибках входных полей.

Обрабатывайте code или type, а не текст title/detail. Для неизвестного будущего кода используйте HTTP-категорию и retry.action. Дополнительные поля можно игнорировать. instance и сведения о списании usage в этой версии отсутствуют.

Коды и причины

Таблица относится к problem-v1. Конкретные методы возвращают применимые к ним причины. Идентификатор type ведёт к соответствующему коду в этой таблице.

HTTP-категории и машинные причины ошибок
КодПричина
400
token_transport_invalid: Недопустимый способ передачи API-токена.
malformed_request: Некорректный синтаксис запроса.
401
authentication_required: API-токен не передан.
invalid_api_token: API-токен недействителен.
403
access_denied: Доступ запрещён.
parser_access_denied: Токен не даёт доступа к этому направлению.
subscription_inactive: Нет действующего доступа по подписке.
404
item_not_found: Подтверждено отсутствие товара.
route_not_found: Маршрут API не найден.
job_not_found: Задание не найдено или недоступно.
405
method_not_allowed: HTTP-метод не поддерживается; проверьте заголовок Allow.
409
request_in_progress: Необходимый слот операции уже занят.
413
request_too_large: Тело запроса превышает допустимый размер.
image_too_large: Изображение превышает допустимый размер.
415
unsupported_media_type: Неподдерживаемый Content-Type или тип изображения.
422
invalid_request: Неверные значения параметров, URL, cursor или фильтров.
429
rate_limit_exceeded: Лимит частоты запросов или API-токена.
quota_exhausted: Исчерпана тарифная квота.
500
internal_error: Внутренняя ошибка BHAPI.
finalization_failed: Сбой завершения после получения результата; проверьте исход операции.
502
upstream_invalid_response: Ответ источника не прошёл проверку.
item_fetch_failed: Не удалось подтвердить карточку товара.
upstream_request_rejected: Источник отклонил запрос без подтверждённой ошибки клиента.
503
parser_overloaded: Временная перегрузка парсера.
service_unavailable: Сервис временно не готов.
upstream_unreachable: Не удалось соединиться с источником.
upstream_access_restricted: Источник ограничил доступ.
parser_internal_error: Внутренняя ошибка источника.
504
upstream_timeout: Истёк срок ожидания источника.
Другой 4xx
request_rejected: Запрос отклонён; проверьте параметры и описание метода.

Действия и квоты

Значения retry.action
ДействиеЧто делать
fix_requestИсправить параметры, метод или формат данных.
provide_tokenПередать действующий API-токен.
review_accessПроверить права токена и активный доступ.
review_planПроверить квоту и тариф в кабинете.
wait_for_resetДождаться восстановления лимита.
waitВыдержать указанную паузу перед новым вызовом.
check_outcomeСверить исход текущего вызова перед новым запросом.
contact_supportОбратиться в поддержку с X-Request-ID и временем запроса.

retry.after_seconds — необязательный integer от 1 до 86400, только для wait и wait_for_reset, если время известно. Отсутствие этого поля не означает, что можно повторить немедленно. Когда интервал есть, Retry-After содержит то же число секунд.

Область лимита scope
ЗначениеСмысл
ip_rateЧастота запросов с IP; учитывайте общий адрес за proxy.
token_dayДневной лимит API-токена.
token_totalОбщий лимит токена; автоматическое восстановление не обещается. Действие по умолчанию — review_access: проверьте ограничения ключа.
free_quotaFree-квота. Другой ключ не восстанавливает грант; ежемесячное обновление не предусмотрено.
paid_monthlyМесячная квота платного тарифа; проверьте период в кабинете.

scope отсутствует, если область не установлена. Перегрузка парсера — 503 parser_overloaded, а не расходуемая клиентская квота.

HTTP-ошибка не гарантирует отсутствие списания. При check_outcome сначала сверьте результат по X-Request-ID и времени запроса. Действие не гарантирует идемпотентность; новый вызов может повлечь новый расход. Переход между форматами не отменяет выполненную операцию.

Ошибки входных полей

issues содержит до 20 безопасных пояснений. Поля записи: location (body, query, path или header), path (массив имён полей и индексов), code, message. Для фильтров возможен available — список допустимых значений.

Сырые входные значения и секреты не возвращаются. Для секретных полей подробный путь может отсутствовать; полный список ошибок и вариантов не гарантируется. Исправляйте разрешённые параметры, не извлекайте значения из текста сообщения.

Переход и ограничения

Сначала добавьте обработку application/problem+json, code, request_id и retry.action; затем включите заголовок на нужных вызовах. В legacy тело ошибки может иметь строку, список или объект detail; новый объект возвращается непосредственно в корне JSON.

В новом формате перегрузка меняется с 429 на 503, занятой слот — с 429 на 409, отказ подтверждения карточки — с 417 на 502. Без заголовка прежние статусы сохраняются. Подтверждённый отказ единственного синхронного результата получает HTTP-ошибку; пустой успешный поиск остаётся успехом.

batch и job: ошибки отдельных элементов пакета и состояния заданий внутри успешного ответа не преобразуются в Problem Details. Общий отказ доступа, входных данных или выполнения запроса к parser task API может возвращать новый формат. Кабинетный /api/v1/jobs в него не входит.

Чтобы вернуться к прежнему формату, уберите заголовок или укажите legacy. Не повторяйте запрос автоматически при неясном исходе. Для браузерных клиентов проверьте, разрешают ли ваши CORS и proxy передачу заголовка и чтение X-Request-ID/Retry-After.