Обработка ошибок и лимитов в Яндекс Маршрутизации: практический гайд по API для администраторов и DevOps | AdminWiki

Обработка ошибок и лимитов в Яндекс Маршрутизации: практический гайд по API для администраторов и DevOps

19 сентября 2026 14 мин. чтения
Содержание статьи

Сбои при обращении к API маршрутизации укладываются в четыре класса: неверный запрос (4xx), отказ авторизации (401 и 403), превышение лимитов (429) и временная неработоспособность сервиса (5xx). Диагностика начинается с одного ответа API: в нём есть HTTP-статус, машинный код ошибки и идентификатор запроса, по которому поддержка находит конкретный вызов в своих логах. Этого набора достаточно, чтобы за пару минут решить, что чинить: свой код, свой ключ или свою нагрузку.

Оговорка по источникам. Разбор ниже опирается на семантику HTTP-статусов и стандартных заголовков, а также на практику смежных API-сервисов. Точные названия полей, значения error.code и размеры квот Яндекс Маршрутизации сверяйте с официальной документацией и OpenAPI-спецификацией сервиса: они отличаются между версиями, а ошибка в догадке стоит дороже одной проверки.

Держите под рукой четыре вещи: request_id из ответа, тело ответа с error.code и error.message, HTTP-статус и точное время запроса с таймзоной. Алгоритм из четырёх шагов: посмотреть статус, прочитать код ошибки, сверить с таблицей ниже, проверить, повторяется ли ошибка на одном запросе или на всех. Ретраить вслепую до диагностики нельзя: повторы без разбора превращают локальный сбой в блокировку. В кейсе с OT Commerce проблема держалась почти три дня, потому что сначала боролись с симптомом, а не с источником (разбор инцидента).

Как быстро понять, что именно сломалось в интеграции с Яндекс Маршрутизацией

Разделите диагностику на два уровня. Уровень запроса: ответ конкретного вызова, его статус и код ошибки. Уровень интеграции: распределение ошибок по времени, доля ретраев, остаток квоты. Первый показывает, что пошло не так сейчас, второй объясняет, почему сбой повторяется. Инструменты второго уровня (анализ логов, трассировка распределённых вызовов) разобраны в материале о диагностике проблем маршрутизации.

Что фиксировать в первую очередь: request_id, error.code, HTTP-статус

request_id приходит в теле или заголовках ответа и выполняет две задачи: связывает запись в ваших логах с конкретным вызовом на стороне провайдера и склеивает цепочку запросов одного бизнес-действия. error.code даёт машиночитаемую причину, по которой строят алерты и ветвление в обработчике. error.message читает человек: чаще всего там указано поле, не прошедшее валидацию. HTTP-статус задаёт класс проблемы и решает, можно ли повторять запрос.

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

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "quota exceeded"
  },
  "request_id": "00000000-0000-0000-0000-000000000000"
}

Что не попадает в логи никогда: API-ключ, токен в заголовке Authorization, полные тела запросов с адресами и персональными данными. Маскируйте координаты и идентификаторы заказчиков, хэшируйте ключ, если он нужен для отладки. Перед правками сверьте версию API: changelog и OpenAPI-спецификация показывают, какие поля и настройки могли измениться, а sandbox позволяет проверить поведение без риска для продакшена.

Типовые ошибки API Яндекс Маршрутизации: таблица «код → причина → действие»

СтатусПричина по смыслу HTTPДействие
400Тело запроса не разбирается: невалидный JSON, координаты вне диапазона, дата в неожиданном форматеСравнить запрос со схемой из спецификации, проверить формат координат и времени
401Ключ отсутствует, истёк или передан в неверном заголовкеПроверить заголовок Authorization, срок жизни ключа, лишние кавычки и пробелы
403Ключ валиден, но не имеет нужного scope или доступ к ресурсу запрещёнСверить scope ключа с вызываемой операцией и ограничениями аккаунта
404Несуществующий ресурс: опечатка в пути, удалённый объект, не та версия эндпоинтаСверить путь и версию API, проверить существование объекта
409Конфликт состояния: повторная отправка уже созданного заказа или объектаПроверить идемпотентность операции и наличие ключа идемпотентности
422Данные синтаксически верны, но семантически неверны: точка вне зоны обслуживания, слишком длинный маршрутПроверить бизнес-правила, зону покрытия и ограничение на число точек
429Превышен rate limit или суточная квотаОстановить ретраи, выдержать Retry-After, снизить QPS
500, 502, 503, 504Сбой на стороне сервиса или сетевого посредникаРетрай с backoff и jitter, при серии ошибок разомкнуть цепь

