Баланс и расходы
Баланс, списания, квоты ключей и история запросов доступны в кабинете. Сумма конкретного списания также возвращается прямо в ответе модели: usage.cost_usd содержит стоимость запроса в USD. Поле добавляется и в обычные JSON-ответы, и в финальный usage-блок потоковой выдачи.
Что проверяется до запроса
- ключ существует и активен;
- срок действия ключа не истёк;
- модель разрешена ключу;
- квота ключа и доступный баланс покрывают максимальную оценку запроса.
Шлюз резервирует оценочную стоимость до обращения к поставщику, затем списывает фактическую стоимость и освобождает остаток резерва.
Для генерации изображений usage.images содержит количество результатов, а usage.cost_usd — итоговое списание. Повтор успешного image-запроса с тем же Idempotency-Key возвращает то же usage без повторного списания.
Если вы получили insufficient_balance, пополните баланс. Если получили quota exceeded, увеличьте квоту ключа или уменьшите max_tokens.
Как агенту прочитать свой бюджет через GET /v1/me
Автономному агенту бесполезно узнавать о пустом балансе из 402 на середине длинного прогона. GET /v1/me отдаёт остаток и квоту по тому же ключу, которым агент ходит в модель.
curl https://go2llm.tech/v1/me \
-H "Authorization: Bearer YOUR_API_KEY"
{
"object": "api_key",
"key": {
"name": "prod",
"masked": "gk_sk_abcd…wxyz",
"quota_usd": 50,
"spent_usd": 12.34,
"rpm_limit": 20,
"allowed_models": ["*"],
"expires_at": null
},
"balance": {
"spendable_usd": 87.66,
"reserved_usd": 0.12,
"currency": "USD"
},
"limits": { "account_max_concurrency": 2 }
}
Маршрут принимает те же заголовки, что остальные: Authorization: Bearer YOUR_API_KEY или x-api-key: YOUR_API_KEY. Отдельный лимит — 60 RPM на ключ, ответ приходит с cache-control: no-store, так что кэшировать его на своей стороне надо осознанно.
| Поле | Значение |
|---|---|
key.masked | маскированный вид ключа, полный секрет не отдаётся |
key.quota_usd | квота ключа в USD; 0 означает «отдельной квоты нет» |
key.spent_usd | израсходовано по этому ключу |
key.rpm_limit | лимит запросов в минуту на ключ, по умолчанию 20 |
key.allowed_models | список разрешённых моделей; ["*"] — все опубликованные |
key.expires_at | срок действия ключа строкой ISO-8601 ("2027-01-02T03:04:05.000Z") или null |
balance.spendable_usd | доступно к трате прямо сейчас |
balance.reserved_usd | удержано под выполняющиеся запросы |
limits.account_max_concurrency | параллельных запросов на аккаунт, по умолчанию 2 |
expires_at — именно ISO-8601, а не unix-время: этот ответ читает сторонний агент, а не наш фронтенд. Лимит параллельности одинаков и для ключа с аккаунтом, и для ключа без него.
spendable_usd и reserved_usd — разные вещи
spendable_usd — то, что вы можете потратить прямо сейчас. Именно эту цифру сравнивайте с оценкой стоимости предстоящей работы.
reserved_usd — деньги, уже удержанные под запросы, которые прямо сейчас выполняются. Резерв ставится по максимальной оценке до вызова поставщика и освобождается после фактического списания. Поэтому нормально видеть ненулевой reserved_usd при параллельных запросах, и поэтому же баланс «вроде есть», а 402 всё равно приходит: под текущие запросы удержано больше, чем видно по остатку. Складывать spendable_usd и reserved_usd, чтобы получить «настоящий баланс», не нужно — тратить можно только первое.
Проверка перед длинным прогоном
import os
import sys
import httpx
BASE_URL = "https://go2llm.tech"
HEADERS = {"Authorization": f"Bearer {os.environ['GO2LLM_API_KEY']}"}
# Грубая оценка сверху: сколько запросов и по какой максимальной цене.
ESTIMATED_COST_USD = 4.0
me = httpx.get(f"{BASE_URL}/v1/me", headers=HEADERS, timeout=30).json()
balance = me["balance"]
if balance is None:
# Ключ не привязан к аккаунту: расход ограничен только квотой ключа.
remaining = me["key"]["quota_usd"] - me["key"]["spent_usd"]
else:
remaining = balance["spendable_usd"]
if remaining < ESTIMATED_COST_USD:
print(f"Останавливаюсь: доступно ${remaining:.2f}, нужно ${ESTIMATED_COST_USD:.2f}")
sys.exit(1)
print(f"Бюджет есть: ${remaining:.2f}")
Квоту ключа стоит проверять вместе с балансом: ключ с квотой упрётся в неё раньше, чем кончится общий баланс аккаунта. Ключ с квотой 0 отдельного лимита не имеет — его расход ограничен только балансом. Ключ без привязанного аккаунта отдаёт "balance": null.
Чего в ответе нет
Маршрут читает ровно то, что нужно для планирования расходов, и ничего сверх:
| Не отдаётся | Почему |
|---|---|
| email, имя и id аккаунта | утечка ключа не должна превращаться в опознание владельца |
| другие ключи аккаунта | иначе один утёкший ключ открывает все остальные |
| реферальные данные, платежи, промо-гранты | для работы не нужны, а ущерб от утечки расширяют |
| полный секрет ключа | только маскированный вид в key.masked |
Разбивка баланса на cash, промо и замороженные гранты тоже не публикуется: это внутренняя механика, она будет меняться, и строить на ней логику агента не стоит. Наружу идут только spendable_usd и reserved_usd.
Эскалации доступа здесь нет: держатель ключа и так может тратить баланс, а читать остаток — строго меньше того, что он уже умеет.
Разбор кодов 402 и 429 — на странице Ошибки API, полный список маршрутов — в Маршрутах API, тарифы — на странице цен и биллинга. Ключ и первый запрос — на go2llm.tech.