Skip to content
agentgateway has joined the Agentic AI FoundationLearn more

For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.

Per-key dollar or token budgets

Page as Markdown

Cap what one API key spends on LLM traffic in US dollars or in tokens, and limit which models it can reach.

Verified Code examples on this page have been automatically tested and verified.

Cap what one API key spends on LLM traffic, in US dollars or in tokens.

About per-key budgets

A per-key budget is a budgets entry on an API key. It charges the realized cost or the token usage of each request that the key sends, and it rejects or records requests once the key passes its limit.

A USD budget is a true spend cap, which is the main reason to choose a per-key budget over a rate limit token budget. Agentgateway prices each request from a model cost catalog and charges the result, so the cap holds however the model mix changes.

A Tokens budget caps token usage instead, and needs no catalog.

For the charging model and the window alignment, see Budget and spend limits.

Before you begin

Install the agentgateway binary.

Set a dollar budget

Step 1: Load a model cost catalog

A USD budget charges the realized cost of each request, and agentgateway computes that cost from a model cost catalog. Create a catalog that prices the model you route to. Rates are the price per 1M tokens, written as strings. The following example charges $10 per 1M input tokens and $30 per 1M output tokens.

Tip

To generate a catalog for real provider pricing instead of writing one by hand, use agctl catalog import. For the full catalog format and how to layer overrides, see Model costs.

cat <<'EOF' > catalog.json
{
  "providers": {
    "openai": {
      "models": {
        "gpt-5": { "rates": { "input": "10.0", "output": "30.0" } }
      }
    }
  }
}
EOF

Step 2: Configure budgets on your API keys

Create a configuration with a database, the catalog, API key authentication, and a budget on each key.

cat <<'EOF' > config.yaml
# yaml-language-server: $schema=https://agentgateway.dev/schema/config

config:
  database:
    url: sqlite://budgets.db
  modelCatalog:
  - file: ./catalog.json
llm:
  policies:
    apiKey:
      mode: strict
      keys:
      - key: sk-team-a-abc123def456
        metadata:
          name: team-a
        allowedModels:
        - "gpt-5*"
        budgets:
        - name: daily-spend
          limit:
            unit: USD
            amount: 0.008
          window:
            rolling: 24h
          onBudgetExceeded: Block
      - key: sk-team-b-xyz789uvw012
        metadata:
          name: team-b
        budgets:
        - name: daily-tokens
          limit:
            unit: Tokens
            amount: 250
          window:
            rolling: 24h
          onBudgetExceeded: Audit
  models:
  - name: "*"
    provider: openAI
    params:
      apiKey: "$OPENAI_API_KEY"
EOF

The team-a key has a dollar budget that blocks requests when the budget is exhausted, and the team-b key has a token budget that only records the overage. Review the following table to understand this configuration. Both limits are small so that you can reach them in a few requests. Use realistic limits in production.

SettingDescription
config.database.urlConnection string for the database that holds budget counts. Agentgateway supports SQLite and PostgreSQL. A budget requires this exact field, and agentgateway refuses to start without it. Setting config.logging.database.url instead does not satisfy the requirement, because that field configures request logging only. For the other things that the database stores, see Configuration storage.
config.modelCatalogCatalog sources that price each request. A USD budget requires a catalog entry for every model that the key uses. A Tokens budget does not.
metadata.nameIdentifies the key in budget counts, logs, and the admin API. A key that has a budget requires this field.
budgetsList of budgets that are charged independently. A key can have several budgets, such as an hourly dollar cap and a monthly dollar cap.
budgets[].nameNames the budget within its key. The name must be unique among that key’s budgets.
budgets[].limit.unitUSD to cap realized cost, or Tokens to cap token usage.
budgets[].limit.amountThe maximum usage in the window. A Tokens amount must be a whole number. A USD amount takes up to nine decimal places.
budgets[].window.rollingLength of the fixed usage window, such as 1h, 24h, or 30d. Windows are aligned to the Unix epoch rather than to the key’s first request.
budgets[].onBudgetExceededBlock to reject requests with a 429 after the limit is passed, or Audit to record the overage and allow the request.
allowedModelsModel name patterns that the key can reach. Omit the field to leave the key unconstrained. For more information, see Limit model access per key.