Коды 4xx, кроме 429, повторять бессмысленно и вредно: ни тело, ни ключ от повтора не изменятся, а нагрузка на сервис вырастет. Ошибки авторизации чаще связаны с настройками доступа, чем с кодом запроса, и их причины разобраны в материале про ошибки аутентификации при обращении к API.

Лимиты и квоты API Яндекс Маршрутизации: что именно ограничено

Лимиты делятся на три группы: частота (запросов в секунду или минуту), суточная квота на число операций и объёмные ограничения на один запрос (число точек, заказов, символов). Ограничения почти всегда двухуровневые: частота плюс объём. У API перевода Polytranslator, например, заявлено до 50 000 входных символов на запрос и 30 запросов перевода в минуту (документация Polytranslator). Там же указано, что бесплатные ежедневные и подписочные кредиты через API не расходуются: то есть важно понимать, какой именно пул списывается за конкретный вызов.

Обратный пример: у Magic Hour API-использование не имеет отдельных лимитов скорости или параллелизма, а ёмкость зависит от плана, при этом кредиты переносятся и не истекают (Trust, Privacy & Data Use). Вывод один: перед нагрузочным тестом выясняйте, что именно расходует квоту в вашем тарифе и как она восстанавливается.

Квота не лечится ретраями. Её лечит снижение нагрузки: батчинг запросов, кэш повторяющихся маршрутов, перенос фоновых задач в окна низкой активности. Проверьте по документации, какие операции списывают суточную квоту, а какие нет: разница между тестовым и продуктовым доступом часто объясняет, почему в sandbox запрос проходит, а в проде возвращает 429.

Как читать заголовки ответа: Retry-After, X-RateLimit-*

Retry-After задаёт паузу: количество секунд или дату, раньше которой повторять запрос нельзя. Это не рекомендация, а условие. Заголовки вида X-RateLimit-Limit, X-RateLimit-Remaining и X-RateLimit-Reset показывают лимит, остаток и момент сброса; это распространённая практика де-факто, и точные имена заголовков в Яндекс Маршрутизации проверяйте по документации, а не по памяти.

Практическое правило: если остаток близок к нулю, снижайте частоту заранее, не дожидаясь 429. Парсите заголовки в клиенте и передавайте в метрики: остаток квоты на графике виден раньше, чем приходит отказ.

Что делать при 429: пауза, распределение, приоритизация запросов

  1. Остановить все ретраи на время из Retry-After. Один воркер, который ждёт, дешевле десяти, которые повторяют.
  2. Понизить общий QPS по всем клиентам интеграции, а не только по тому, что получил 429.
  3. Ввести очередь с приоритетами: оперативные заказы идут первыми, фоновая синхронизация каталога или истории маршрутов ждёт.
  4. При устойчивом 429 пересмотреть архитектуру: батчинг, локальный кэш результатов, отказ от лишних вызовов.

Главная ошибка при 429: параллельные ретраи от нескольких воркеров. Каждый видит одну и ту же ошибку, каждый повторяет, суммарный QPS растёт, и блокировка продлевается. Ограничитель частоты должен быть общим для всех процессов, а не локальным для каждого.

Retry-логика для API Яндекс Маршрутизации: что повторять, а что нельзя

Правило: повторять можно только идемпотентные операции и только при 5xx, а также при сетевых таймаутах и обрывах соединения. Коды 4xx, кроме 429, не повторяются. 429 повторяется исключительно после паузы из Retry-After. Базовые параметры, которые работают в проде: base delay 1 секунда, множитель 2, максимум 30 секунд, jitter ±20%, 3-5 попыток, retry budget не более 10% от общего числа запросов. Без бюджета ретраи разрастаются в шторм, и вы своими руками создаёте нагрузку, которую потом принимаете за атаку. В кейсе OT Commerce число платных вызовов выросло примерно в 6-7 раз, а загрузка CPU доходила до 98-100% (разбор инцидента).

