Ошибка 429 Too Many Requests - это единственный код состояния, который про вас, а не про сервер и не про то, что вы запросили. Ничего не сломалось, ничего не пропало, и находиться здесь вам можно. Вы просто просили чаще, чем кто-то счёл разумным, и ответом стал отказ с приложенными к нему часами.

Всё остальное в диапазоне 4xx описывает один запрос: этот был испорчен, у этого не было учётных данных, этот ничего не назвал. Ошибка 429 описывает закономерность. Это сервер говорит о вашем поведении на отрезке времени, и поэтому она может прийти в ответ на совершенно корректный запрос, который десять минут назад сработал бы.

Единственная ошибка, при которой повтор делает хуже

Большинство ошибок вознаграждают повторную попытку. Ошибка 503 часто проходит со второго раза, а при 500 стоит один раз перезагрузить страницу на случай, если вы попали в неудачный момент. Обычно инстинкт прав, а здесь он ровно неправ.

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

Так что ошибка 429 - это тот случай, когда бездействие и есть активный выбор. Переждите окно, а затем сходите ещё раз, один.

Заголовки, которые говорят, когда вернуться

Ошибка 429 обычно несёт Retry-After, где указано либо число секунд, либо дата:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1789412400

Retry-After - стандартный и единственный, на который можно полагаться. Семейство X-RateLimit-* - это соглашение, а не спецификация: широко используется, пишется разными сервисами по-разному, и его стоит читать, когда оно есть, потому что оно рассказывает про форму лимита, а не только про ближайший момент, когда можно постучаться.

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

Когда трафик был не ваш

Неудобство ошибки 429 в том, что считается редко когда человек. Считается IP-адрес, ключ API, аккаунт или сессия, и каждое из этого покрывает не одного вас.

Офис за единственным адресом, кофейня, мобильный оператор с carrier-grade NAT, CI-пайплайн, делящий ключ с командой, коллега, проверяющий скрипт на том же эндпоинте с теми же учётными данными. Всё это порождает ошибку 429 для того, кто не делал ничего необычного, и ничего из этого со страницы ошибки не видно.

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

Вас могут ограничивать, а ошибки 429 вы так и не увидите

Ничто не обязывает сервис брать именно этот код. Многие возвращают 403, чтобы ограничение выглядело решением о правах, некоторые возвращают 503, потому что снаружи “не сейчас” верно в любом случае, а CDN перед приложением часто отвечает своим выбором, а не выбором приложения.

Так что ошибка 429 - в некотором роде хорошая новость: это сервис, говорящий с вами конкретно. Её отсутствие не доказывает, что вас не ограничивают, и если 403 или 503 появляется только под нагрузкой и только с одного адреса, подозревать стоит ограничитель, что бы ни говорил код.

Если это ваш API

Вопрос, на который стоит ответить до всякой настройки, - какой слой считает. Лимит в вашем приложении, лимит в nginx и лимит на CDN - это три разных набора чисел, и тот, в который упираются ваши пользователи, обычно не тот, на который вы смотрели.

Дальше отправляйте заголовки. Ошибка 429 без Retry-After - это сервис, отказывающийся сказать когда, и это оставляет каждому клиенту придумывать backoff самому, а придуманное хуже того числа, которое вы бы им дали. Быть конкретным ничего не стоит.

И следите за тем, что именно считает лимит. Лимиты на IP наказывают офисы и общие сети за поведение одного человека внутри, и это обращение в поддержку, приходящее в виде “ваш сайт сломан для всей нашей команды”. Лимиты на аккаунт или на ключ сложнее реализовать и гораздо легче объяснить.

Почему ошибку 429 трудно восстановить задним числом

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

Session Replay

Бесплатное расширение Chrome. Один клик на странице, которая ведёт себя неправильно, захватывает снимок экрана, консоль и сетевой журнал, и даёт вам ссылку для вставки в задачу.

Получить расширение

Сетевой журнал - это то, что вообще делает ошибку 429 расследуемой: упавший запрос, точная минута и заголовки ответа, говорящие, в какой лимит вы упёрлись и сколько ему оставалось идти. Это разница между отчётом, который можно сопоставить с журналом ограничителя, и сообщением о том, что сайт немного не работал сегодня утром.

В одном абзаце

Ошибка 429 Too Many Requests означает, что вы просили слишком часто, и это делает её единственным кодом состояния о вашем поведении, а не о сервере или ресурсе. Ещё это единственная ошибка, при которой повтор активно вредит, потому что повтор - это ещё один запрос, который ограничитель посчитает: прочитайте Retry-After, подождите, затем сходите один раз. Считаемый трафик часто не ваш, поскольку лимиты вешаются на адреса и ключи, покрывающие целые офисы и команды, а телефон на мобильном интернете скажет вам, с каким из двух вы имеете дело. Если API ваш, выясните, какой слой считает, всегда отправляйте Retry-After и ограничивайте лучше ключ, чем адрес, чтобы один человек не мог запереть снаружи целое здание.