ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference
Getting Started
    Develop in the portal
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
    Develop locally with the CLI
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
Concepts
API Management
AI Gateway
    OverviewGetting StartedSource ControlUniversal API
    Providers
    Teams
    Apps
      OverviewManaging AppsPolicy ChainsCustom PoliciesFallbacksUsage Limits
    Policies
    Cookbooks
    Integrations
MCP Gateway
MCP Server
Developer Portal
Development
Deploying & Source Control
Analytics
Observability
Networking & Infrastructure
Account Management
Programming API
Build with AI
Zuplo CLI
Migration Guides
Platform LimitsVersion Support PolicySecuritySupportTrust & ComplianceChangelog
powered by Zudoku
Apps

Usage Limits & Budget Rules

The Zuplo AI Gateway provides hierarchical usage limits and budget controls to manage LLM spending across the gateway's Zuplo project. Limits can be set at the gateway, team, and app levels.

Budget Hierarchy

Limits at every level apply together. A request is blocked when the gateway, an ancestor team, or the app is over a Block limit. The error identifies whether a shared node budget or an expression-specific budget ran out:

  • Gateway - Limits across the Zuplo project (for example, $1,000/day), covering all teams, sub-teams, and apps combined
  • Teams - Limits covering the team's own usage and every descendant sub-team and app (for example, $500/day for the Engineering team)
  • Apps - Per-app limits for granular control (for example, $10/day for a hackathon app)

Budgets at different levels don't constrain each other's values, and app budgets under a team may add up to more than the team's own budget - the team's budget is the aggregate cap. For example, take a $100/month team containing a $50/month sub-team with two apps capped at $25/month and $40/month. When the apps have spent $40 and $10, the sub-team's aggregate reaches $50 and requests through both apps are blocked - including the app that is still under its own $25 cap. Apps elsewhere in the $100/month team keep working until that team's own aggregate reaches $100.

Team · spent $50 of $100
Sub-team · spent $50 of $50
App A · spent $10 of $25
App B · spent $40 of $40
Other apps in the team
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

Where limits are configured

LevelShared budgetPer-expression budgets
GatewaySettings → Usage LimitsSupported in configuration, but not authorable in the Portal
TeamThe team's Usage & Limits tabThe team's Usage & Limits tab
AppThe Budgets and Costs policy in the app's policy chainThe Budgets and Costs policy

Gateway and team limits apply independently to every descendant app; they aren't copied into the app's configuration. Each node meters its own usage plus all descendant usage. A request is blocked if any node in its path is over a Block limit. An app's own limits live on the Budgets and Costs policy.

Everything a team enforces lives on its Usage & Limits tab. The Policy Template tab is only the template: a Budgets and Costs entry there is copied into each new app as the app's own starting budget and doesn't enforce the team's budget.

How a budget rule works

Every node's budgets are a list of rules. Each rule answers two questions—what it budgets, and what the caps are.

Scope decides who shares the budget:

ScopeWhat it budgets
Shared (budgetBy: "app")One shared budget for the gateway, team, or app and its descendants
By metadata (budgetBy: "expression")A separate budget at that node for every distinct value of an expression

A By metadata rule gives each distinct expression value its own allowance. With the expression request.headers.get("x-user") and a $5 daily cap, every distinct x-user value gets its own $5 per day—one user exhausting their budget doesn't affect anyone else. A node can combine a shared rule with several expression rules.

Ancestor rules of both kinds apply to every request through the node's subtree. The difference is who gets blocked: a team's Shared rule blocks all of the team's traffic once the team's aggregate is over, while a team's By metadata rule meters each value across all of the team's apps combined and blocks only the exhausted value—the same x-user shares one budget whether it calls App A or App B.

Limit rows set the caps. Each row is a meter, a period, an amount, and an action:

FieldValues
MeterCost (dollars), Tokens (input plus output), Requests
PeriodHourly, Daily, Weekly, Monthly
LimitAn absolute amount in the meter's own unit
ActionBlock rejects the request; Warn notifies without blocking

Add as many rows as you need to cap several meters and periods at once. A Warn row and a Block row on the same meter and period give you warn-then-block—warn at $80, block at $100. The warn amount has to be the lower of the two.

The Overview tab charts daily and monthly usage only. Hourly and weekly rows are enforced, but no meter on that tab tracks them.

Budget rule periods follow the UTC calendar

Hourly periods reset at the top of each hour, daily at 00:00 UTC, weekly on Monday at 00:00 UTC, and monthly on the first of the month. A rule added mid-period gets the full allowance for the rest of that period—the limit isn't prorated—and resets at the next boundary. Changing a limit mid-period doesn't reset the period's usage. Custom anchors aren't configurable.

