go2llm API documentation

Balance and spending

Your balance, charges, key quotas and request history live in the dashboard. The amount of a specific charge is also returned directly in the model's response: usage.cost_usd carries the USD cost of the request. The field is added both to regular JSON responses and to the final usage block of a stream.

What is checked before a request

  • the key exists and is active;
  • the key has not expired;
  • the model is allowed for the key;
  • the key quota and the available balance cover the maximum estimate for the request.

The gateway reserves the estimated cost before calling the provider, then charges the actual cost and releases the rest of the reservation.

For image generation, usage.images holds the number of results and usage.cost_usd the final charge. Repeating a successful image request with the same Idempotency-Key returns the same usage without charging again.

If you get insufficient_balance, top up the balance. If you get quota exceeded, raise the key's quota or lower max_tokens.

How an agent reads its own budget with GET /v1/me

It is no use to an autonomous agent to learn about an empty balance from a 402 halfway through a long run. GET /v1/me returns the remaining balance and the quota for the very key the agent uses to call models.

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 }
}

The route takes the same headers as the rest: Authorization: Bearer YOUR_API_KEY or x-api-key: YOUR_API_KEY. It has its own limit of 60 RPM per key, and the response comes with cache-control: no-store, so caching it on your side is a deliberate decision.

FieldMeaning
key.maskedthe masked form of the key; the full secret is never returned
key.quota_usdthe key's quota in USD; 0 means "no separate quota"
key.spent_usdspent through this key
key.rpm_limitrequests per minute allowed for the key, 20 by default
key.allowed_modelsthe list of allowed models; ["*"] means all published ones
key.expires_atthe key's expiry as an ISO-8601 string ("2027-01-02T03:04:05.000Z") or null
balance.spendable_usdavailable to spend right now
balance.reserved_usdheld against requests currently in flight
limits.account_max_concurrencyconcurrent requests per account, 2 by default

expires_at is ISO-8601 rather than unix time on purpose: this response is read by a third-party agent, not by our own frontend. The concurrency limit is the same for a key with an account and for a key without one.

spendable_usd and reserved_usd are different things

spendable_usd is what you can spend right now. That is the number to compare against your estimate of the upcoming work.

reserved_usd is money already held against requests that are running at this moment. The reservation is made at the maximum estimate before the provider is called and is released after the actual charge. So a non-zero reserved_usd is normal under concurrency, and that is also why the balance can look fine and a 402 still arrives: more is held against current requests than the remainder suggests. There is no need to add spendable_usd and reserved_usd together to get a "real balance" — only the first one is spendable.

A check before a long run

import os
import sys

import httpx

BASE_URL = "https://go2llm.tech"
HEADERS = {"Authorization": f"Bearer {os.environ['GO2LLM_API_KEY']}"}

# A rough upper bound: how many requests, and at what maximum price.
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:
    # The key is not attached to an account: spending is capped by the key quota alone.
    remaining = me["key"]["quota_usd"] - me["key"]["spent_usd"]
else:
    remaining = balance["spendable_usd"]

if remaining < ESTIMATED_COST_USD:
    print(f"Stopping: ${remaining:.2f} available, ${ESTIMATED_COST_USD:.2f} needed")
    sys.exit(1)

print(f"Budget is there: ${remaining:.2f}")

Check the key quota alongside the balance: a key with a quota hits it before the account's overall balance runs out. A key with quota 0 has no separate cap — its spending is limited only by the balance. A key with no account attached returns "balance": null.

What the response does not contain

The route reads exactly what you need to plan spending, and nothing beyond it:

Not returnedWhy
account email, name and ida leaked key must not turn into an identification of its owner
the account's other keysotherwise one leaked key opens all the rest
referral data, payments, promo grantsnot needed for the work, and they widen the blast radius of a leak
the full key secretonly the masked form in key.masked

The split of the balance into cash, promo and frozen grants is not published either: that is internal mechanics, it will change, and building agent logic on it is a bad idea. Only spendable_usd and reserved_usd go outside.

There is no privilege escalation here: the holder of a key can already spend the balance, and reading the remainder is strictly less than what they can already do.

The 402 and 429 codes are explained on the API errors page, the full route list is in API endpoints, and the rates are on the pricing and billing page. Get a key and make your first request at go2llm.tech.