API7 Docs

limit-count plugin for Apache APISIX

Skill for configuring the APISIX limit-count plugin via the a6 CLI. Covers fixed and sliding windows, Redis Sentinel, delayed sync, and shared quotas.

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: /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.