API key controls
When you mint a key you can scope it: cap its monthly spend, restrict it to a set of models, and give it an expiry. These are enforced on every inference call. A key created without any of them behaves exactly as it always has — unlimited, all models, no expiry.
Creating a scoped key
POST /v1/keys accepts these optional fields alongside name:
spend_limit_usd— monthly cap in whole USDallowed_models— an allowlist of Sable model idsexpires_in_days— days until the key stops workingrate_limit_per_min— this key's own request/minute limit (see Rate limits); omit to use the global default
curl https://api.buildsable.com/v1/keys \
-H "Authorization: Bearer $SABLE_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "trading-agent-prod",
"spend_limit_usd": 50,
"allowed_models": ["sable-llama-3.3-70b", "sable-deepseek-v3"],
"expires_in_days": 90
}'The plaintext key is returned exactly once at creation — store it immediately. See Authentication.
Enforcement
The controls are checked at inference time on /v1/chat/completions and
/v1/embeddings:
| Condition | Status | Error type |
| --- | --- | --- |
| Monthly spend would exceed spend_limit_usd | 402 | spend_limit_exceeded |
| Requested model not in allowed_models | 403 | scope_denied |
| Call made after the key's expiry | 401 | — |
Spend is measured against the same metered cost that appears in each receipt and rolls up in usage, so a key's cap and your account totals always agree.
# HTTP 402 Payment Required
{
"error": {
"type": "spend_limit_exceeded",
"message": "monthly spend limit reached for this key"
}
}The spend cap is a monthly window. A key that hits its limit resumes at
the start of the next month; revoke and re-mint if you need it sooner, or
raise the cap on a fresh key. Expiry is one-way — an expired key returns
401 and can't be reactivated.