Идемпотентность: как не создать дубль заказа при повторе

Механика ключа идемпотентности одинакова у большинства API: клиент генерирует ключ один раз на бизнес-операцию, хранит его вместе с телом запроса и при повторе передаёт тот же ключ и то же тело. Сервер по ключу возвращает результат первой операции вместо второй. В Polytranslator рекомендуют отправлять Idempotency-Key с каждым переводом, а при повторной попытке использовать тот же ключ и то же тело запроса; если перевод не удался, зарезервированные кредиты возвращаются (документация Polytranslator).

Проверьте по документации Яндекс Маршрутизации, поддерживаются ли ключи идемпотентности для создающих операций: заказов, сохранённых маршрутов, подписок. Если поддержки нет, закрывайте дедупликацию у себя: таблица с идентификатором бизнес-операции и статусом, проверка перед повтором, уникальный индекс в базе. Срок хранения ключей обычно берут 24 часа: этого хватает, чтобы пережить деградацию провайдера и не потерять защиту от дубля.

Новый ключ при повторе равен новой операции. Это самая дорогая ошибка в retry-логике: она приводит и к дублям заказов, и к двойному расходу квоты.

Exponential backoff с jitter и retry budget: параметры, которые работают

ПараметрЗначениеЗачем нужен
base delay1 сСтартовая пауза после первой ошибки
множитель2Удвоение задержки на каждой попытке
max delay30 сОграничение сверху, чтобы попытки не растянулись на часы
jitter±20%Разводит повторы разных воркеров во времени
max attempts3-5Больше попыток почти не повышает успех при 5xx
retry budget10%Верхняя доля ретраев от общего трафика
circuit breaker: порог5 ошибок подрядСигнал размыкать цепь
circuit breaker: пауза30-60 сВремя на восстановление внешнего сервиса

Jitter обязателен. Без него все воркеры выдержат одинаковую паузу и повторят запросы в одну и ту же секунду, создав пик, после которого придёт 429.

import random, time

BASE, FACTOR, MAX_DELAY, ATTEMPTS, JITTER = 1.0, 2.0, 30.0, 5, 0.2

def call_with_retry(send, retry_after=None):
    for attempt in range(1, ATTEMPTS + 1):
        status, response = send()
        if status < 500 and status != 429:
            return status, response
        if status == 429 and retry_after is not None:
            time.sleep(retry_after)
            continue
        delay = min(MAX_DELAY, BASE * FACTOR ** (attempt - 1))
        time.sleep(delay * (1 + random.uniform(-JITTER, JITTER)))
    return status, response

В коде выше retry_after приходит из заголовка ответа. Для Python подойдёт tenacity, для Go - обёртки с context deadline, для прокси-слоя - политики ретраев Envoy или Istio. Таймаут всего запроса должен быть больше суммы задержек, иначе попытки будут обрываться на середине.

Circuit breaker и graceful degradation при устойчивых 5xx

У размыкателя три состояния: closed (запросы идут), open (запросы не отправляются, клиент сразу получает ошибку или кэш), half-open (пробный запрос проверяет, восстановился ли сервис). Открытая цепь защищает обе стороны: вы не тратите квоту на заведомо неудачные вызовы, провайдер не получает лишнюю нагрузку во время деградации.

Деградация должна быть осмысленной. Если расчёт маршрута недоступен, отдавайте последний успешный результат с пометкой, что сведения могут быть неактуальны, и отключайте некритичные фоновые задачи: пересчёт истории, прогрев кэша, аналитические выгрузки. Готовые конфигурации для размыкателей, таймаутов и идемпотентности в Kubernetes, Docker и Nginx собраны в материале про типичные ошибки проектирования маршрутизации. В кейсе OT Commerce внешний антибот-модуль не помог, потому что проблема была не в защите периметра, а в архитектуре обработки нагрузки.

Логирование запросов к API Яндекс Маршрутизации: схема, поля, примеры

