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 the buckets below, and counters in different buckets are independent — exceeding one bucket does not affect another for the same caller.

Buckets ​

BucketLimitApplied to
defaultyour plan's rate × 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.
flags_client_ip120 req / 60sFlags client-key endpoints (/api/v1/flags/client/...), keyed per client IP.
flags_clientyour plan's rate × 60sThe same endpoints, keyed per client key rather than per team — so browser traffic cannot spend the budget your server-side traffic depends on.

The default bucket follows your plan ​

The default bucket is sized from the API rate your plan sells, over the same 60-second window:

PlanAPI ratedefault bucket
Free10 req/s600 req / 60s
Starter50 req/s3,000 req / 60s
Pro100 req/s6,000 req / 60s
Scale500 req/s30,000 req / 60s
Enterpriseunlimitednot rate limited

Unauthenticated requests have no plan to read and fall back to 100 req / 60s.

The fixed-limit buckets cap an endpoint rather than a customer: an expensive query is expensive on every plan.

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}

retry_after is the time left in the current window, rounded up to a whole second and never 0. Wait that long and the next request lands in a fresh window, at or after x-ratelimit-reset.

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.

Monthly plan quota ​

The rate limit above is not the only thing that returns 429. Your plan also caps how much of each service you may use per month, and that cap is a different counter with a different reset and a different fix.

Rate limitPlan quota
Window60 secondsCalendar month, UTC
ResetsOn the next minute boundaryOn the 1st of the month
Body{"error": "Rate limit exceeded", "retry_after": 23}{"error": "Monthly usage limit exceeded. Upgrade your plan."}
FixRetry after the windowRaise the plan, or send less

A 429 carrying retry_after will clear on its own. A 429 that says Monthly usage limit exceeded will not — retrying it is pointless until the month rolls over.

Checking what is left ​

GET /api/v1/usage reports this month's usage against your plan for every metered service:

bash
curl https://app.krafter.dev/api/v1/usage \
  -H "Authorization: Bearer kr_live_abc123def456"
json
{
  "period_start": "2026-09-01T00:00:00Z",
  "period_end": "2026-10-01T00:00:00Z",
  "plan": "pro",
  "services": {
    "mail":  {"metric": "emails_sent", "used": 9812, "limit": 10000, "remaining": 188, "unlimited": false},
    "forms": {"metric": "submissions", "used": 41, "limit": 1000, "remaining": 959, "unlimited": false}
  }
}

On a plan with no ceiling for a service, limit and remaining are null and unlimited is true — so remaining is never a number you have to second-guess.

Scopes. A key sees the services it is scoped for and no others: a mail:send key gets services.mail alone. billing:read returns every service. A key with none of these scopes gets 403.

Warnings before the wall ​

Krafter emails the team owner when a service passes 80% and again at 95% of its monthly limit — once per threshold, per service, per month. The mail names the service, the numbers, and what runs out.

You do not have to subscribe to anything, and the warning is sent by the platform, so it does not consume the quota it is warning you about.

Shed load before the cap, not after

If a service is on a critical path — sign-in codes, for instance — poll /api/v1/usage and stop sending what you can do without (digests, reminders) while there is still room for what you cannot.

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