AIKraft Agents
Главная/Блог/Гайд/Как победить ошибки 429: Полное…
Гайд12 мин чтения · 11 ноября 2026 г.

Как победить ошибки 429: Полное руководство по лимитам LLM API

Разбираем причины ошибок 429 Too Many Requests в LLM API, учимся читать заголовки, настраивать умные ретраи и использовать fallback-модели для стабильной работы приложений.

Как победить ошибки 429: Полное руководство по лимитам LLM API

Вы запускаете свой первый серьезный пайплайн обработки данных или массово генерируете контент, и вдруг — тишина. Или, что еще хуже, поток ошибок. В консоли или логах приложения всплывает красная строка: 429 Too Many Requests. Это не баг вашего кода и не сбой сервера в привычном понимании. Это жесткое напоминание о том, что вы превысили допустимую скорость запросов к модели искусственного интеллекта. Для разработчиков, работающих с OpenAI, Anthropic или агрегаторами вроде OpenRouter, ошибка 429 становится первым серьезным препятствием на пути от прототипа к продакшену.

Большинство новичков реагируют на это стандартно: добавляют цикл повторных попыток (retry loop) с небольшой задержкой. Но на практике это часто не работает. В нашем эксперименте, где мы отправили пакет из 60 запросов через стандартный Python SDK OpenAI, 23 запроса все равно упали с ошибкой, несмотря на встроенные механизмы повторных попыток. Причина проста: стандартные библиотеки часто не учитывают специфические заголовки времени сброса лимитов, которые возвращают провайдеры, и просто «бьются головой о стену» в течение нескольких секунд, прежде чем сдаться.

В этой статье мы не просто объясним, что такое rate limits. Мы разберем реальные кейсы, покажем, как правильно интерпретировать заголовки ответов, и предложим три надежных стратегии решения проблемы: умный ретрай с учетом времени сброса, переключение на резервные модели (fallback) и использование нескольких провайдеров. Мы протестировали каждый метод на API OpenRouter, и вы увидите точные цифры, время выполнения и стоимость решений.

01Что такое ошибка 429 и как она выглядит изнутри?

Ошибка 429 Too Many Requests означает, что вы отправили больше запросов или токенов, чем разрешено вашим лимитом в текущем временном окне. Это может произойти с OpenAI, Anthropic и любыми другими провайдерами. Чаще всего эта ошибка впервые проявляется, когда ваше приложение переходит от тестового режима к реальной нагрузке или когда пакетная задача (batch job) разветвляется на несколько рабочих потоков (workers).

Давайте посмотрим на конкретный пример. Мы отправили 40 запросов одновременно к бесплатной модели poolside/laguna-s-2.1:free, у которой установлен лимит в 20 запросов в минуту. Результат был предсказуемым, но информативным: 38 запросов успешно выполнились, а два вернули код 429 от провайдера, обслуживающего модель.

Важно понимать, что разные источники лимитов генерируют разные структуры ошибок. Ошибка от самого OpenRouter (например, превышение дневного лимита бесплатных моделей) будет отличаться от ошибки от upstream-провайдера (того, кто фактически хостит модель). В первом случае вы увидите метаданные, указывающие на openrouter_free_tier_per_minute, а во втором — upstream_provider_shared_pool.

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

  • X-RateLimit-Limit: Максимальное количество запросов, разрешенных в окне.
  • X-RateLimit-Remaining: Сколько запросов осталось до исчерпания лимита.
  • X-RateLimit-Reset: Самое раннее время, когда повторная попытка может увенчаться успехом. Это Unix-временная метка в миллисекундах.

Тело ответа также содержит поле limit_source, которое говорит вам, чье именно ограничение вы нарушили, и remedy_hint — подсказку для человека, читающего логи. Однако в коде никогда не парсите текст подсказки; всегда проверяйте поле limit_source.

02Почему лимиты срабатывают на разном количестве запросов?

Вы можете задаться вопросом: если лимит составляет 20 запросов в минуту, почему в нашем тесте успешно прошли 38 из 40? Это не баг, а особенность работы алгоритмов ограничения скорости (rate limiting algorithms).

