BETA
Skip to content

Rate limits

All Krafter APIs are rate-limited. Authenticated routes are keyed per API key team; public unauthenticated routes are keyed per client IP. Each route belongs to one of four buckets, and counters in different buckets are independent — exceeding one bucket does not affect another for the same caller.

Buckets

BucketLimitApplied to
default100 req / 60sEvery authenticated /api/v1/... endpoint via the :api_authenticated pipeline.
strict30 req / 60sExpensive endpoints — flag evaluate / bootstrap / track, push subscriber registration, audit scans run, public surveys.
public200 req / 60sPublic unauthenticated endpoints — form submissions, public analytics ingest, webhook ingest.
log_ingest300 req / 60sLog ingest endpoints, keyed per project ingest token.

Some endpoints pipe through both default and strict. In that case each bucket counts independently — the request will fail when either counter exceeds its limit. The response headers (x-ratelimit-*) only reflect the last bucket evaluated (strict overwrites default), so a request near the default ceiling can still succeed and report the remaining strict budget without surfacing how close it is to the default cap. Per-endpoint docs include a "Rate limit" callout when a non-default bucket is in play.

Response headers

Every API response includes the bucket counters in lowercase headers:

  • x-ratelimit-limit — the bucket's configured limit (e.g. 100)
  • x-ratelimit-remaining — requests left in the current window
  • x-ratelimit-reset — Unix timestamp (seconds) when the window resets

Exceeding the limit (429)

When a client exceeds its bucket, the response is:

  • HTTP 429 Too Many Requests
  • retry-after: <seconds> header (RFC 9110)
  • JSON body: {"error": "Rate limit exceeded", "retry_after": <seconds>}
http
HTTP/1.1 429 Too Many Requests
content-type: application/json
retry-after: 23
x-ratelimit-limit: 100
x-ratelimit-remaining: 0
x-ratelimit-reset: 1714128360

{"error": "Rate limit exceeded", "retry_after": 23}

Audit API exception

The Audit API normally wraps responses in the {data, meta, error} envelope, but its :strict_rate_limit 429 responses use the platform shape above (no envelope). See Audit overview → Rate-limit responses for details.

Window semantics

Windows are fixed-window: counters reset on a clock boundary derived from (unix_timestamp / window_seconds) * window_seconds. A client that hits the bucket at second 59 of a 60-second window will see counters reset 1 second later, not 60 seconds later.

Because reset is wall-clock aligned (not sliding), a burst at the end of one window followed by a burst at the start of the next can temporarily exceed the per-minute average — by design, to keep the limiter cheap.

Disabling for tests

The rate limiter checks Application.get_env(:krafter, :rate_limit_enabled, true) — when the value is false, the plug is a no-op. Production has it enabled; the test environment disables it so test suites are not throttled.

Built by Krafter Studio