Setting an app's limits

  1. Open the Apps & Teams tab and select the app.

  2. On the Policies tab, configure the Budgets and Costs policy (add it from Add Policy if the chain doesn't have it).

  3. Select Add rule, choose the scope, and use Add limit to add rows for the meters and periods you want to cap. For a By metadata rule, enter the expression that identifies each caller.

  4. Select Apply, then Save changes on the policy chain. The change applies within about a minute.

The app editor groups gateway and ancestor team rules under Inherited. Those rules remain stored and enforced at the gateway or team; edit them where they were created.

Budget expressions

A By metadata rule identifies each caller with an expression evaluated per request. The editor validates it as you type. Available values include:

  • request.headers.get("x-user") and other request headers
  • request.user.sub and request.user.data.<key> from the authenticated API key
  • request.url, request.method, request.query, request.searchParams, and request.params
  • context.contextId, context.requestId, context.route, and context.custom

The editor labels each expression by where its value comes from, because that decides how much the budget can be trusted. A client-supplied value—a header or query parameter—is attribution, not enforcement: a caller who changes it gets a fresh budget. Budget on request.user.*, labeled verified, when the segment itself has to hold, and keep a shared-budget rule alongside it as the hard cap.

Changing an expression starts a new budget. The old rule's usage stays with the old expression, so every value's accounting restarts from zero. Expressions are also case-sensitive: get("X-User") and get("x-user") read the same header but budget separately.

App rules are stored in options.budgetRules on the app's own Budgets and Costs policy entry. For the app rule schema, see the Budgets and Costs policy reference.

This rule set budgets each user, each organization, and the app as a whole:

Code
{ "budgetRules": [ { "budgetBy": "expression", "expression": "request.headers.get(\"x-user\")", "meters": [ { "meter": "tokens", "period": "hourly", "value": 20000, "action": "block" } ] }, { "budgetBy": "expression", "expression": "request.headers.get(\"x-organization\")", "meters": [ { "meter": "cost", "period": "daily", "value": 50, "action": "block" } ] }, { "budgetBy": "app", "meters": [ { "meter": "cost", "period": "monthly", "value": 2000, "action": "warn" }, { "meter": "cost", "period": "monthly", "value": 2500, "action": "block" } ] } ] }

Every distinct x-user value gets its own 20,000 tokens per hour, resetting at the top of the hour. Every distinct x-organization value gets its own $50 per day, resetting at 00:00 UTC. The app rule caps the whole app at $2,500 per month, warning at $2,000, resetting on the first of the month.

Setting team and gateway limits

Team and gateway limits cover the node's own usage and all descendant usage. They apply to every descendant app whether or not its chain includes Budgets and Costs.

Team rules—shared and per-expression—are added on the team's Usage & Limits tab, using the same meter, period, amount, and action rows as the app editor.

To add a shared gateway rule, open Settings → Usage Limits. The gateway root supports per-expression rules in metadata.budgetRules, but the Portal doesn't author them. It displays a stored expression rule as read-only and lets you delete it.

Each budgetBy: "app" rule is a shared budget for the node where the rule is stored. At the gateway or team level, app means that gateway or team node, not an app policy configuration. A team's usage includes its sub-teams and apps, and the gateway's usage includes the whole project.

Gateway and team rules are stored in metadata.budgetRules on that node. The editor groups ancestor rules under Inherited, but each rule remains enforced at the node where it was created.

When a limit is exceeded

If the app has a quota fallback model configured, an exceeded limit routes requests to that model instead of blocking—see Fallback Models, where the fallback model is selected. This applies to gateway and team limits as well as the app's own. The fallback's usage still counts toward the limits.

Without a fallback, the request is rejected with 429 Too Many Requests and a budget object naming what ran out:

Code
{ "type": "https://httpproblems.com/http-status/429", "title": "Too Many Requests", "detail": "Your daily cost budget has been exhausted (limit: 5).", "budget": { "scope": "dimension", "dimensionedBy": "request.headers.get(\"x-user\")", "ruleId": "3f2a9c1b", "meter": "cost", "window": "daily", "limit": 5 } }

The scope tells the caller which budget they hit. It is application when the gateway, a team, or the app exhausts a shared node budget. It is dimension when a value exhausts an expression budget. Quote the ruleId when raising a support request.

Budgets fail open by default

The Budgets and Costs policy's throwOnFailure option—Fail closed when metering is unavailable in the editor—controls how an error while checking the app's own limits is handled. It defaults to false, so the request continues through the gateway. Set it to true to reject the request instead. Team and gateway limit checks run independently of this policy.

Monitoring Usage

Each app, team, and the gateway show current usage against their limits:

  1. Open the Apps & Teams tab and select an app or team
  2. The Overview tab shows daily and monthly usage—spend, tokens, and requests—with progress against any configured limits
  3. The same tab's Metrics frame charts request count, token usage, and cost over time; View in Analytics opens the full request-level breakdown scoped to that app or team

Related Resources

  • Getting Started - Set up your first AI Gateway project with budget controls
  • Managing Teams - Configure team-level budgets
  • Managing Apps - Configure app-level limits
  • Fallback Models - Serve a cheaper model instead of blocking when a limit is exceeded
Edit this page
Last modified on September 2, 2026
FallbacksOverview
On this page
  • Budget Hierarchy
  • Where limits are configured
  • How a budget rule works
  • Setting an app's limits
    • Budget expressions
  • Setting team and gateway limits
  • When a limit is exceeded
  • Monitoring Usage
  • Related Resources
JSON
JSON