Cost & metering
Every inference is metered. Pricing is published per model, the exact cost
of each call rides along in its signed receipt, and your
account totals roll up in GET /v1/usage, all in metadata, none of it
touching prompt or completion content.
Per-model pricing
GET /v1/models advertises two price fields per model, both in USD per
1M tokens:
prompt_usd_per_mtok: price of input tokenscompletion_usd_per_mtok: price of output tokens
curl https://api.buildsable.com/v1/models \
-H "Authorization: Bearer $SABLE_API_KEY"
# {
# "data": [
# {
# "id": "sable-llama-3.3-70b",
# "prompt_usd_per_mtok": 0.12,
# "completion_usd_per_mtok": 0.30,
# ...
# }
# ]
# }Per-request cost
Each call's metered cost is reported as cost_micro_usd (millionths of
a dollar) inside the signed receipt. A cost of 270 means $0.000270.
Using micro-USD keeps every figure an exact integer, with no
floating-point rounding between the gateway and your books. The cost is
prompt_tokens × prompt_usd_per_mtok + completion_tokens × completion_usd_per_mtok
(the per-Mtok price is also the per-token micro-USD rate), so at the prices
above 1,000 prompt + 500 completion tokens cost 1000×0.12 + 500×0.30 = 270.
{
"model": "sable-llama-3.3-70b",
"prompt_tokens": 1000,
"completion_tokens": 500,
"total_tokens": 1500,
"cost_micro_usd": 270,
"logging": "metadata-only"
}
Because the cost lives in a signed receipt, it's tamper-evident: the same secp256k1 signature that proves the inference happened also proves what it cost. See Verifiable receipts.
Token counts come straight from the upstream's usage block. On the rare
streamed response where an upstream omits it, Sable estimates the counts
locally so the call still meters instead of recording zero. It's an approximation,
and only ever a fallback when there's no upstream count to use.
Prompt-cache passthrough
When an upstream serves part of your prompt from its prompt cache, it reports the
number of cached tokens (OpenAI as prompt_tokens_details.cached_tokens,
Anthropic as cache_read_input_tokens). Sable bills those cached prompt tokens at
a discounted rate, passing the cache saving through to you rather than charging
every input token at full price.
You do not opt in or change anything: send the same repeated system prompt or context an agent loop reuses, and if the upstream caches it, your bill drops automatically. The receipt makes it explicit and verifiable:
"cost_micro_usd": 240,
"cached_tokens": 500,
"cache_savings_micro_usd": 30
cached_tokens is how many prompt tokens hit the cache, and
cache_savings_micro_usd is what that saved you on this call versus full-rate
input. Both are omitted when there was no cache hit, so an uncached request is
billed and receipted exactly as before. This is metadata only, in keeping with
the privacy contract: Sable reads the cached count the upstream
reports and never caches or stores any prompt content itself.
Reserved credit while a request is in flight
Credit is checked before a request reaches an upstream, and at that moment the real cost isn't known yet. So the gateway reserves the worst case for the life of the request and releases it the moment the request settles. Spendable credit is your balance minus everything currently reserved.
- A sandbox reserves every second of its
timeout_secs, on every vCPU and every MB of memory it asked for, the most that run could possibly cost. - Inference reserves an estimate derived from the request body and
max_tokens.
A reservation is not a charge. Nothing moves in the ledger until the request
settles, and what's then debited is the metered cost, usually much less than
what was held. Reservations are released as each request finishes, whatever its
outcome, and one left behind by a request that crashed is swept automatically.
They never touch the ledger, but both balance endpoints report them:
GET /v1/billing/balance and GET /v1/credit return held_micro_usd,
spendable_micro_usd, and spendable_usd alongside the raw balance.
Spendable is the balance minus credit reserved by in-flight holds, and it is
the number that decides whether the next request is authorized.
This is what stops many simultaneous requests from all passing a credit check that only one of them can pay for: a balance that covers one call authorizes one call, not a hundred.
The consequence for a caller is that a 402 can now describe a temporary state
rather than an empty balance:
{
"error": {
"message": "account has no spendable credit (balance $0.4200, $0.4200 reserved by requests already in flight). Top up with USDT, or retry when those finish.",
"type": "insufficient_credit"
},
"x402Version": 1
}
When the message names credit reserved by requests already in flight, the
balance is real and simply committed elsewhere; retrying once those requests
finish succeeds without paying anything more. A plain out-of-credit 402 says
so instead, and both carry the x402 accepts terms so an agent can settle and
retry; see Paying with USDT.
Account totals
GET /v1/usage aggregates spend across your account. The response carries
total_cost_micro_usd for the period, and each request entry includes its
own cost_micro_usd, so you can reconcile line by line.
import requests
usage = requests.get(
"https://api.buildsable.com/v1/usage",
headers={"Authorization": f"Bearer {SABLE_SESSION_TOKEN}"},
).json()
dollars = usage["total_cost_micro_usd"] / 1_000_000
print(f"Spend this period: ${dollars:.6f}")/v1/usage is session-gated. It uses the dashboard bearer session
(sess_…), not an sk-sable_… key. To cap what a single key can spend, set
a spend_limit_usd when you mint it; see
API key controls.
The raw usage ledger
For reconciliation beyond the aggregate, GET /v1/usage/events
(session-gated) returns the raw usage ledger, filterable by since, until,
key_id, kind, and status. Results paginate with a keyset cursor (pass
back the response's next_cursor to continue), and ?format=csv exports
the same rows for a spreadsheet or your books. Like everything metered, the
rows are metadata only: model, tokens, cost, latency, status, never content.