Основы
Пределы частоты
Пределы защищают каналы от потока сообщений, а сервер от перегрузки. Устроены они так же, как в Discord. discord.py и discord.js следят за ними сами, ждут, когда нужно, и повторяют запрос. Если вы пишете без библиотеки, прочитайте эту страницу целиком.
Как считаются пределы
У каждого действия есть своё «ведро» запросов. В ведро помещается несколько запросов подряд, а потом оно постепенно наполняется снова. Например, предел «5 за 2 секунды» значит, что 5 сообщений можно отправить сразу, а дальше по одному каждые 0,4 секунды.
Один запрос может тратить сразу несколько вёдер. Сообщение вебхука тратит и ведро самого вебхука, и общее ведро канала. Запрос проходит, только если место есть во всех. Запрос, упёршийся в предел, ничего не сделал, поэтому его можно спокойно повторить.
Пределы вебхука
| Что | Предел | Ведро |
|---|---|---|
| Отправка и правка сообщений одним вебхуком | 5 за 2 секунды | webhook.execute |
| Сообщения всех вебхуков одного канала вместе | 30 в минуту | webhook.place |
| Чтение, изменение и удаление самого вебхука и чтение и удаление его сообщений | 10 за 30 секунд | webhook.manage |
| Запросы с неверным ключом вебхука с одного IP-адреса | 30 в минуту | webhook.bad_token |
Предел на канал общий для всех его вебхуков. Если в канал пишут несколько программ, вместе они не отправят больше, чем 30 в минуту.
Заголовки ответа
В каждом ответе есть заголовки с остатком самого тесного из вёдер, которые потратил запрос.
| Заголовок | Значение |
|---|---|
X-RateLimit-Limit | Сколько запросов помещается в ведро |
X-RateLimit-Remaining | Сколько запросов можно сделать прямо сейчас |
X-RateLimit-Reset-After | Через сколько секунд ведро наполнится полностью, дробное число |
X-RateLimit-Reset | То же самое моментом времени, в секундах Unix |
X-RateLimit-Bucket | Имя ведра. У ручек с общим пределом оно одинаковое |
HTTP/1.1 204 No Content
X-RateLimit-Limit: 5
X-RateLimit-Remaining: 3
X-RateLimit-Reset-After: 0.800
X-RateLimit-Reset: 1793176200.800
X-RateLimit-Bucket: webhook.executeКогда X-RateLimit-Remaining равен 0, следующий запрос в это ведро получит отказ. Лучше подождать X-RateLimit-Reset-After секунд заранее, а не дожидаться отказа.
Ответ 429
Если предел исчерпан, сервер отвечает статусом 429 Too Many Requests.
{
"code": 0,
"message": "Слишком часто — подождите 2 с",
"retry_after": 1.6,
"global": false
}| Поле | Что значит |
|---|---|
retry_after | Сколько секунд подождать перед повтором. Дробное число |
global | true, если исчерпан общий предел вызывающего, а не предел этого действия |
Заголовки этого ответа такие же, плюс Retry-After (то же ожидание целыми секундами с округлением вверх), X-RateLimit-Scope и X-RateLimit-Global.
Как обработать 429 без библиотеки
Подождите ровно retry_after секунд и повторите тот же запрос. Не повторяйте раньше, такой запрос тоже упрётся в предел.
import time
import requests
def send(url, payload, attempts=5):
for _ in range(attempts):
response = requests.post(url, json=payload)
if response.status_code != 429:
response.raise_for_status()
return response
time.sleep(response.json()["retry_after"])
raise RuntimeError("Не удалось отправить: сервер просит подождать слишком часто")Если программе нужно отправить много сообщений подряд, например длинный лог, лучше собрать их в одно сообщение или в несколько карточек одного сообщения. Это быстрее и не засоряет канал.
Защита от потока запросов
Помимо пределов API сервер отсекает слишком частые запросы с одного IP-адреса ещё до того, как они дойдут до API. Обычная программа в эту защиту не упирается. Если она всё же сработала, придёт тот же ответ 429, но с "global": true и без заголовков X-RateLimit-*. Подождите retry_after секунд и уменьшите частоту запросов.