Skip to content

Rate Limits

Rate limiting is enforced on the API. Two limiters apply, both keyed by client IP address — not by credential or organization.

Every endpoint is covered by a global limiter:

Budget 300 requests per 60 seconds per IP
Applies to All endpoints, including /v1/ussd/*, /v1/customers, /v1/payment/* and /v1/webhooks/callback
Exempt /healthz and /metrics

Exceeding it returns 429 Too Many Requests:

{
"error": "Too Many Requests",
"message": "Too many requests. Please slow down and try again shortly."
}

POST /v1/oauth/token carries a second, much stricter limiter on top of the global one, to blunt brute-forcing of client secrets:

Budget 10 requests per 15 minutes per IP
Applies to POST /v1/oauth/token

Exceeding it returns 429 Too Many Requests:

{
"error": "Too Many Requests",
"message": "Too many authentication attempts. Please try again in a few minutes."
}

Both limiters emit the RFC draft-6 rate-limit headers, as four separate fields:

Header Meaning Example
RateLimit-Limit Requests allowed in the current window 300
RateLimit-Remaining Requests left in the current window 299
RateLimit-Reset Seconds until the window resets 60
RateLimit-Policy The policy being applied, as limit;w=window-seconds 300;w=60
Retry-After Sent on a 429 — seconds to wait before retrying 47

Exempt endpoints carry no rate-limit headers at all, so do not treat their absence as a budget of zero.

Back off rather than retrying immediately:

  1. Read Retry-After and wait at least that long.
  2. Retry with exponential backoff and jitter.
  3. Because the limit is per IP, several processes behind one egress address share a single budget. Account for that when sizing concurrency.

A retried mutating request must reuse the same X-Idempotency-Key so the operation still happens at most once.