Ограничение скорости API — заголовки, экспоненциальный откат и выживание перед 429
Вы получили код 429. API сообщает вам о необходимости снижения скорости. Вот как расшифровать заголовки X-RateLimit-*, понять значение Retry-After и реализовать экспоненциальный откат с добавлением случайного сдвига, чтобы ваши интеграции корректно обрабатывали ограничения скорости, а не наносили удары по серверу.
Вы получили код 429. Возможно, ваш обработчик веб-хука перестал работать. Возможно, батч-задача была проигнорирована. API вернул сообщение «Слишком много запросов» и поток ответных заголовков, которые вы, вероятно, пропустили.
Эти заголовки содержат всю информацию. Вот как их читать и как написать логику повторных попыток, которая не усугубляет проблему.
Заголовки, которые действительно важны
Большинство API, ограничивающих количество запросов, возвращают какие-то из этих заголовков на каждом ответе — не только при коде 429:
- X-RateLimit-Limit — общее количество раз, которое вы можете сделать в текущем окне. REST-интерфейс GitHub предоставляет авторизованным пользователям 5 000 запросов в час; неавторизованные запросы ограничены 60.
- X-RateLimit-Remaining — количество оставшихся запросов в текущем окне. Когда этот показатель достигает нуля, следующий запрос возвращает код 429.
- X-RateLimit-Reset — время сброса окна, в формате Unix-эпохи. Это тот заголовок, который чаще всего игнорируют разработчики, и при этом наиболее полезный.
- X-RateLimit-Used (специфичный для GitHub) — количество уже использованных запросов. Отражает
Limit - Remainingно полезен для проверки состояния. - Retry-After — появляется только в ответах с кодом 429. Может быть указано как количество секунд ожидания или как строка даты в формате HTTP. Если API отправляет этот заголовок, используйте его — он точнее, чем любое вычисление, которое вы можете сделать самостоятельно.
Реальные заголовки ответа GitHub выглядят так:
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4823
X-RateLimit-Reset: 1716998400
X-RateLimit-Used: 177
X-RateLimit-Resource: core
The X-RateLimit-Resource заголовок специфичен для GitHub: они поддерживают отдельные пулы квот для основного REST-интерфейса, поиска и GraphQL. Использование квоты поиска (ограничено до 30 запросов в минуту) не влияет на основные квоты — и наоборот.
Stripe отличается
от X-RateLimit-* названия. Их заголовки имеют другой префикс:
Stripe-Ratelimit-Limit: 100
Stripe-Ratelimit-Remaining: 97
Stripe-Ratelimit-Reset: 1716998460
И на ответе с кодом 429:
Retry-After: 30
по умолчанию у Stripe ограничение составляет 100 активных запросов в секунду, а не в час. Это важно, чем кажется: цикл импорта 500 клиентов может исчерпать этот диапазон менее чем за 5 секунд, если вы не ограничиваете запросы на вашем конце.
Stripe также различает ограничения по количеству запросов и ограничения по конкретным ресурсам (например, создание слишком большого количества клиентов в коротком промежутке времени). В теле ответа с кодом 429 указано, какой именно лимит был превышен — всегда логируйте полное тело ответа, а не только код статуса.
Декодирование времени сброса
The X-RateLimit-Reset значение — это Unix-эпоха. 1716998400 показывает ничего несущественное на первый взгляд, но его легко декодировать: используйте Конвертер временных меток Unix для преобразования в понятную дату UTC и определения, насколько далеко до сброса.
В коде: reset_time - time.now() показывает количество секунд до сброса. Но проверьте X-RateLimit-Remaining сначала — если у вас ещё есть квота, то ничего не нужно ждать.
Что сообщает тело ответа с кодом 429
Код 429 сам по себе недостаточен. Тело ответа обычно указывает, какой именно лимит был превышен:
GitHub:
{
"message": "API rate limit exceeded for user ID 12345.",
"documentation_url": "https://docs.github.com/rest/overview/rate-limits"
}
Stripe:
{
"error": {
"code": "rate_limit",
"message": "Too many requests hit the API too quickly.",
"type": "invalid_request_error"
}
}
OpenAI идёт дальше: в сообщении об ошибке указано, какой именно лимит был превышен — либо по токенам в минуту, либо по запросам в минуту, что кардинально меняет стратегию повторных попыток. Всегда логируйте полное тело ответа с кодом 429.
Экспоненциальный откат с дрожанием
Простое решение: перехватить код 429, поставить паузу на 1 секунду и повторить запрос. Это не сработает по двум причинам:
- Если у вас несколько рабочих процессов, которые обращаются к одному и тому же конечному пункту, они все будут ждать 1 секунду и повторять запрос одновременно — это синхронизированный поток повторных попыток, который воссоздаёт саму проблему.
- 1 секунда бесполезна, если вы исчерпали квоту на час или на день. Вы просто соберёте ещё 3600 ошибок 429.
Правильный подход — экспоненциальный откат с дрожанием: каждая попытка ждёт дольше предыдущей, с случайным компонентом, чтобы разнести попытки одновременных рабочих процессов.
import time
import random
import requests
def fetch_with_backoff(url, headers, max_retries=5):
base_delay = 1 # seconds
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code != 429:
return response
# Prefer Retry-After if the API provides it
retry_after = response.headers.get("Retry-After")
if retry_after:
wait = int(retry_after)
else:
# Fall back to X-RateLimit-Reset
reset = response.headers.get("X-RateLimit-Reset")
if reset:
wait = max(0, int(reset) - int(time.time()))
else:
# Pure exponential backoff with full jitter
cap = 60 # max wait: 60s
wait = random.uniform(0, min(cap, base_delay * (2 ** attempt)))
print(f"Rate limited. Attempt {attempt + 1}/{max_retries}. Waiting {wait:.1f}s")
time.sleep(wait)
raise Exception(f"Max retries exceeded after {max_retries} attempts")
Приоритет в этом реализации умышленно установлен следующим образом:
- Retry-After первым — если API указывает, сколько секунд нужно ждать, используйте это. Не угадывайте с собственным расчётом.
- X-RateLimit-Reset как альтернатива — рассчитайте реальное количество секунд до сброса, а не предполагайте фиксированный интервал.
- Полное дрожание как последний вариант —
random.uniform(0, cap)распределяет попытки по всему интервалу отката. В блоге по архитектуре AWS описано это как «полное дрожание» и показано, что оно значительно снижает коллизии на сервере по сравнению с равномерным дрожанием или отсутствием дрожания вовсе. max(0, ...)при сбросе — время сброса может быть в прошлом к моменту, когда вы производите расчёты. Защититесь от отрицательного значения времени ожидания, которое может вызвать сбой вашего обработчика.
Общие ошибки
Рассматривание ошибок, не являющихся 429, как ошибок лимита запросов. Код 503 — это ошибка сервера. Код 401 означает, что ваши данные неверны. Проверьте status_code == 429 до применения логики повторных попыток по лимиту запросов.
Поглощение ошибки 429 и возврат пустых данных. Скрытые сбои сложнее отладить, чем исключения, которые были выброшены. Показывайте ошибку.
Использование фиксированного интервала. Если вы исчерпали квоту на час, и у вас осталось 47 минут, то ожидание 5 секунд не принесёт вам ничего. Рассчитывайте на основе времени сброса.
Повторные попытки без предела. Установите max_retries установите лимит и увеличьте после того, как квота исчерпана. Некоторые ошибки 429 указывают на исчерпание квоты, которое не восстанавливается до следующего периода оплаты — бесконечный цикл повторных попыток — это баг.
Не отслеживание X-RateLimit-Remaining заранее. Если Remaining падает ниже 10% из Limit, начните распределять запросы до того, как вы достигнете нуля. Большинство SDK не делают это автоматически. Стоимость — несколько миллисекунд дополнительной задержки; выгоды — вы не увидите ошибки 429 вообще.
Подводя итог
Ошибка 429 — это не одноразовая проблема, которую можно исправить и забыть. Это повторяющаяся ограничение, и игнорирование заголовков, которые с ней идут, означает, что вы будете постоянно сталкиваться с тем же барьером. Используйте Retry-After при наличии в API. Рассчитывайте на основе X-RateLimit-Reset при отсутствии. Добавьте дрожание, чтобы повторные попытки не синхронизировались. Установите лимит, чтобы бесконечные циклы повторных попыток не превращались в инциденты в продакшене.
И когда вы смотрите на X-RateLimit-Reset: 1716998400 и спрашиваете, когда это на самом деле происходит — Конвертер временных меток Unix покажет это за один клик.
Установите наши расширения
Добавьте инструменты ввода-вывода в свой любимый браузер для мгновенного доступа и более быстрого поиска
恵 Табло результатов прибыло!
Табло результатов — это интересный способ следить за вашими играми, все данные хранятся в вашем браузере. Скоро появятся новые функции!
Подписаться на новости
все Новые поступления
всеОбновлять: Наш последний инструмент было добавлено 18 Июня, 2026
