Документация API go2llm

429 rate_limit_error: лимиты, Retry-After и правильный backoff

Ошибка 429 означает, что запросов пришло больше, чем разрешено за окно времени. go2llm возвращает 429 с кодом rate_limit при превышении 20 запросов в минуту на ключ и с кодом quota при исчерпании долларовой квоты ключа. Anthropic API отвечает rate_limit_error, OpenAI — rate_limit_exceeded или insufficient_quota. Первые два кода лечатся ожиданием, quota и insufficient_quota — нет.

Страница составлена 2026-09-11, обновлена 2026-09-11.

Как выглядит ошибка

go2llm при превышении RPM отдаёт статус 429, заголовок retry-after в секундах и такое тело:

{
  "error": {
    "type": "rate_limit",
    "code": "rate_limit",
    "message": "api key RPM exceeded",
    "request_id": "gw_1a7d3e50c9b84f26ae1b7c04d5f38e92"
  }
}

Отдельный случай — перегрузка самого шлюза, а не вашего ключа:

{ "error": { "type": "rate_limit", "code": "rate_limit", "message": "gateway capacity exceeded" } }

Официальный Anthropic API на том же превышении возвращает другой формат:

{
  "type": "error",
  "error": {
    "type": "rate_limit_error",
    "message": "Number of request tokens has exceeded your per-minute rate limit"
  }
}

OpenAI API при исчерпании оплаченных кредитов возвращает 429 с кодом insufficient_quota и текстом You exceeded your current quota, please check your plan and billing details. Это не временная ошибка: ожидание её не лечит, нужно пополнение.

Воспроизвести 429 на go2llm можно 25 параллельными запросами при лимите 20 RPM:

seq 25 | xargs -P 25 -I{} curl -s -o /dev/null -w "%{http_code}\n" \
  https://go2llm.tech/v1/chat/completions \
  -H "Authorization: Bearer $GO2LLM_API_KEY" \
  -H "content-type: application/json" \
  -d '{"model":"gpt-5.6-luna","max_tokens":8,"messages":[{"role":"user","content":"1"}]}'

Причины 429

1. Превышен лимит 20 RPM на ключ

go2llm по умолчанию выставляет каждому ключу лимит 20 запросов в минуту. Двадцать первый запрос в том же минутном окне получает 429 rate_limit с сообщением api key RPM exceeded и заголовком retry-after. Лимит считается по ключу, а не по аккаунту: два ключа дают 40 RPM суммарно.

2. Исчерпана долларовая квота ключа

Каждому ключу go2llm можно задать квоту в долларах. При её исчерпании шлюз возвращает 429 с кодом quota и сообщением quota exceeded. Ожидание тут не помогает — квота не восстанавливается сама. Ключ с квотой 0 считается ключом без отдельного лимита: расход ограничен только балансом аккаунта. Ключ, созданный в кабинете с пустым полем квоты, идёт без отдельного лимита.

3. Перегружен сам шлюз

Сообщение gateway capacity exceeded — это защита от одновременных тяжёлых запросов, а не ваш персональный лимит. go2llm отдаёт retry-after, и повтор через указанное число секунд обычно проходит.

4. Параллельные воркеры без общего счётчика

Восемь процессов Celery, каждый со своим клиентом и своей логикой ретраев, суммарно легко выходят за 20 RPM, хотя ни один из них по отдельности лимит не нарушает. Счётчик на стороне go2llm один на ключ, счётчиков на стороне ваших воркеров — восемь.

5. Ретрай без jitter усиливает проблему

Классический sleep(2 ** attempt) без случайной добавки синхронизирует все упавшие запросы: они возвращаются ровно через 1, 2, 4 и 8 секунд одной волной и снова получают 429. Это называется thundering herd и превращает одну секунду перегрузки в минуту.

6. У вендора лимит считается в токенах, а не в запросах

Anthropic API и OpenAI API ограничивают не только RPM, но и TPM — токены в минуту. Один запрос с контекстом на 200 000 токенов может исчерпать минутный лимит в одиночку, хотя RPM при этом равен единице. Точные значения RPM и TPM по tier'ам мы не приводим: их публикуют и меняют сами вендоры, сверяйтесь с их документацией по лимитам. В опубликованных лимитах go2llm TPM не фигурирует: на ключе настраиваются квота в долларах, список моделей, IP allow-list, срок действия и лимит 20 RPM по умолчанию.

