Skip to main content

Rate Limit Policies

A rate limit policy is a quota rule managed separately from caller API keys and models. It can select traffic by team, member, API key, model, model name, or provider, then divide matching requests into independent buckets. A policy can also suspend its own enforcement during recurring windows.

Conditional policies use request conditions and optional bucket dimensions. They are the recommended form for new configurations. Classic policies remain available for existing configurations that target one API key, model, team, or member.

Policies do not replace limits configured on individual API keys or models. AISIX evaluates every matching policy together with the limits on those resources, and the request continues only when every matching bucket has capacity.

This guide shows how to configure and verify conditional policies through AISIX Cloud or the open-source AISIX gateway, maintain classic policies, and schedule policy suspensions.

Prerequisites

Before starting, prepare the following:

  • Familiarity with the limit fields, counter behavior, storage options, and resource-specific limits described in API Key and Model Rate Limits.
  • A working model alias and caller API key that can send proxy requests.
  • For the per-member examples, a caller API key bound to the selected team and a member.
  • One of these configuration paths:
    • AISIX Cloud with an environment, an attached gateway, and a write-scoped admin token.
    • An open-source AISIX gateway with a declarative resources file that you can validate and reload.

Export the gateway request values used to verify the examples:

# AISIX_PROXY has no trailing slash or endpoint path.
# The local quickstarts use http://127.0.0.1:3000.
export AISIX_PROXY="YOUR_AISIX_GATEWAY_URL"
export AISIX_MODEL="gpt-4o-prod"
export AISIX_API_KEY="YOUR_CALLER_API_KEY"

For AISIX Cloud, also export the control-plane values and resource IDs used by the examples:

# AISIX_CP includes /api and has no trailing slash.
# The local On-Premises quickstart uses http://localhost:8080/api.
export AISIX_CP="YOUR_AISIX_CLOUD_ADMIN_API_BASE_URL"
export AISIX_TOKEN="YOUR_ADMIN_TOKEN"
export ENV_ID="YOUR_ENVIRONMENT_ID"
export TEAM_ID="YOUR_TEAM_ID"

Choose a Policy Form

A policy uses one of two forms. In AISIX Cloud, the form is fixed at creation. For the open-source AISIX gateway, an entry in the declarative resources file must use exactly one form; mixing fields from both forms is a load error.

  • Conditional policy (recommended): matches requests with a condition tree, splits counters by dimensions in group_by, and caps traffic with the same seven-field limits shape used by caller API keys and models. This is the only form created by the dashboard's New policy flow.
  • Classic policy (legacy): targets one subject through scope and scope_ref and uses window with max_requests or max_tokens. Existing classic policies remain supported by the Admin API and resources file.

Use a conditional policy for new configurations. It can reproduce every classic scope and supports several policies for the same request.

Configure a Conditional Policy

A conditional policy makes three decisions: which requests match, how their counters split, and how much traffic each bucket can use.

{
"name": "algo-team-premium-models",
"conditions": [
{ "dimension": "team", "operator": "in", "value": ["<team-id>"] },
{
"logic": "or",
"children": [
{ "dimension": "model_name", "operator": "~~", "value": "^gpt-4" },
{ "dimension": "provider", "operator": "==", "value": "anthropic" }
]
}
],
"group_by": ["member"],
"limits": { "rpm": 20 }
}

Match Requests with Conditions

Top-level nodes in conditions all apply, which creates an AND relationship. A node can be a single condition or a group that combines nested children with logic: and or logic: or. An empty or omitted list matches every request in the environment.

DimensionValueNotes
teamTeam IDMatched against the caller API key's team binding
memberMember or user IDMatched against the caller API key's member binding
api_keyCaller API key ID
modelModel IDMatched per dispatched model; inside a model group, this is the selected target
model_nameModel aliasSupports regular-expression matching across aliases
providerProvider catalog ID, such as openai or anthropicMatched against the dispatched model's provider

In AISIX Cloud, identity dimensions use resource IDs; model_name and provider use strings. For the open-source AISIX gateway, api_key and model values use the matching entries' display_name in the declarative resources file. The team and member values match the caller API key's team_id and user_id verbatim.

Operators follow lua-resty-expr, the same expression vocabulary used by APISIX route vars:

OperatorMeaningApplies To
== / ~=Equal / not equalAll dimensions
inValue in a list of 1–64 entriesAll dimensions
~~ / ~*Case-sensitive / case-insensitive regular expression, up to 256 charactersmodel_name and provider
negate: trueInvert a condition or groupAny node

The team, member, api_key, and model dimensions carry IDs, so they use equality or list operators. A request that does not carry a dimension matches neither a condition on that dimension nor its negation, although another branch of an or group can still match. A policy can contain up to 16 conditions with up to 3 levels of nesting.