Логировать нужно и ошибки, и успешные запросы: без успешных не с чем сравнивать. Нормальную latency и долю ошибок определяют по базовой линии, а её не построить из одних сбоев. Общие принципы сбора логов и метрик в распределённых системах описаны в статье про наблюдаемость в service mesh, те же правила применимы к клиенту API.

Что логировать обязательно, а что запрещено

Обязательный набор: timestamp с таймзоной, request_id, endpoint, HTTP-статус, error.code, latency_ms, retry_attempt, idempotency_key, размер ответа. Запрещённый набор: API-ключ и токен, персональные сведения, полное тело запроса с адресами и координатами. Формат - structured logs в JSON: их разбирает парсер, а не человек, поэтому error_code должно лежать отдельным полем, а не внутри строки сообщения.

Ротация и срок хранения: 30-90 дней для подробных логов, дольше храните только агрегаты. Координаты хэшируйте или обрезайте до района, если нужна статистика по географии.

Пример structured-лога для запроса маршрута

{"ts":"2026-09-19T10:12:03.441+03:00","request_id":"0b0f...","endpoint":"/v1/route","status":200,"error_code":null,"latency_ms":184,"retry_attempt":0,"idempotency_key":null}
{"ts":"2026-09-19T10:12:06.902+03:00","request_id":"0b0f...","endpoint":"/v1/route","status":429,"error_code":"RATE_LIMIT_EXCEEDED","latency_ms":51,"retry_attempt":2,"idempotency_key":null,"retry_after_s":30}

Разница между записями видна сразу: успешный вызов без ретраев и отказ по лимиту, который уже дважды повторялся. Поле retry_attempt в логе отвечает на главный вопрос разбора инцидента: шторм повторов идёт от вашей логики или трафик вырос по внешним причинам.

Мониторинг и алерты: как узнать о проблеме до жалоб пользователей

Смотреть нужно на четыре метрики: доля ошибок по классам (4xx, 5xx, 429), p95 и p99 latency, доля ретраев, остаток суточной квоты. Каждая по отдельности обманывает. Рост 5xx при стабильной latency бывает следствием сетевых проблем на вашей стороне, а рост latency без ошибок - ранним признаком деградации провайдера. Полный алгоритм расследования по метрикам, логам и профилям описан в статье про мониторинг производительности автоматизированных систем.

Ключевые метрики: error rate, latency, retry rate, остаток квоты

МетрикаЧто показываетПорог для алерта
error rate 5xxДоля ответов сервиса с серверной ошибкойwarning > 1% за 5 минут, critical > 5%
error rate 4xxОшибки запросов, обычно связаны с изменениями в коде или ключахwarning при росте в 2 раза от базовой линии
429 rateДоля отказов по лимитамwarning > 0,5% за 5 минут
p95 и p99 latencyХвостовые задержки расчёта маршрутаwarning при превышении 2x от baseline
retry rateДоля повторов от общего трафикаwarning при превышении retry budget 10%
остаток квотыЗапас до суточного лимитаwarning ниже 20%, critical ниже 5%

Остаток квоты экспортируйте из заголовков ответа: график приближения к нулю позволяет перераспределить нагрузку днём, а не ночью после блокировки.

Примеры алертов и дашборда в Prometheus/Grafana

# рост доли 5xx
sum(rate(routing_requests_total{status=~"5.."}[5m])) / sum(rate(routing_requests_total[5m])) > 0.01

# рост 429
sum(rate(routing_requests_total{status="429"}[5m])) / sum(rate(routing_requests_total[5m])) > 0.005

# падение p95 относительно baseline
histogram_quantile(0.95, sum(rate(routing_latency_seconds_bucket[5m])) by (le)) > 2 * routing_p95_baseline

# приближение к суточной квоте
routing_quota_remaining / routing_quota_limit < 0.2

# аномальный рост QPS
sum(rate(routing_requests_total[5m])) > 3 * avg_over_time(sum(rate(routing_requests_total[5m]))[1h:5m])

Имена метрик здесь условные, замените их на свои. Дашборд соберите в три ряда: сверху error rate и latency, в середине разбивка по error.code и endpoint, снизу остаток квоты и retry rate. SLO привяжите к пользовательским сценариям: доля успешно построенных маршрутов за 5 минут и доля ответов быстрее 2 секунд.

