ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference

AI Gateway Metering (v2) Policy

Meters AI Gateway v2 usage and enforces app and ancestor usage limits.

Limits configured on parent teams or the bucket root are enforced through a server-side hierarchical check.

The authentication policy must run before this policy so the app configuration id is available for meter storage and analytics.

Beta

This policy is in beta. You can use it today, but it may change in non-backward compatible ways before the final release.

Configuration

The configuration shows how to configure the policy in the 'policies.json' document.

Code
{ "name": "my-ai-gateway-metering-v2-inbound-policy", "policyType": "ai-gateway-metering-v2-inbound", "handler": { "export": "AIGatewayMeteringV2InboundPolicy", "module": "$import(@zuplo/runtime)", "options": { "limits": { "costs": { "daily": { "warning": {} }, "monthly": { "warning": {} } }, "requests": { "daily": { "warning": {} }, "monthly": { "warning": {} } }, "tokens": { "daily": { "warning": {} }, "monthly": { "warning": {} } } }, "throwOnFailure": false } } }

Policy Configuration

  • name <string> - The name of your policy instance. This is used as a reference in your routes.
  • policyType <string> - The identifier of the policy. This is used by the Zuplo UI. Value should be ai-gateway-metering-v2-inbound.
  • handler.export <string> - The name of the exported type. Value should be AIGatewayMeteringV2InboundPolicy.
  • handler.module <string> - The module containing the policy. Value should be $import(@zuplo/runtime).
  • handler.options <object> - The options for this policy. See Policy Options below.

Policy Options

The options for this policy are specified below. All properties are optional unless specifically marked as required.

  • throwOnFailure <boolean> - Throw when the metering service is unavailable instead of failing open. Defaults to false.
  • limits <object> - Usage limits grouped by meter.
    • costs <object> - No description available.
      • daily <object> - No description available.
        • enabled (required) <boolean> - No description available.
        • limit <number> - No description available.
        • warning <object> - No description available.
          • enabled (required) <boolean> - No description available.
          • threshold <number> - Percentage of the limit at which to emit a warning event.
      • monthly <object> - No description available.
        • enabled (required) <boolean> - No description available.
        • limit <number> - No description available.
        • warning <object> - No description available.
          • enabled (required) <boolean> - No description available.
          • threshold <number> - Percentage of the limit at which to emit a warning event.
    • tokens <object> - No description available.
      • daily <object> - No description available.
        • enabled (required) <boolean> - No description available.
        • limit <number> - No description available.
        • warning <object> - No description available.
          • enabled (required) <boolean> - No description available.
          • threshold <number> - Percentage of the limit at which to emit a warning event.
      • monthly <object> - No description available.
        • enabled (required) <boolean> - No description available.
        • limit <number> - No description available.
        • warning <object> - No description available.
          • enabled (required) <boolean> - No description available.
          • threshold <number> - Percentage of the limit at which to emit a warning event.
    • requests <object> - No description available.
      • daily <object> - No description available.
        • enabled (required) <boolean> - No description available.
        • limit <number> - No description available.
        • warning <object> - No description available.
          • enabled (required) <boolean> - No description available.
          • threshold <number> - Percentage of the limit at which to emit a warning event.
      • monthly <object> - No description available.
        • enabled (required) <boolean> - No description available.
        • limit <number> - No description available.
        • warning <object> - No description available.
          • enabled (required) <boolean> - No description available.
          • threshold <number> - Percentage of the limit at which to emit a warning event.

Using the Policy

AI Gateway Metering (v2)

ai-gateway-metering-v2-inbound records AI Gateway usage and enforces daily or monthly limits before the provider request runs. Declare the policy in policies.json, then add it to an application's inboundPolicyChain.

Example

Code
{ "name": "ai-gateway-metering-v2-inbound", "options": { "throwOnFailure": false, "limits": { "costs": { "monthly": { "enabled": true, "limit": 100, "warning": { "enabled": true, "threshold": 80 } } }, "requests": { "daily": { "enabled": true, "limit": 1000 } } } } }

The supported meters are costs, tokens, and requests. Each meter can have daily and monthly settings. A period is enforced only when enabled is true and limit is present. Usage equal to the limit is blocked.

When throwOnFailure is false, metering failures pass the request through. Set it to true to fail the request instead.

Team limits

Limits configured on a parent team or the bucket root are enforced automatically through a server-side hierarchical check against aggregated usage. The options.limits settings govern only the app running this policy; the server walks the ancestor chain and reports the first exceeded limit.

An exceeded team or root limit follows the same configured model-fallback or 429 response path as an app limit. Ancestor enforcement cannot be disabled from the app's policy-chain entry. When the hierarchical check is unavailable, throwOnFailure controls the behavior: the default skips enforcement for that request while continuing to meter it, and true fails the request.

Known residual

Grandfathered v2 apps that already store metadata.limits are still evaluated at the leaf by the server's hierarchy walk. The companion Gateway Service change rejects introducing or modifying app metadata limits; cleaning up stale rows is tracked separately.

Set limits from custom code

Use a custom inbound policy to vary limits for individual requests. For example, the following policy gives callers with a pro plan a higher daily request limit:

Code
import { AIGatewayMeteringV2InboundPolicy, type ZuploContext, type ZuploRequest, } from "@zuplo/runtime"; export default function applyPlanLimit( request: ZuploRequest, context: ZuploContext, ) { const dailyRequestLimit = request.user?.data.plan === "pro" ? 10_000 : 1_000; AIGatewayMeteringV2InboundPolicy.setLimits(context, { requests: { daily: { enabled: true, limit: dailyRequestLimit, }, }, }); return request; }

Register the module as a custom policy:

Code
{ "name": "plan-based-ai-limits", "policyType": "custom-code-inbound", "handler": { "export": "default", "module": "$import(./modules/plan-based-ai-limits)" } }

Place it before metering in the application's inbound policy chain:

Code
{ "inboundPolicyChain": [ { "name": "plan-based-ai-limits" }, { "name": "ai-gateway-metering-v2-inbound" } ] }

setLimits changes only the fields supplied for the current request. Other settings from the metering policy remain unchanged. For example, setting only requests.daily.limit preserves requests.daily.enabled, its warning settings, and all monthly limits. It does not update the policy configuration or affect later requests.

The exported AIGatewayMeteringV2LimitOverrides interface can be used when a custom policy builds the override object separately. Its shape matches the limits option. These values are called overrides because they temporarily replace matching policy settings for the current request:

  • Choose a meter: costs, tokens, or requests.
  • Choose a period: daily or monthly.
  • Set one or more values: enabled, limit, or warning.

If model filtering and fallback-model policies selected a quotaFallback, an exceeded limit activates that fallback instead of returning 429. The fallback request is still metered. Order the chain as model filtering, fallback model, metering, then policies that may short-circuit such as semantic cache.

Read more about how policies work

Edit this page
Last modified on August 10, 2026
On this page
  • Configuration
    • Policy Configuration
    • Policy Options
  • Using the Policy
JSON
JSON
TypeScript
JSON
JSON