Split Counters into Buckets

group_by accepts team, member, api_key, model, and provider. It gives every distinct combination of the selected dimension values an independent bucket with the same limits. An empty or omitted group_by creates one shared bucket. A matched request that lacks a selected bucket dimension is not subject to the policy.

Common patterns include:

  1. Team pool: match one team and leave group_by empty so the whole team shares the quota.
  2. Per-member default: match one team and set group_by: [member] so every member receives an independent quota. Stack this with a team-pool policy to enforce both a team total and a per-member maximum.
  3. Fair-share a model: match one model and set group_by: [team] so each team receives an independent quota.

Set the Limits

limits accepts rps, rpm, rph, rpd, tpm, tpd, and concurrency. At least one field is required. Token limits settle after the response or stream reports usage, as described in Rate Limit Fields and Counter Behavior.

action is reserved for future behaviors. reject, which returns HTTP 429, is the only supported value and the default.

See the Resources File Reference for the complete policy schema and field validation rules.

AISIX Cloud

To create a conditional policy in the dashboard:

  1. Open the environment and select Rate limits.
  2. Select New policy and enter a Name.
  3. Under Match conditions, add condition rows. Add condition groups when the policy needs any-of logic or nested combinations. No conditions means the policy matches every request.
  4. Under Quota buckets, keep Shared quota or choose Split by dimension and select the dimensions.
  5. Fill at least one field in the Limits grid and select Create policy.

The policy list renders conditions as chips, with groups in parentheses and explicit AND/OR relationships. Editing a policy can change its conditions, buckets, and limits. Changing the set of bucket dimensions changes the counter keys; editing only the conditions or limits does not reset existing counters.

For automation, create a policy through the Admin API. The following example gives every member of one team an independent one-request-per-minute bucket:

curl -sS -X POST "${AISIX_CP}/environments/${ENV_ID}/rate_limits" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"name": "team-member-default",
"conditions": [
{ "dimension": "team", "operator": "in", "value": ["${TEAM_ID}"] }
],
"group_by": ["member"],
"limits": { "rpm": 1 }
}
EOF

A successful request returns 201 Created, and the policy projects to attached gateways. Conditional policies have no one-per-subject constraint, so you can stack as many as the scenario requires. See the AISIX Cloud Admin API Reference for list, get, update, and delete operations.

Open-Source AISIX Gateway

Declare conditional policies in the rate_limit_policies collection. Condition values on api_key and model take the target entry's display_name. AISIX resolves those names when it loads the resources file, and an unknown name is a load error.

resources.yaml
api_keys:
- display_name: chat-app
key_env: CALLER_API_KEY
allowed_models:
- gpt-4o-prod
team_id: team-blue
user_id: user-alice

rate_limit_policies:
- name: team-member-default
conditions:
- dimension: team
operator: in
value: [team-blue]
group_by: [member]
limits:
rpm: 1

team_id and user_id bind the caller API key to a team and member. The team condition matches the key's team_id; the member bucket keys on its user_id.

❷ Conditions on team and member match these bound values verbatim.

group_by: [member] gives every bound member an independent one-request-per-minute bucket.

Make the caller credential available to the validation command:

export CALLER_API_KEY="${AISIX_API_KEY}"

Validate the complete resources file and reload the gateway.

Verify the Policy

Configure and test one policy at a time. The runnable policy examples use one request per minute, so you can use the request sequence in Verify Rate Limiting with a caller and model that match the policy.

When a conditional policy rejects a request on an OpenAI-compatible endpoint, the 429 body identifies the policy:

{
"error": {
"message": "request limit exceeded (requests) (policy 'team-member-default')",
"type": "rate_limit_exceeded",
"policy": {
"id": "<policy-id>",
"name": "team-member-default"
}
}
}

If a request matches several policies or direct limits, every matching layer must have capacity. The policy named in an OpenAI-compatible response is the layer that rejected that request. Anthropic-compatible errors retain the strict Anthropic envelope and do not include the policy object.

Manage Classic Single-Scope Policies

A classic policy targets one subject. Each classic scope has a direct conditional equivalent:

Classic ScopeMatching TrafficConditional Equivalent
API keyRequests authenticated with the selected caller API keyconditions: api_key in {K}
ModelRequests resolved to the selected modelconditions: model in {M}
TeamRequests whose caller API key carries the selected team bindingconditions: team in {T}
MemberRequests whose caller API key carries the selected member bindingconditions: member in {U}
Each member in a teamRequests carrying both the selected team and a member bindingconditions: team in {T} with group_by: [member]

Classic policies use scope and scope_ref with a window of second, minute, or hour. They require max_requests, max_tokens, or both. Token caps apply only to minute windows. AISIX Cloud rejects a token cap on another window. For the open-source AISIX gateway, the resources schema accepts it, but the runtime does not enforce the cap.

