Ошибки и ретраи

Единый формат, коды, стратегия повторов

Ошибки всегда возвращаются с HTTP-статусом и единым JSON-телом. Сохраняйте request_id — он поможет нам и вам найти запрос в логах.

Формат ошибки

json
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Please retry after 2 seconds.",
    "type": "rate_limit_error",
    "request_id": "sr_req_7k3m9x2p",
    "param": null,
    "details": []
  }
}

Справочник кодов

invalid_request400retry: noОтсутствуют поля, неверные типы, слишком длинный prompt
invalid_api_key401retry: noНет/невалидный/отозванный/просроченный ключ
forbidden403retry: noДоступ запрещён — чужая организация или недостаточно прав
not_found404retry: noМодель или ресурс не найдены
content_filter400retry: noЗапрос/ответ заблокирован фильтром безопасности
rate_limited429retry: yes (backoff)RPM или квота кредитов исчерпана
insufficient_credits402retry: noНулевой баланс, пополните счёт
provider_error502retry: yesОшибка на стороне upstream-провайдера
timeout504retry: yesПровайдер не ответил в разумное время
service_unavailable503retry: yesВременная недоступность — повторите позже

Стратегия ретраев

Повторяйте только безопасные коды (429, 502, 503, 504) с экспоненциальным backoff и джиттером.

python
import time, random
from openai import OpenAI, APIError, RateLimitError

client = OpenAI(base_url="https://api.kodikrouter.ru/v1", api_key=...)

def chat_with_retry(messages, model, max_attempts=5):
    for attempt in range(max_attempts):
        try:
            return client.chat.completions.create(model=model, messages=messages)
        except RateLimitError as e:
            wait = min(2 ** attempt, 30) + random.random()
            time.sleep(wait)
        except APIError as e:
            if e.status_code in (502, 503, 504) and attempt < max_attempts - 1:
                time.sleep(min(2 ** attempt, 30) + random.random())
                continue
            raise
    raise RuntimeError("exhausted retries")

Идемпотентность

Для гарантированно-однократной записи (например, списания) отправляйте заголовок Idempotency-Key: <uuid>. Повторный запрос с тем же ключом вернёт тот же ответ без дополнительной тарификации.

http
POST /v1/chat/completions HTTP/1.1
Authorization: Bearer sk-sr_live_...
Content-Type: application/json
Idempotency-Key: 9a1b2c3d-4e5f-6789-abcd-ef0123456789
Клиенты openai и anthropic SDK автоматически добавляют экспоненциальный backoff и Idempotency-Key. Если используете их — ретраи уже в комплекте.