Budgets and Access Policies
Cap spend per day, week or month, and limit the models, providers and products a workspace or key can use.
What Guardrails Do
Guardrails stop a request before it costs anything, on every product: language models, embeddings, speech, transcription, realtime audio, images, video, OCR, reranking and moderation.
- Budgets cap spend per UTC day, week (from Monday) or month, for a workspace and for each API key. They sit beside a key's lifetime Total Spend Limit.
- Access policies allow or block models, providers and products, for a workspace and for each API key.
A workspace's guardrails apply to every key in it. A key's own guardrails apply on top, so a key can only be held to less than its workspace. Workspace owners and admins set workspace guardrails; anyone who can edit keys sets a key's.
Set Budgets
In the dashboard, open Settings and fill in Daily (USD), Weekly (USD) or Monthly (USD) under Guardrails. Each shows what has been spent this period and when it resets. For one key, open API Keys, choose the key and set Daily Budget, Weekly Budget or Monthly Budget. Leave a field blank for no limit.
With the API, update the workspace's guardrails with a dashboard session token (sess.*) from signing in. A number sets a period's limit in USD, null removes it, and periods you leave out stay as they are:
export AUDIOLAB_SESSION="YOUR_SESSION_TOKEN"
curl -X PATCH "$AUDIOLAB_API_URL/v1/workspaces/ws_YOUR_WORKSPACE/guardrails" \
-H "Authorization: Bearer $AUDIOLAB_SESSION" \
-H "Content-Type: application/json" \
-d '{"budgets": {"daily": 25, "monthly": 500}}'A key takes the same budgets object when you create or update it.
How spend counts against a budget:
- Each request holds its most it can cost when it starts, and the hold counts until the request settles at its real cost. A request is refused when its hold would take spend past any budget, so several requests running at once can never overshoot one.
- Spend counts in the period its request started, even if it settles after the reset.
- Lowering a budget below what has been spent stops new requests, and a language request already running makes no further attempt. Its spend so far still settles.
- Refunds and released holds come off the period they were counted in.
Restrict Models, Providers and Products
An access policy has six lists. An empty allowed list allows everything, and a block always wins:
| Field | Holds |
|---|---|
allowed_models, blocked_models | Model IDs, or a prefix ending in *, such as anthropic/* or openai/gpt-5* |
allowed_providers, blocked_providers | Provider names, such as openai, anthropic, google or deepinfra |
allowed_products, blocked_products | llm, embeddings, transcription, speech, speech-to-speech, images, videos, ocr, rerank, moderation, inference |
A model matches through any of its IDs, so blocking openai/gpt-oss-20b also blocks every host that serves it, and deepinfra/* blocks only DeepInfra's copies. In the dashboard, the Access Policy section under Guardrails has a switch per product and a field for each model and provider list.
This policy lets the workspace use Anthropic models and the GPT-5 family, never xAI, and no video:
curl -X PATCH "$AUDIOLAB_API_URL/v1/workspaces/ws_YOUR_WORKSPACE/guardrails" \
-H "Authorization: Bearer $AUDIOLAB_SESSION" \
-H "Content-Type: application/json" \
-d '{"access_policy": {"allowed_models": ["anthropic/*", "openai/gpt-5*"], "blocked_providers": ["xai"], "blocked_products": ["videos"]}}'access_policy replaces the whole policy; {} or null removes it. A rule the API cannot read is refused with 400 and a param naming it, such as access_policy.allowed_models.1.
How Routing Treats a Policy
Language routing leaves blocked endpoints out before it ranks:
- A model with several hosts, a
modelsfallback list, a named router,costream/autoand a fusion panel each skip what the policy blocks and use what is left. The skipped endpoints are listed underblockedin thex-audiolab-routingheader and the body'sroutingfield. - A request whose only candidates are blocked is refused with
403before anything is held. providerpreferences on a request can narrow the choice further but never reach past the policy. Asking only for a blocked provider is a403.- In a batch, each line of a blocked model fails on its own with
403.
{
"mode": "fallback",
"requested": "openai/gpt-oss-20b",
"model": "openai/gpt-oss-20b",
"endpoint": "groq/gpt-oss-20b",
"provider": "groq",
"blocked": ["deepinfra/openai-gpt-oss-20b"],
"attempts": [{ "model": "groq/gpt-oss-20b", "provider": "groq", "role": "output", "status": "succeeded" }]
}Check What a Key Has Left
Get the calling key with the key itself. limit, limit_remaining and limit_reset follow OpenRouter and describe the key's own tightest limit. budgets lists every limit that applies, the key's and its workspace's, and effective_budget is the one that would stop the key first:
const response = await fetch(`${process.env.AUDIOLAB_API_URL}/v1/key`, {
headers: { Authorization: `Bearer ${process.env.AUDIOLAB_API_KEY}` },
});
if (!response.ok) throw new Error(`Key lookup failed: ${response.status}`);
const { data } = await response.json();
console.log(data.limit, data.limit_remaining, data.limit_reset);
console.log(data.effective_budget);Run it with node key.mjs (Node.js 18 or later). Part of a response for a key with a $3 daily budget in a workspace with a $1 weekly budget:
{
"data": {
"limit": 3,
"limit_remaining": 2.4,
"limit_reset": "daily",
"usage_daily": 0.6,
"effective_budget": {
"scope": "workspace",
"period": "weekly",
"limit": 1,
"usage": 0.85,
"remaining": 0.15,
"resets_at": "2026-10-19T00:00:00.000Z"
}
}
}Handle Refusals
A budget refusal is 402 with code budget_exceeded. metadata names the budget, its period and when it resets, so you can wait for the reset or raise the budget:
{
"error": {
"code": "budget_exceeded",
"type": "billing_error",
"message": "The workspace's daily budget of $25.00 would be exceeded. It resets at 2026-10-16T00:00:00.000Z.",
"param": null,
"metadata": {
"limit": "workspace_budget",
"budget": { "scope": "workspace", "period": "daily", "limit": 25, "usage": 24.97, "resets_at": "2026-10-16T00:00:00.000Z" }
}
}
}limit is key_budget for a key's own budget. A key's lifetime Total Spend Limit is 402 with code key_limit_exceeded and limit key_spend_limit; an empty workspace balance is insufficient_credits.
An access refusal is 403 with code access_denied. metadata names the policy (workspace or key), the rule (models, providers or products) and what it refused:
{
"error": {
"code": "access_denied",
"type": "permission_error",
"message": "openai/gpt-5.2 is blocked by the workspace's access policy (providers).",
"param": null,
"metadata": { "policy": "workspace", "rule": "providers", "product": "llm", "provider": "openai", "model": "openai/gpt-5.2" }
}
}Media and workspace endpoints send the same code, message and metadata, with request_id in place of type. Neither refusal is worth retrying until the budget resets or the policy changes.
Zero Data Retention per Provider
Your account's privacy settings choose zero data retention per provider family. A workspace can add the same restriction for its own requests under privacy.zdr in its settings. It can only restrict, and a patch keeps the families it leaves out:
curl -X PATCH "$AUDIOLAB_API_URL/v1/settings" \
-H "Authorization: Bearer $AUDIOLAB_SESSION" \
-H "Content-Type: application/json" \
-d '{"workspace_id": "ws_YOUR_WORKSPACE", "privacy": {"zdr": {"anthropic": true}}}'