Rate limit в API: что это, какие бывают лимиты и клиент под них
Что значит 429 на самом деле, чем RPM отличается от TPM и burst и как написать клиент, который не упирается в лимиты и не ретраит то, что ретраить бесполезно.
5 мин · · API-ключи · разработка
Rate limit for API — это ограничение на то, как часто и как много вы можете отправлять запросов за единицу времени. Когда вы его превышаете, сервер отвечает кодом 429, и дальше всё зависит от клиента: либо он аккуратно подождёт и повторит, либо засыплет сервер ретраями и усугубит ситуацию. Ниже — какие бывают лимиты у API моделей, как они выглядят через шлюз и как написать клиент на Python, который с ними живёт.
Какие бывают лимиты
У LLM API обычно несколько независимых ограничений, и 429 может прийти по любому из них:
| Лимит | Что считает | Типичный симптом |
|---|---|---|
| RPM (requests per minute) | число запросов в минуту | 429 при серии коротких запросов |
| TPM (tokens per minute) | входные и выходные токены в минуту | 429 при немногих, но «тяжёлых» запросах |
| Одновременные запросы | сколько запросов «в полёте» | 429 при запуске 50 потоков сразу |
| Бюджетный лимит ключа | потраченные доллары за день или месяц | 429 до конца суток или месяца |
Первые три задаёт поставщик модели, причём для разных моделей и разных уровней доступа цифры различаются. Конкретные значения у поставщиков меняются, поэтому в статье их нет: смотрите документацию поставщика.
Burst и средняя скорость: в чём разница
Лимит «120 запросов в минуту» можно понимать двумя способами. Первый: окно в минуту, внутри которого можно потратить все 120 запросов за первые пять секунд. Второй: равномерная скорость, 2 запроса в секунду, без всплесков. Разница называется burst (всплеск).
Часто это схема token bucket: «ведро» вмещает, скажем, 10 запросов (burst), а наполняется со скоростью 2 запроса в секунду. Пока ведро не пустое, вы можете выстрелить пачкой из 10, потом упрётесь в среднюю скорость. Если вы не знаете, как устроен лимит у конкретного поставщика, безопасно держаться средней скорости и считать burst запасом на неровности.
Простая арифметика для планирования: при 120 RPM и burst 10 тысяча запросов займёт не меньше (1000 − 10) / 2 = 495 секунд, то есть около 8 минут. Если ваши запросы весят по 1 500 токенов, то на 120 RPM приходится 120 × 1 500 = 180 000 токенов в минуту, и TPM может сработать раньше RPM.
Как это устроено через шлюз Zukko
У самого Zukko общего rate limit нет. 429 через шлюз приходит в двух случаях:
- Исчерпан дневной или месячный лимит ключа (
key_daily_limit_reachedилиkey_monthly_limit_reached). Вы сами задаёте его в долларах для каждого ключа, подробнее в документации по лимитам ключей и кэшу. - Ограничение у провайдера модели: поставщик ответил 429, и шлюз передаёт это вам.
Различать их важно, потому что реакция противоположная. Лимит поставщика проходит через секунды или минуты, и повтор с паузой помогает. Лимит ключа не пройдёт, пока не наступит новый день или месяц, либо пока вы не поднимете его в кабинете: ретрай здесь только жжёт время. Отличить можно по коду в теле ответа.
Если лимиты ключа — ваш предохранитель от «убежавшего» скрипта, ставьте их заранее, а ретраи пишите только для лимита провайдера. Про ошибку отдельно: что делать с 429.
Клиент с очередью и экспоненциальной задержкой
Ниже асинхронный клиент на Python. Он держит среднюю скорость через token bucket, ограничивает число одновременных запросов семафором, а при 429 от провайдера ждёт 1, 2, 4, 8 секунд и так далее с небольшим случайным добавком (jitter), чтобы параллельные задачи не просыпались одновременно.
import asyncio, os, random, time
from openai import AsyncOpenAI, RateLimitError, APIStatusError, APIConnectionError
client = AsyncOpenAI(
base_url="https://api.zukko.pro/v1",
api_key=os.environ["ZUKKO_API_KEY"],
max_retries=0, # ретраи делаем сами
)
class Limiter:
"""Token bucket: средняя скорость rpm в минуту, всплеск до burst."""
def __init__(self, rpm: int, burst: int):
self.rate = rpm / 60.0
self.capacity = burstЧто здесь важно. Пауза происходит вне семафора, иначе «спящие» задачи занимали бы слоты. Ошибки 400-го диапазона, кроме 429, вылетают сразу: ретраить неверный запрос бессмысленно. Лимитер ставится перед каждой попыткой, включая повторные, потому что повтор тоже расходует запрос. Если в ответе есть заголовок Retry-After, лучше использовать его вместо расчётной паузы; проверьте, приходит ли он у вашей модели.
Практические правила
- Начинайте ниже лимита. Если поставщик разрешает 120 RPM, ставьте 80–100: запас на клиентские часы и чужие запросы с того же ключа.
- Считайте токены, а не только запросы. Тяжёлые запросы упираются в TPM; заранее оценить размер можно через count_tokens или по длине текста.
- Ограничьте параллелизм. Часто достаточно 4–8 одновременных запросов, чтобы выйти на нужную скорость.
- Логируйте причину 429. Сохраняйте тело ответа: по нему видно, лимит ключа это или поставщика.
- Не ретрайте бесконечно. Потолок попыток и общий таймаут задачи обязательны.
Для больших пакетов смотрите разбор массовой обработки без упора в лимит.
Частые вопросы
Есть ли у Zukko общий rate limit?
Нет, своего общего ограничения скорости нет. 429 приходит либо из-за дневного или месячного лимита вашего ключа, либо потому, что ответил 429 провайдер модели.
Чем RPM отличается от TPM?
RPM считает запросы, TPM считает токены, входные и выходные вместе. Много мелких запросов упираются в RPM, несколько огромных — в TPM.
Нужно ли ретраить 429?
Если это лимит провайдера, да, с экспоненциальной паузой и jitter. Если лимит ключа (key_daily_limit_reached, key_monthly_limit_reached), нет: поднимите лимит в кабинете или дождитесь нового периода.
Как не получить 429 от своего же ключа?
Задайте дневной и месячный лимит в долларах под реальный бюджет задачи и следите за расходом в разделе «Использование». Создать ключ и выставить лимиты можно в кабинете.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Свой прокси к нейросети на Node.js: не показывать API-ключ во фронтендеНебольшой сервер на Express между вашим фронтендом и API нейросети: ключ остаётся на сервере, а модели, длина запроса и частота ограничены.
- DeepSeek API на Python: запросы, стриминг, JSON и обработка ошибокГотовый код для DeepSeek V4.1 Flash и V4 Pro на OpenAI SDK: запрос, стриминг, разбор JSON и аккуратная обработка ошибок.
- Gemini API на Python: запросы через OpenAI SDK и OpenAI-форматРабочий пример Gemini API на Python без отдельной библиотеки: меняем base_url в OpenAI SDK, добавляем стриминг и JSON и сравниваем с google-genai.
- Anthropic SDK для Python: base_url, ключ и первый запрос через шлюзОфициальный пакет anthropic для Python нужно только направить на другой адрес. Разбираем base_url, ключ, стриминг, подсчёт токенов, кэш и ошибки.