Перейти к содержимому

Кончился free-tier провайдера

Когда перебираешь провайдеров на этапе прототипа, у каждого свои триальные кредиты — и заканчиваются они молча. Внешне это выглядит как «модель вдруг перестала отвечать»: код падает в insufficient_quota или 402 Payment Required, а вы полчаса думаете, что сломали промпт.

Отделите эту ошибку от обычных сбоев и пришлите push ровно один раз — с именем провайдера, у которого кончилось.

Ловим именно «кончилась квота», а не любую ошибку

Заголовок раздела «Ловим именно «кончилась квота», а не любую ошибку»

Важно не путать «кончились кредиты» (лечится пополнением) с 429 (лечится ожиданием). У провайдеров это разные коды/типы — проверяем точно.

import os, requests
import openai, anthropic
NOTIFLY_URL = os.environ["NOTIFLY_URL"]
NOTIFLY_TOKEN = os.environ["NOTIFLY_TOKEN"]
def notify(title, msg, prio):
requests.post(f"{NOTIFLY_URL}/message",
params={"token": NOTIFLY_TOKEN},
json={"title": title, "message": msg, "priority": prio},
timeout=5)
def _alerted_once(provider):
"""Флаг на провайдера: алёрт про кончившийся триал шлём один раз."""
flag = f"/tmp/free-tier-{provider}.flag"
if os.path.exists(flag):
return True
open(flag, "w").close()
return False
def is_quota_exhausted(exc):
"""True только для «кончились кредиты/квота», не для rate-limit."""
# OpenAI: 429 c code == "insufficient_quota" (да, тот же статус, что и rate-limit)
if isinstance(exc, openai.RateLimitError):
code = (getattr(exc, "code", None)
or (exc.body or {}).get("code") if getattr(exc, "body", None) else None)
return code == "insufficient_quota"
# Anthropic и многие OpenAI-совместимые API: 402 Payment Required
status = getattr(exc, "status_code", None)
return status == 402
def guard(provider, call):
"""Выполнить вызов; при исчерпании квоты — push один раз и проброс."""
try:
return call()
except Exception as e:
if is_quota_exhausted(e) and not _alerted_once(provider):
notify(
f"🪙 {provider}: кончился free-tier",
f"Ошибка квоты у {provider}. Пополните счёт или переключите провайдера.",
priority=7,
)
raise
# использование
oai = openai.OpenAI()
resp = guard("openai", lambda: oai.chat.completions.create(
model="gpt-4o-mini", messages=[{"role": "user", "content": "ping"}]))

insufficient_quota у OpenAI приезжает под тем же статусом 429, что и rate-limit, — поэтому недостаточно смотреть на HTTP-код, нужен именно code. Flag-файл на провайдера гарантирует, что при пополнении вы не получите один и тот же алёрт сотню раз подряд.

Если вы гоняете один и тот же промпт по нескольким API сразу, оберните каждый своим guard(provider, ...) — и первый же push скажет, у кого именно кончилось, без раскопок в логах. При пополнении удалите flag-файл (rm /tmp/free-tier-openai.flag), чтобы алёрт снова стал возможен.

  • имя провайдера и модель;
  • точный код ошибки (insufficient_quota / 402) — чтобы отличить от 429;
  • прямую ссылку на страницу billing этого провайдера;
  • напоминание удалить flag-файл после пополнения.