Warning

A USD budget charges nothing unless the catalog prices the models that the key uses. Agentgateway does not report an error in this case. The budget stays at zero usage and never rejects a request. Before you rely on a USD budget, confirm that the access log records agw.ai.usage.cost.total for the traffic that the budget covers.

Step 3: Start agentgateway

agentgateway -f config.yaml

Step 4: Verify model access

  1. Send a request for a model that the key is allowed to use. Verify that the request succeeds. At the catalog rates from Step 1, this request costs about $0.003 of the key’s $0.008 budget.

    curl -s -o /dev/null -w "%{http_code}\n" http://localhost:4000/v1/chat/completions \
      -H "Authorization: Bearer sk-team-a-abc123def456" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "gpt-5",
        "max_tokens": 100,
        "messages": [{"role": "user", "content": "Hello!"}]
      }'

    Example output:

    200
  2. Send a request for a model that the key is not allowed to use. Verify that agentgateway rejects the request with a 403 status.

    curl -s http://localhost:4000/v1/chat/completions \
      -H "Authorization: Bearer sk-team-a-abc123def456" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "claude-sonnet-5",
        "max_tokens": 100,
        "messages": [{"role": "user", "content": "Hello!"}]
      }'

    Example output:

    {"error":{"message":"Model is not allowed for this API key","type":"invalid_request_error","code":"model_not_allowed"}}

    A rejected request never reaches a provider, so agentgateway does not charge it to the key’s budget.

Step 5: Verify the dollar budget

  1. Send three more requests with the team-a key. Each request costs about $0.003, and Step 4 already spent $0.003. The second request in this loop therefore takes the key past its $0.008 limit.

    for i in 1 2 3; do
      curl -s -o /dev/null -w "request $i: %{http_code}\n" http://localhost:4000/v1/chat/completions \
        -H "Authorization: Bearer sk-team-a-abc123def456" \
        -H "Content-Type: application/json" \
        -d '{
          "model": "gpt-5",
          "max_tokens": 100,
          "messages": [{"role": "user", "content": "Hello!"}]
        }'
    done

    The second request completes and takes the total to $0.00903, so the third request is the first one that agentgateway rejects.

    Example output:

    request 1: 200
    request 2: 200
    request 3: 429
  2. Send one more request to see the error body.

    curl -s http://localhost:4000/v1/chat/completions \
      -H "Authorization: Bearer sk-team-a-abc123def456" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "gpt-5",
        "max_tokens": 100,
        "messages": [{"role": "user", "content": "Hello!"}]
      }'

    Example output:

    {"error":{"message":"Budget exceeded","type":"rate_limit_error","code":"budget_exceeded"}}

    Agentgateway also writes a log line for each rejected request. A USD budget reports its amounts in dollars.

    warn budget API key budget exceeded api_key="team-a" budget="daily-spend" used=0.00903 limit_unit="USD" limit_amount=0.008 exceeded=true

Step 6: Check budget usage

Query the admin API for the current usage of a key. To return every budget, omit the apiKeyName parameter.

curl -s "http://localhost:15000/api/budgets/status?apiKeyName=team-a" | jq .

The used value is a dollar amount, and it is higher than the limit, because agentgateway charged the request that crossed the limit after it completed.

Example output:

{
  "observedAt": 1787773624201,
  "budgets": [
    {
      "apiKeyName": "team-a",
      "name": "daily-spend",
      "limit": {
        "unit": "USD",
        "amount": "0.008"
      },
      "usage": {
        "used": "0.00903",
        "remaining": "0",
        "exceeded": true
      },
      "window": {
        "start": 1787702400000,
        "end": 1787788800000,
        "durationMs": 86400000,
        "expired": false
      },
      "onBudgetExceeded": "Block",
      "updatedAt": 1787773623159
    }
  ]
}