Как починить по шагам

Шаг 1. Прочитайте error.code. rate_limit — ждите. quota — поднимите квоту ключа в кабинете или создайте второй ключ, ожидание бесполезно.

Шаг 2. Прочитайте заголовок retry-after. go2llm отдаёт его в секундах на всех 429. Если заголовок есть — используйте его значение, а не свою формулу.

Шаг 3. Внедрите экспоненциальный backoff с jitter. Рабочая реализация:

import random, time
import httpx

def go2llm_chat(payload, api_key, max_attempts=6, base=1.0, cap=32.0):
    """POST /v1/chat/completions с backoff на 429/502/503 и уважением Retry-After."""
    with httpx.Client(timeout=120.0) as client:
        for attempt in range(max_attempts):
            r = client.post(
                "https://go2llm.tech/v1/chat/completions",
                headers={"Authorization": f"Bearer {api_key}"},
                json=payload,
            )
            if r.status_code < 400:
                return r.json()

            err = r.json().get("error", {})
            code = err.get("code") or err.get("type")

            # quota и insufficient_quota ожиданием не лечатся
            if r.status_code == 429 and code == "quota":
                raise RuntimeError(f"Квота ключа исчерпана: {err.get('message')}")
            if r.status_code not in (429, 502, 503) or attempt == max_attempts - 1:
                raise RuntimeError(f"{r.status_code} {code}: {err.get('message')} "
                                   f"[request_id={err.get('request_id')}]")

            hint = r.headers.get("retry-after")
            delay = float(hint) if hint and hint.isdigit() else min(cap, base * 2 ** attempt)
            time.sleep(delay * (0.5 + random.random() / 2))  # jitter 50–100%

Шесть попыток с базой 1 секунда и потолком 32 секунды дают максимум около 47 секунд ожидания — достаточно, чтобы пережить минутное окно RPM почти целиком.

Шаг 4. Ограничьте параллелизм на своей стороне. При лимите 20 RPM и средней длительности запроса 3 секунды безопасный потолок — примерно 1 одновременный запрос на каждые 3 секунды, то есть семафор на 4–6 воркеров, а не на 25.

Как избежать 429 в будущем

Разведите нагрузку по ключам: отдельный ключ go2llm на прод, на стейдж и на CI даёт каждому свои 20 RPM и изолирует всплески. Поставьте общий семафор или token bucket в приложении — счётчик на клиенте дешевле, чем 429 и ретрай на сервере. Переведите пакетную работу на дешёвые модели: gpt-5.6-luna стоит $0.04 за 1M входных токенов и $0.24 за выход, glm-5.3-flash — $0.018 и $0.06, поэтому размазать нагрузку по времени на них дешевле, чем упираться в лимит на claude-opus-5 за $0.50/$2.51.

Отдельно про стрим: повторять SSE-поток после получения первого валидного события нельзя. go2llm может подобрать совместимый fallback-маршрут только до первого байта ответа; после этого переключение не выполняется, и ретрай на клиенте даст дублирующий ответ и двойное списание.

Связанные ошибки

FAQ

Чем rate_limit отличается от quota в ответе go2llm? rate_limit — превышение 20 запросов в минуту, проходит само через секунды, ретрай уместен. quota — исчерпание долларовой квоты конкретного ключа, само не восстанавливается: нужно поднять квоту в кабинете или выпустить новый ключ.

Заголовок Retry-After всегда приходит? go2llm ставит retry-after в секундах на статусах 429 и 503. Если заголовок отсутствует — используйте формулу min(32, 2 ** attempt) с jitter 50–100%.

Как повысить лимит 20 RPM? Лимит настраивается на уровне ключа в кабинете go2llm. Простой способ поднять суммарную пропускную способность без изменения настроек — завести несколько ключей и распределить воркеры между ними.

Списываются ли деньги за запрос, который вернул 429? Нет. go2llm резервирует стоимость до вызова модели и освобождает резерв, если запрос отклонён на лимите. Баланс не уходит ниже нуля, а фактическая стоимость успешного запроса приходит в usage.cost_usd.


Получить ключ и сделать первый запрос за 3 минуты → go2llm.tech. Подключение — в документации, цены всех моделей каталога — на странице тарифов.