Большинство современных систем используют алгоритм "Token Bucket" (ведро с токенами) или аналогичные механизмы, которые пополняются непрерывно, а не скачкообразно в начале каждой минуты. Кроме того, система может принять запросы, которые поступают в один и тот же момент времени, до того, как счетчик успеет обновиться. Например, Anthropic использует именно такой подход.

OpenAI также может вернуть ошибку 429 с кодом slow_down, даже если вы не превысили свой общий лимит в минуту. Это происходит, когда ваш трафик нарастает слишком быстро. Система видит резкий всплеск и просит вас немного замедлиться, чтобы избежать перегрузки инфраструктуры.

Мы провели дополнительные тесты, отправив 161 запрос в разных паттернах, чтобы понять, как работает лимит бесплатных моделей OpenRouter. Результаты показали:

  • Последовательные запросы после двух минут простоя: 30 успешных запросов.
  • Последовательные запросы в минуту, следующую за предыдущей: 30 успешных запросов.
  • 30 одновременных запросов: 29 успешных запросов.

Вывод прост: относитесь к числу 20 не как к жесткой границе, а как к порогу, после которого вероятность получения ошибки 429 резко возрастает. Также важно знать, что OpenRouter применяет единый лимит в минуту ко всем бесплатным моделям аккаунта. Если вы превысите лимит для одной бесплатной модели, вы не сможете отправить запрос и к другой бесплатной модели в этот же момент — обе вернут 429.

03Шаг 1: Определите, чей лимит вы нарушили

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

Вот таблица, которая поможет вам быстро сориентироваться:

💡
Важно различать типы лимитов. Ошибки 429 могут быть вызваны не только скоростью запросов, но и финансовыми ограничениями. OpenAI и Anthropic возвращают 429, когда вы достигаете месячного лимита расходов. Повторные попытки здесь бессмысленны до сброса лимита. В OpenRouter финансовые проблемы (нехватка кредитов) обычно возвращают код 402, а не 429.
limit_source Статус Чей лимит Решение
openrouter_free_tier_per_minute 429 Лимит бесплатных моделей OpenRouter Ждать X-RateLimit-Reset (Шаг 2) или перейти на платную модель (Шаг 3)
upstream_provider_shared_pool 429 Провайдер, хостящий модель Использовать другого провайдера (Шаг 4) или fallback-модель (Шаг 3)
openrouter_in_flight_budget 402 Бюджет активных запросов Ждать Retry-After и повторить попытку
openrouter_key_limit, openrouter_credits 402 Лимит ключа или баланс Повысить лимит ключа или пополнить баланс

Если вы получили ошибку upstream_provider_shared_pool, это означает, что провайдер, обслуживающий модель, исчерпал свои ресурсы. Такие провайдеры редко сообщают точное время восстановления, поэтому заголовки сброса (reset headers) в таких ответах часто отсутствуют. В таких случаях лучшее решение — переключиться на другого провайдера или другую модель.

Как победить ошибки 429: Полное руководство по лимитам LLM API

04Шаг 2: Умный ретрай с учетом времени сброса

Стандартные Python SDK для OpenAI и Anthropic по умолчанию пытаются повторить запрос дважды при ошибке 429. Задержка начинается с 0.5 секунд и никогда не превышает 8 секунд. Они учитывают заголовок Retry-After, если сервер его отправляет. Однако, как мы видели выше, OpenRouter часто отправляет X-RateLimit-Reset вместо Retry-After, который стандартные SDK игнорируют.

В результате все повторные попытки попадают в одно и то же полное окно лимита, и запросы продолжают падать. Чтобы исправить это, нужно написать собственную функцию задержки, которая будет читать заголовок X-RateLimit-Reset и ждать ровно до того момента, когда лимит обновится.

Вот пример реализации на Python, который мы использовали для тестирования:

terminalpython
import email.utils
import os
import random
import time
from concurrent.futures import ThreadPoolExecutor
import requests

URL = "https://openrouter.ai/api/v1/chat/completions"
HEADERS = {
    "Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"
}