In AISIX Cloud, an environment can contain only one classic policy for each scope and target. The scope and target cannot change after creation.

Export the ID of the caller API key that the classic policy should limit:

export API_KEY_ID="YOUR_CALLER_API_KEY_ID"

Then create a one-request-per-minute policy for that caller API key:

curl -sS -X POST "${AISIX_CP}/environments/${ENV_ID}/rate_limits" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"name": "rate-limited-app-policy",
"scope": "api_key",
"scope_ref": "${API_KEY_ID}",
"window": "minute",
"max_requests": 1
}
EOF

The dashboard marks classic policies with a Legacy badge and displays their conditional equivalents. Its one-click convert action recreates a classic policy in conditional form. Conversion restarts the counters and cannot be undone. Without converting, you can edit a classic policy's name, window, caps, and suspension schedules.

Suspend a Policy on a Schedule

A policy of either form can carry recurring suspension windows in schedules. AISIX skips the policy while any window matches and resumes enforcing it when the window closes.

Scheduling has the following behavior:

  • A window uses days_of_week for weekly recurrence or dates for explicit calendar dates. These fields are mutually exclusive.
  • start_time and end_time use HH:MM in the window's IANA timezone. 24:00 is valid only for end_time and means the end of the day.
  • An end_time before start_time crosses midnight and belongs to its start day. For example, Friday 22:00 to 09:00 covers Friday night through Saturday morning.
  • Multiple windows form a union; the policy is suspended while any window matches.
  • Suspension does not reset counters. A bucket retains its counts when a window opens and closes.
  • Budgets, authentication, and guardrails remain active because only the rate-limit policy is suspended.

The following schedule suspends a policy on weekends and holidays and on weeknights from 22:00 to 09:00. Sunday is included in the overnight window so Monday morning before 09:00 is covered:

{
"schedules": [
{
"timezone": "Asia/Shanghai",
"days_of_week": ["mon", "tue", "wed", "thu", "fri", "sun"],
"start_time": "22:00",
"end_time": "09:00"
},
{
"timezone": "Asia/Shanghai",
"days_of_week": ["sat", "sun"],
"start_time": "00:00",
"end_time": "24:00"
},
{
"timezone": "Asia/Shanghai",
"dates": ["2026-10-01", "2026-10-02", "2026-10-03"],
"start_time": "00:00",
"end_time": "24:00"
}
]
}

AISIX Cloud

In the dashboard, open the policy's create or edit form. Under Suspension windows, select the timezone, choose weekly days or explicit dates, and set the time range or All day. The policy list summarizes the windows and shows a Suspended now badge while one is active.

For automation, export the ID of the policy you want to update:

export POLICY_ID="YOUR_RATE_LIMIT_POLICY_ID"

Then PATCH the policy with the complete replacement list. Send null or an empty list to remove every window:

curl -sS -X PATCH "${AISIX_CP}/environments/${ENV_ID}/rate_limits/${POLICY_ID}" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"schedules": [
{
"timezone": "Asia/Shanghai",
"days_of_week": ["sat", "sun"],
"start_time": "00:00",
"end_time": "24:00"
}
]
}
EOF
note

Gateway versions released before schedules existed reject a policy row that carries the field and stop enforcing that policy. Upgrade every attached gateway before adding schedules. Policies without schedules are unaffected.

Open-Source AISIX Gateway

Add the same windows to a policy in resources.yaml. The following example adds the weekend window to the conditional policy configured earlier:

resources.yaml
rate_limit_policies:
- name: team-member-default
conditions:
- dimension: team
operator: in
value: [team-blue]
group_by: [member]
limits:
rpm: 1
schedules:
- timezone: Asia/Shanghai
days_of_week: [sat, sun]
start_time: "00:00"
end_time: "24:00"

Validate the complete resources file and reload the gateway. See the Resources File Reference for every schedule field and validation rule.

Troubleshooting

When a policy does not behave as expected, first confirm which request attributes it can evaluate and which other limit layers still apply.

SymptomWhat to Check
A team or member policy does not apply.Confirm that the caller API key carries the matching team or member binding. A policy grouped by member skips a request whose caller API key has no member binding.
An API key or model condition does not match.In AISIX Cloud, use the resource ID. In the open-source resources file, use the entry's display_name, then validate and reload the complete file.
Requests still return 429 while a policy is suspended.Check for direct caller or model limits and other matching policies. A schedule suspends only the policy that carries it.
A schedule added through AISIX Cloud does not take effect.Confirm that the policy change reached the gateway and that every attached gateway supports schedules.

Next Steps

Review API Key and Model Rate Limits for resource-specific limits, counter storage, and shared enforcement behavior. Use Metrics to monitor rate-limit rejections.