Skip to main content

a6-plugin-limit-count

Overview

The limit-count plugin rate-limits requests using a counter in a time window. Define a maximum number of requests (count) within an interval (time_window). The default window_type is fixed. Set window_type: sliding to smooth bursts at window boundaries. Supports per-IP, per-consumer, per-header, or custom variable keys. For distributed APISIX deployments, share counters through Redis, Redis Cluster, or Redis Sentinel (policy: redis-sentinel).

Redis Sentinel, sliding windows, and delayed Redis synchronization (sync_interval) are available from APISIX 3.18.0. Field tables and examples: https://docs.api7.ai/hub/limit-count

When to Use

  • Simple request counting (e.g., 100 requests per hour)
  • API quota enforcement per consumer or API key
  • Shared rate limits across multiple APISIX nodes (via Redis)
  • Grouped quotas across multiple routes

Plugin Configuration Reference

Core Fields

FieldTypeRequiredDefaultDescription
countintegerYes*Max requests allowed in the time window. > 0
time_windowintegerYes*Time window in seconds. > 0
key_typestringNo"var"Key type: "var", "var_combination", or "constant"
keystringNo"remote_addr"Variable name or combination for counting
rejected_codeintegerNo503HTTP status on rejection (200–599)
rejected_msgstringNoCustom rejection message body
groupstringNoShare counters across routes with same group ID
policystringNo"local"Storage: "local", "redis", "redis-cluster", or "redis-sentinel"
window_typestringNo"fixed""fixed" or "sliding" (APISIX 3.18.0+)
sync_intervalnumberNo-1Redis sync interval in seconds. -1 syncs every request. Min 0.1 when enabled; must be smaller than a numeric time_window
show_limit_quota_headerbooleanNotrueInclude X-RateLimit-* headers in responses
allow_degradationbooleanNofalseAllow requests when plugin fails

*Required unless using rules array.

Redis Fields (when policy: "redis")

FieldTypeRequiredDefaultDescription
redis_hoststringYesRedis server address
redis_portintegerNo6379Redis port
redis_usernamestringNoRedis ACL username
redis_passwordstringNoRedis password
redis_databaseintegerNo0Redis database index
redis_timeoutintegerNo1000Timeout in milliseconds
redis_sslbooleanNofalseEnable TLS to Redis

Redis Cluster Fields (when policy: "redis-cluster")

FieldTypeRequiredDefaultDescription
redis_cluster_nodesarray[string]YesArray of "host:port" (min 2)
redis_cluster_namestringYesCluster name
redis_passwordstringNoCluster password
redis_timeoutintegerNo1000Timeout in milliseconds
redis_cluster_sslbooleanNofalseEnable TLS

Redis Sentinel Fields (when policy: "redis-sentinel", APISIX 3.18.0+)

FieldTypeRequiredDefaultDescription
redis_sentinelsarray[object]YesSentinel nodes: { "host": "...", "port": 26379 }
redis_master_namestringYesSentinel-monitored master name
redis_rolestringNo"master""master" or "slave"
redis_usernamestringNoRedis ACL username
redis_passwordstringNoRedis password
redis_databaseintegerNo0Redis database index
sentinel_usernamestringNoRedis Sentinel ACL username
sentinel_passwordstringNoRedis Sentinel password
redis_connect_timeoutintegerNo1000Connection timeout in milliseconds
redis_read_timeoutintegerNo1000Read timeout in milliseconds
redis_keepalive_timeoutintegerNo60000Keepalive timeout in milliseconds

Key Types

key_typekey FormatExampleDescription
"var"NGINX variable (no $)"remote_addr"Single variable
"var_combination"$var1 $var2"$remote_addr $consumer_name"Multiple variables combined
"constant"Any string"global"Same counter for all requests

Response Headers

When show_limit_quota_header: true (default):

HeaderDescription
X-RateLimit-LimitTotal quota for the time window
X-RateLimit-RemainingRemaining requests in current window
X-RateLimit-ResetSeconds until counter resets

Step-by-Step: Basic Rate Limiting

1. Rate limit by client IP (route-level)

a6 route create -f - <<'EOF'
{
"id": "rate-limited-api",
"uri": "/api/*",
"plugins": {
"limit-count": {
"count": 100,
"time_window": 60,
"key_type": "var",
"key": "remote_addr",
"rejected_code": 429,
"rejected_msg": "Rate limit exceeded. Try again later."
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"backend:8080": 1
}
}
}
EOF

100 requests per 60 seconds per client IP.

2. Rate limit per consumer