def retry_delay(headers, attempt):
    """Секунды ожидания после 429: Retry-After, затем X-RateLimit-Reset, затем backoff."""
    retry_after = headers.get("Retry-After")
    if retry_after:
        if retry_after.replace(".", "").isdigit():
            return float(retry_after)
        when = email.utils.parsedate_to_datetime(retry_after).timestamp()
        return max(0.0, when - time.time())
    
    reset = headers.get("X-RateLimit-Reset") # Unix time in milliseconds
    if reset:
        # Разносим клиентов на несколько секунд после сброса, чтобы они не пытались одновременно
        return max(0.0, int(reset) / 1000 - time.time() + random.uniform(1, 5))
    
    # Экспоненциальная задержка с джиттером
    return min(2 ** attempt, 60) + random.uniform(0.5, 1)

def chat(body, max_attempts):
    for attempt in range(max_attempts):
        r = requests.post(URL, headers=HEADERS, json=body, timeout=120)
        source = None
        if r.status_code in [402, 429]:
            source = r.json()["error"].get("metadata", {}).get("limit_source")
            
        if source == "openrouter_free_tier_daily":
            # Сбрасывается в полночь по UTC, поэтому не ждем до этого времени
            r.raise_for_status()
            
        # 402 от бюджета активных запросов очищается после Retry-After; другие 402 требуют кредитов.
        if r.status_code != 429 and source != "openrouter_in_flight_budget":
            r.raise_for_status()
            
        return r.json()
        
        if attempt == max_attempts - 1:
            break # Исчерпали попытки, не ждем перед сбоем
            
        wait = retry_delay(r.headers, attempt)
        print(f"{r.status_code} ({source}), attempt {attempt + 1}: waiting {wait:.1f}s")
        time.sleep(wait)
        
    r.raise_for_status()

if __name__ == "__main__":
    def job(i):
        return chat({
            "model": "poolside/laguna-s-2.1:free",
            "messages": [{"role": "user", "content": "Reply with the number ."}],
            "max_tokens": 16
        })

    t0 = time.time()
    with ThreadPoolExecutor(max_workers=10) as pool:
        results = list(pool.map(job, range(60)))
    print(f"{len(results)} of 60 completed in {time.time() - t0:.0f}s")

Результат этого подхода поразителен. В то время как стандартный SDK оставил 23 неудачных запроса из 60, наш кастомный ретрай успешно выполнил все 60 запросов. Однако цена этого успеха — время. Пакет занял от 89 до 92 секунд, в то время как стандартный SDK справился за 12 секунд (но с большим количеством ошибок). Функция retry_delay сначала проверяет Retry-After, затем X-RateLimit-Reset, и только затем использует экспоненциальную задержку. Случайное добавление 1-5 секунд (jitter) предотвращает ситуацию, когда все 10 рабочих потоков пытаются сделать запрос в один и тот же момент после сброса лимита.

Схема работы квантования моделей для локального запуска.
Схема работы квантования моделей для локального запуска.

05Шаг 3: Отправляйте избыточный трафик на fallback-модель

Если ответ от второй модели допустим для вашего приложения, самый эффективный способ избежать задержек — это настроить fallback. Вместо того чтобы ждать, пока сбросится лимит первой модели, вы можете мгновенно переключиться на другую модель, которая еще не исчерпала свои ресурсы.

В нашем тесте мы использовали ту же модель, но с включенным механизмом fallback. Если основной провайдер возвращал 429, система автоматически пробовала другую модель или того же провайдера с другими настройками. Результат был впечатляющим: все 60 запросов были выполнены всего за 13 секунд, а стоимость составила ничтожные $0.000066. Никакого ожидания, никаких таймаутов.

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

06Шаг 4: Позвольте другому провайдеру обслуживать ту же модель

OpenRouter и другие агрегаторы позволяют вам не только переключаться между моделями, но и между провайдерами, которые хостят одну и ту же модель. Если модель google/gemma-4-31b-it:free возвращает ошибку upstream_provider_shared_pool, это значит, что конкретный провайдер (например, Google Cloud) перегружен. Но тот же самый вес модели может быть доступен у другого провайдера, например, DeepInfra или Together AI.

В нашем эксперименте, когда бесплатная версия Gemma 4 31B была временно ограничена на стороне провайдера, тот же самый запрос с включенным fallback успешно выполнился через DeepInfra за $0.0000037. Это демонстрирует мощь агрегаторов: они абстрагируют вас от проблем конкретного провайдера.

