API Reference

Get remaining credits

Two numbers for the account behind the key: everything credited to it and everything it has spent. Any key on the account reads the same pair.

GET/api/v1/credits

The key in the header is the whole request: there are no path, query or body parameters, and no way to ask for a period — both numbers cover the entire life of the account. The call itself costs nothing and works at any balance, including zero.

Response

Fields of the 200 response
FieldTypeDescription
data.total_creditsnumberrequiredEverything credited to the account, in US dollars: the current balance plus everything already charged. It is not a sum of your transfers — a correction made on our side moves it too, and the service fee on a top-up never enters it.
data.total_usagenumberrequiredEverything charged to the account, in US dollars, summed from its request log — the same amounts you see per request in usage.cost and in the account area.

Scroll the table sideways for the description.

200 application/json
{
  "data": {
    "total_credits": 250.0,
    "total_usage": 173.418244
  }
}

Both are lifetime totals carried to six decimal places, and neither is windowed: nothing resets at midnight, at the start of a month or thirty days back. total_usage only ever grows; total_credits goes down only if a balance correction is made, or for as long as a request is in flight (below). For spend inside the current day, week or month, and for any ceiling set on the account, read the key route instead.

Spend per day, week and month

Remaining balance

There is no remaining field. Subtract: total_credits - total_usage is the money left to spend, and it is the same figure the account area shows. In the example above that is 76.581756.

shell
curl -s https://api.gtokens.io/api/v1/credits \
  -H "Authorization: Bearer sk-sl-your-key" \
  | jq '.data.total_credits - .data.total_usage'
main.py
import requests

r = requests.get(
    "https://api.gtokens.io/api/v1/credits",
    headers={"Authorization": "Bearer sk-sl-your-key"},
    timeout=30,
)
r.raise_for_status()
data = r.json()["data"]

remaining = data["total_credits"] - data["total_usage"]
if remaining < 1.0:          # stop before requests start coming back 402
    raise SystemExit(f"balance is down to {remaining:.2f}")
main.ts
const r = await fetch('https://api.gtokens.io/api/v1/credits', {
  headers: { Authorization: 'Bearer sk-sl-your-key' },
})
if (!r.ok) throw new Error(`credits: ${r.status}`)

const { data } = await r.json()
const remaining = data.total_credits - data.total_usage
if (remaining < 1) throw new Error(`balance is down to ${remaining.toFixed(2)}`)

Both fields arrive as JSON doubles, so the difference can come out with a floating tail such as 76.58175600000001. Compare it against a threshold rather than testing for zero, and stop a batch while the threshold is still above the price of one request: once the balance cannot cover a request, that request comes back 402 payment_required and is not charged.

During a request the difference reads low

Before a request goes upstream we hold an estimate of its price against the balance, and the charge reaches total_usage only when the request settles. So while requests are in flight the difference is smaller than the money you actually have — never larger. The held estimate returns the moment the request finishes, whether it succeeded or failed, and a hold left behind by a request that never finished is returned on its own.

Errors

Every error arrives in the same envelope: the HTTP status repeated in error.code, a message meant for a human, and the code to branch on in error.metadata.error_type.

401 application/json
{
  "error": {
    "code": 401,
    "message": "missing bearer token",
    "metadata": {
      "error_type": "authentication",
      "reason": "invalid_credentials"
    }
  }
}
Statuses this route returns
StatusWhen it happenserror_type
401No Authorization header, a header that is not a bearer token, or a key that is unknown or revoked.authentication
403The key is valid, but the account is suspended. Reissuing the key changes nothing here.permission_denied
500Our side failed. The body carries no detail beyond this; the request is safe to repeat.server

Scroll the table horizontally for the typed code.

Every status and typed code

Authorization

The key goes in as a bearer token, here as on every other route:

Authorization: Bearer sk-sl-…

The two numbers belong to the account, not to the key that asked for them: keys of one account share one balance, so all of them return the same pair.

How keys are issued and used