Урок из кейса OT Commerce: загрузка CPU 98-100% и рост вызовов в 6-7 раз не были замечены вовремя, проблема длилась почти три дня. Алерт на аномальный рост QPS и на долю 429 поймал бы это в первые часы.

Разбор реальных сценариев: как выглядит деградация интеграции в проде

Третий частый сценарий - истёкший или отозванный API-ключ. Все запросы падают с 401 и 403, а retry-логика исправно их повторяет: в логах растёт шум, метрики показывают всплеск ошибок, реальная причина тонет в повторах. Лечится это проверкой срока жизни ключа по расписанию и алертом на первую пару 401, а не увеличением числа попыток.

Каскадный сбой из-за неконтролируемых повторов

Цепочка развивается предсказуемо: один воркер получает 503, повторяет запрос без backoff, к нему присоединяются остальные, суммарный QPS растёт в разы, провайдер отвечает 429, клиенты повторяют и это. За несколько минут деградация внешнего сервиса превращается в отказ вашего. Проверьте себя по четырём вопросам: есть ли retry budget, есть ли jitter, есть ли circuit breaker, логируется ли retry_attempt. Три отрицательных ответа из четырёх означают, что следующий инцидент у провайдера станет вашим.

Аномальный трафик и рост платных вызовов

В кейсе с OT Commerce загрузка CPU на сервере магазина временами доходила до 98-100%, а количество платных вызовов API выросло примерно в 6-7 раз; проблема продолжалась почти три дня, и сторонний платный антибот-модуль её не остановил (разбор инцидента). Основная часть проблемного трафика оказалась не классической сетевой DDoS-атакой, а автоматизированными запросами к сайту. Вывод для интеграции с API: алерт на аномальный рост QPS и на долю 429 полезнее, чем надежда на защиту периметра. Полезная метрика - число вызовов API на одного пользователя или на одну бизнес-операцию: её рост показывает, что логика начала дублировать запросы, даже когда внешних атак нет.

Чек-лист устойчивой интеграции и что делать, если ничего не помогло

Чек-лист из 10 пунктов для продакшена

  1. Ключи идемпотентности на все операции, создающие заказы, маршруты и подписки: один ключ на бизнес-операцию, хранение 24 часа.
  2. Retry только для 5xx, сетевых таймаутов и 429 после Retry-After; 4xx не повторяются.
  3. Exponential backoff: base 1 с, множитель 2, максимум 30 с, jitter ±20%, 3-5 попыток.
  4. Retry budget не выше 10% от общего трафика и общий ограничитель частоты для всех воркеров.
  5. Circuit breaker с порогом 5 подряд 5xx и паузой 30-60 секунд, с состоянием half-open.
  6. Structured logs в JSON с request_id, статусом, error_code, latency_ms и retry_attempt.
  7. Метрики error rate, p95 и p99 latency, retry rate, остаток суточной квоты в Prometheus.
  8. Алерты на пороги: 5xx выше 1%, 429 выше 0,5%, p95 выше 2x baseline, квота ниже 20%.
  9. Тестовый контур и sandbox для проверки retry и обработки 429 перед выкаткой в прод.
  10. Регламент разбора инцидентов: кто смотрит request_id, где хранятся логи, как фиксируется причина.

Как правильно обратиться в поддержку: что приложить к тикету

Соберите пакет: request_id проблемного запроса, точное время с таймзоной, endpoint, HTTP-статус, error.code и error.message, частоту воспроизведения, пример запроса без токена и персональных сведений. Без request_id разбор идёт дольше: поддержка не может сопоставить ваш вызов со своими логами.

Пока тикет в работе, включите деградацию: кэш последних успешных маршрутов с пометкой о возможной неактуальности, отключение некритичных фоновых задач, уведомление пользователей о сниженной точности расчёта. Надёжная интеграция отличается предсказуемой реакцией на сбой: понятный код в логе, ограниченный retry, осмысленный ответ пользователю вместо таймаута.

Поделиться:
Сохранить гайд? В закладки браузера