You can also review and edit budgets in the built-in Admin UI, on the same LLM > Virtual API Keys page that lists your keys.

Step 7: Try a token budget

The team-b key that you configured in Step 2 caps tokens rather than dollars, and it uses the Audit action. An Audit budget records usage and logs the overage, but it never rejects a request. Use an Audit budget to size a limit before you enforce it, or to alert on a team that goes over its allocation without interrupting its work.

  1. Send four requests with the team-b key. Each request uses 101 tokens, so four requests use more than the 250-token limit allows.

    for i in 1 2 3 4; do
      curl -s -o /dev/null -w "request $i: %{http_code}\n" http://localhost:4000/v1/chat/completions \
        -H "Authorization: Bearer sk-team-b-xyz789uvw012" \
        -H "Content-Type: application/json" \
        -d '{
          "model": "gpt-5",
          "max_tokens": 100,
          "messages": [{"role": "user", "content": "Hello!"}]
        }'
    done

    Every request succeeds, even after the key passes its limit.

    Example output:

    request 1: 200
    request 2: 200
    request 3: 200
    request 4: 200
  2. Check the gateway logs. Agentgateway logs each request that it receives while the budget is over its limit, so you can alert on the budget target in your log pipeline. A Tokens budget reports its amounts as whole tokens.

    warn budget API key budget exceeded api_key="team-b" budget="daily-tokens" used=303 limit_unit="Tokens" limit_amount=250 exceeded=true

Limit model access per key

The allowedModels field limits which models an API key can reach. The field is independent of budgets, so it needs no database and no catalog, and you can use it on its own.

llm:
  policies:
    apiKey:
      mode: strict
      keys:
      - key: sk-team-a-abc123def456
        metadata:
          name: team-a
        allowedModels:
        - "gpt-5*"
        - claude-sonnet-5

Each entry is an exact model name or a pattern with one * wildcard, such as a gpt-5* prefix or a *-mini suffix. Agentgateway rejects a request for any other model with a 403 response and the model_not_allowed code, before the request reaches a provider.

ValueEffect
The field is omittedThe key can reach every model that the gateway serves. This is the default.
An empty listThe key can reach no models. Agentgateway rejects every LLM request from the key.
["*"]The key can reach every model. You cannot combine * with another pattern in the same list.

The field also filters the model list that the key sees. A request to /v1/models returns only the models that the key is allowed to use. For example, a gateway names three models individually rather than with the "*" pattern that Step 2 uses.

llm:
  models:
  - name: gpt-5
    # ...
  - name: gpt-5-mini
    # ...
  - name: claude-sonnet-5
    # ...

A key with allowedModels: ["gpt-5*"] sees gpt-5 and gpt-5-mini. A key with no allowedModels sees all three. A key with an empty list sees none. Agentgateway returns a model that you configure as a pattern, such as "*", as that pattern rather than as expanded names.

Clean up

  1. Stop agentgateway with Ctrl+C.

  2. Remove the budget database, the catalog, and the configuration file.

    rm -f budgets.db catalog.json config.yaml

What’s next

Was this page helpful?
Agentgateway assistant

Ask me anything about agentgateway configuration, features, or usage.

Note: AI-generated content might contain errors; please verify and test all returned information.

Tip: one topic per conversation gives the best results. Use the + button in the chat header to start a new conversation.

Switching topics? Starting a new conversation improves accuracy.
↑↓ navigate select esc dismiss

What could be improved?

Your feedback helps us improve assistant answers and identify docs gaps we should fix.

Need more help? Join us on Discord: https://discord.gg/y9efgEmppm

Want to use your own agent? Add the Solo MCP server to query our docs directly. Get started here: https://search.solo.io/.