a6 consumer create -f - <<'EOF'
{
"username": "free-tier",
"plugins": {
"limit-count": {
"count": 100,
"time_window": 3600,
"rejected_code": 429
}
}
}
EOF

a6 consumer create -f - <<'EOF'
{
"username": "premium",
"plugins": {
"limit-count": {
"count": 10000,
"time_window": 3600,
"rejected_code": 429
}
}
}
EOF

Consumer-level limits apply across all routes the consumer accesses.

Common Patterns

Shared quota across routes (group)

{
"plugins": {
"limit-count": {
"count": 1000,
"time_window": 3600,
"group": "api-v1",
"rejected_code": 429
}
}
}

All routes with "group": "api-v1" share the same 1000 req/hour counter. Important: All routes in a group must have identical limit-count config.

Multi-variable key (IP + consumer)

{
"plugins": {
"limit-count": {
"count": 50,
"time_window": 60,
"key_type": "var_combination",
"key": "$remote_addr $consumer_name",
"rejected_code": 429
}
}
}

Global rate limit (all requests share one counter)

{
"plugins": {
"limit-count": {
"count": 10000,
"time_window": 60,
"key_type": "constant",
"key": "global",
"rejected_code": 429
}
}
}

Distributed rate limiting with Redis

{
"plugins": {
"limit-count": {
"count": 1000,
"time_window": 60,
"key": "remote_addr",
"policy": "redis",
"redis_host": "redis.example.com",
"redis_port": 6379,
"redis_password": "secret",
"redis_database": 0,
"redis_ssl": true,
"rejected_code": 429
}
}
}

Use Redis when running multiple APISIX nodes to share counters.

Redis Sentinel

{
"plugins": {
"limit-count": {
"count": 1000,
"time_window": 60,
"key": "remote_addr",
"policy": "redis-sentinel",
"redis_master_name": "mymaster",
"redis_sentinels": [
{ "host": "192.168.1.10", "port": 26379 },
{ "host": "192.168.1.11", "port": 26379 }
],
"rejected_code": 429
}
}
}

Sliding window and delayed Redis sync

{
"plugins": {
"limit-count": {
"count": 1000,
"time_window": 60,
"window_type": "sliding",
"policy": "redis",
"redis_host": "redis.example.com",
"sync_interval": 1,
"rejected_code": 429
}
}
}

sync_interval also works with redis-cluster and redis-sentinel. A numeric time_window must be greater than sync_interval, or APISIX rejects the plugin configuration. If a variable-based time_window resolves to a value less than or equal to sync_interval at request time, APISIX falls back to per-request synchronization.

Redis cluster

{
"plugins": {
"limit-count": {
"count": 1000,
"time_window": 60,
"key": "remote_addr",
"policy": "redis-cluster",
"redis_cluster_nodes": [
"192.168.1.10:6379",
"192.168.1.11:6379",
"192.168.1.12:6379"
],
"redis_cluster_name": "apisix-cluster",
"redis_password": "secret",
"rejected_code": 429
}
}
}

Troubleshooting

SymptomCauseFix
Limits not shared across APISIX nodesUsing policy: "local" (default)Switch to "redis", "redis-cluster", or "redis-sentinel"
Group config rejectedMismatched configs in same groupEnsure all routes in group have identical limit-count config
Unexpected counter resetFixed-window boundaryNormal for window_type: fixed; use "sliding" to smooth bursts
Key empty, all clients share one counterVariable doesn't existVerify key variable name; falls back to remote_addr
Rate limit headers missingshow_limit_quota_header: falseSet to true (default)
503 instead of 429Default rejected_code is 503Set rejected_code: 429 explicitly

Fixed-Window Algorithm Note

limit-count defaults to a fixed-window algorithm. Counters reset at exact intervals, so a burst at the boundary of two windows can temporarily exceed the intended rate (for example, 100 req/min allows 200 requests if 100 come at t=59s and 100 at t=61s). Set window_type: sliding to weight the previous window, or combine with limit-req (leaky bucket).

Config Sync Example

version: "1"
consumers:
- username: free-tier
plugins:
limit-count:
count: 100
time_window: 3600
rejected_code: 429
routes:
- id: rate-limited-api
uri: /api/*
plugins:
limit-count:
count: 1000
time_window: 60
key: remote_addr
rejected_code: 429
upstream_id: api-upstream
upstreams:
- id: api-upstream
type: roundrobin
nodes:
"backend:8080": 1

This page is generated from a6-plugin-limit-count/SKILL.md in the api7/a6 repository. Browse all skills on the AI Agent Skills page.