Мы также протестировали сценарий с 150 запросами, закрепленными за одним провайдером без failover. В каждом запуске до 32 запросов падали. Включив failover (переключение на других провайдеров), мы достигли 100% успешных запросов во всех тестах. Это критически важно для продакшен-систем, где надежность важнее экономии на копейки.

Темная тема с визуализацией процессов квантования.
Темная тема с визуализацией процессов квантования.

07Как избежать ошибок 429 вообще?

Хотя исправление ошибок важно, лучшая стратегия — их предотвращение. Вот несколько рекомендаций для архитекторов систем с LLM:

  1. Используйте очередь запросов (Queueing). Вместо того чтобы отправлять все запросы одновременно, используйте очередь (например, Redis или RabbitMQ) и ограничьте скорость потребления (consumption rate) на уровне, безопасном для лимитов провайдера. Это сгладит пики нагрузки.
  2. Мониторинг заголовков. Всегда парсите X-RateLimit-Remaining в ваших ответах. Если значение близко к нулю, замедляйте отправку новых запросов заранее, не дожидаясь ошибки 429.
  3. Кэширование. Если вы часто запрашиваете одни и те же данные, кэшируйте ответы. Это не только сэкономит деньги, но и снизит нагрузку на лимиты API.
  4. Локальная запуск моделей. Для очень чувствительных к задержкам или частым задачам рассмотрите возможность запуска квантованных локальных моделей (например, через Ollama или LM Studio). Это полностью избавит вас от проблем с сетевыми лимитами.
⚠️
Осторожно с кэшированием. При кэшировании ответов LLM помните, что модели могут быть недетерминированными. Даже с одним и тем же промптом ответ может немного отличаться. Используйте кэширование только для задач, где точность до символа не критична, или кэшируйте по хэшу промпта и параметров.
Сравнение подходов: MCP, CLI и Skills в контексте управления запросами.
Сравнение подходов: MCP, CLI и Skills в контексте управления запросами.

08Часто задаваемые вопросы

Что делать, если я достиг месячного лимита расходов?

Если вы получили ошибку 429, связанную с финансовым лимитом (часто это видно по коду ошибки или сообщению), повторные попытки не помогут. Вам нужно либо пополнить баланс, либо подождать, пока лимит сбросится в начале следующего месяца. В OpenRouter финансовые ограничения обычно возвращают код 402.

Почему мой код работает локально, но падает в продакшене?

Локально вы, скорее всего, отправляете запросы последовательно или с большими задержками. В продакшене, особенно при использовании многопоточности (threading/multiprocessing), количество запросов в секунду может резко возрасти, мгновенно исчерпывая лимиты. Всегда тестируйте нагрузку с использованием пулов потоков или очередей.

Можно ли увеличить лимиты для бесплатных моделей?

Обычно нет. Лимиты для бесплатных моделей (free-tier) являются жесткими и предназначены для тестирования. Для production-нагрузки необходимо использовать платные модели и ключи API, которые имеют значительно более высокие лимиты.

09Что это значит на практике

Ошибка 429 — это не тупик, а часть экосистемы работы с LLM API. Понимание того, как работают лимиты, какие заголовки возвращают провайдеры и как правильно реагировать на превышение скорости, превращает эту проблему из критического сбоя в управляемый процесс.

Наше исследование показало, что:

  • Стандартные SDK часто недостаточно умны для работы с кастомными заголовками сброса лимитов (как X-RateLimit-Reset у OpenRouter).
  • Написание собственного механизма ретрая с учетом времени сброса гарантирует 100% успешное выполнение пакетных задач, но требует времени.
  • Использование fallback-моделей и множественных провайдеров (через агрегаторы) — это самый быстрый и экономичный способ обеспечить отказоустойчивость, особенно для задач, не требующих строгой идентичности модели.

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

📌
Итоговый чек-лист. 1. Проверяйте limit_source в ошибках. 2. Используйте X-RateLimit-Reset для ожидания. 3. Настраивайте fallback-модели. 4. Мониторьте X-RateLimit-Remaining для превентивного замедления.

Источник: OpenRouter ↗