Почему по торговому центру#
Ключ выдаётся сотруднику, и у одного ТЦ таких сотрудников может быть пятеро. Если бы счётчик жил на ключе, ТЦ получал бы пятикратную частоту, за которую не платил. Поэтому запросы, адресованные конкретному торговому центру, считаются в общее ведро этого ТЦ.
Практическое следствие: заводить дополнительные ключи, чтобы ускорить выгрузку, бесполезно. Если лимита не хватает, нужен другой тариф ТЦ.
Величины#
| Что | В минуту | В сутки |
|---|---|---|
| Запросы к ТЦ на тарифе Basic | 60 | 5 000 |
| Запросы к ТЦ на тарифе PRO | 300 | 50 000 |
Запросы вне ТЦ (/shopping_centers) | 60 | 5 000 |
Выдача токенов /auth/token | 10 с одного IP | — |
| Запросы без валидного токена | 30 с одного IP | — |
Запросы к разным торговым центрам считаются независимо: исчерпанный лимит по одному ТЦ не мешает работать с другими.
Заголовки#
На успешных ответах приходят:
| Заголовок | Значение |
|---|---|
X-RateLimit-Limit | Лимит минутного окна |
X-RateLimit-Remaining | Сколько осталось в текущей минуте |
X-RateLimit-Reset | Unix-время конца текущей минуты |
Заголовки отражают только минутное окно. Остаток суточного лимита наружу не отдаётся. И заголовков не будет на ответах, отклонённых до обработки, например на 401 или 403.
Ответ 429#
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1785000060{
"success": false,
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded"
}
}Retry-After содержит секунды до конца исчерпанного окна. Для минутного лимита
это от 1 до 60 секунд, для суточного может быть до конца суток.
Как не упираться#
Уважать Retry-After. Простой цикл с ожиданием ровно этого времени решает проблему
целиком; экспоненциальный backoff нужен только для 503 и 500.
Забирать пачками. Один запрос за месяц по дням лучше тридцати запросов по дню.
Максимальный per_page у большинства методов измеряется сотнями строк.
Кэшировать справочники. Список ТЦ, список точек, зоны подсчёта и настройки товарооборота меняются редко. Дёргать их перед каждой выгрузкой не нужно.
Не запрашивать токен на каждый вызов. Токен живёт час, этого хватает на тысячи запросов.
while True:
resp = session.get(url, headers=headers, params=params)
if resp.status_code != 429:
break
time.sleep(int(resp.headers.get("Retry-After", 60)))Про параллельность#
Лимит в 60 или 300 запросов в минуту не означает, что их стоит слать разом. Права на
каждый запрос проверяются во внешнем сервисе, и он плохо переносит нагрузку пачками:
при 16 одновременных запросах доля отказов 503 rights_unavailable доходила до 16 %,
при четырёх — до 2 %, при последовательной выгрузке отказов не было.
Держите 2–4 одновременных запроса и повторяйте 503 с задержкой. Скорость от этого
не пострадает: выгрузку ограничивает минутный лимит, а не число потоков.
Отдельно про надёжность#
Лимитер намеренно работает «в пользу клиента»: если наше хранилище счётчиков недоступно, запросы проходят, а не отклоняются. Проверка прав ведёт себя ровно наоборот и при недоступности отказывает. Разница осмысленна: лимитер бережёт нашу инфраструктуру, права берегут данные клиентов.