KasarKasar Docs
API Reference

Rate Limits

Per-endpoint rate limits, response headers, and how to handle 429 Too Many Requests correctly.

Kasar applies rate limits across the whole API surface — the public REST v1 endpoints, the MCP tool endpoint, and sensitive auth/OAuth/extension routes — to prevent abuse: credential brute-force, email spam, account enumeration, OAuth client spam, runaway LLM cost, unbounded meeting-recorder provisioning, and cost-asymmetric or runaway API traffic. Limits are enforced with a sliding window stored in Redis and counted per the identity key documented below.

Rate limiting is applied in addition to authentication. An authenticated token is never immune to rate limits.

Response Headers

Every rate-limited response — whether allowed or denied — carries standard headers so you can adapt your client behavior:

HeaderDescription
X-RateLimit-LimitMaximum number of requests allowed within the window
X-RateLimit-RemainingRequests still allowed in the current window
X-RateLimit-ResetEpoch seconds (UTC) when the window resets
X-RateLimit-PolicyIdentifier of the policy that was applied (e.g. auth:magic-link)
Retry-AfterOnly on 429: seconds to wait before retrying

429 Response Body

When a request is denied, the API returns HTTP 429 Too Many Requests:

{
  "error": "Too many requests",
  "policy": "auth:magic-link",
  "retryAfterSeconds": 420
}

Respect the Retry-After header. Implement exponential backoff for automated clients, and surface a clear message (e.g. "Please wait a few minutes before trying again") to end users.

Current Policies

Each policy below applies to a specific endpoint or family of endpoints. The identity key column describes which attribute the limit is scoped to — for example, IP + email means two different emails from the same IP share no bucket, and the same email from two different IPs share no bucket either.

Authentication

PolicyEndpointLimitWindowIdentity key
auth:magic-linkPOST /api/auth/magic-link1510 minIP + email
auth:signupPOST /api/auth/signup101 hourIP
auth:verify-tokenGET /api/auth/verify-token2010 minIP + email
auth:invite-verifyPOST /api/auth/invite/verify2010 minIP
auth:invite-sendInvitation creation (server action)201 hourOrganization id

Why these limits?

  • auth:magic-link — Prevents an attacker from spamming your users' inboxes or enumerating which emails have an account.
  • auth:signup — Prevents bulk bot-driven organization creation.
  • auth:verify-token — Caps brute-force attempts on verification tokens. Legitimate users only consume the link once.
  • auth:invite-verify — Caps brute-force on invitation tokens.
  • auth:invite-send — Caps email spam from a compromised admin account. Keyed by organization rather than IP; 20 per hour comfortably covers onboarding a full team in one sitting.

auth:invite-send is enforced on a server action, not an HTTP route. Server actions have no Response object, so instead of a 429 they surface an in-band error in the action result. The other authentication policies run on HTTP routes and return a standard 429.

OAuth

PolicyEndpointLimitWindowIdentity key
oauth:registerPOST /api/oauth/register201 hourIP
oauth:tokenPOST /api/oauth/token6010 minIP

Why these limits?

  • oauth:register — Dynamic Client Registration (RFC 7591) is an open endpoint; the cap stops spam creation of oauth_clients rows per IP.
  • oauth:token — Caps abuse of the authorization_code grant per IP. Codes are already single-use, PKCE-protected, and expire after 60 seconds, so this is a backstop.

AI & LLM

PolicyEndpointLimitWindowIdentity key
agent:streamPOST /api/agent/stream1201 hourAuthenticated user id (falls back to IP)

Why this limit? Each streaming run triggers Claude API calls whose cost scales with token usage. A capped per-user ceiling prevents a single compromised session from running up a large bill.

Chrome Extension (Meeting Recorder)

PolicyEndpointLimitWindowIdentity key
extension:session-createPOST /api/extension/sessions2010 minAuthenticated user id
extension:session-create-orgPOST /api/extension/sessions10010 minOrganization id
extension:session-stopPOST /api/extension/sessions/{meetingId}/stop6010 minAuthenticated user id
extension:session-refresh-wsPOST /api/extension/sessions/{meetingId}/refresh-ws3010 minAuthenticated user id

Why these limits? Starting a recording provisions a meeting-bot pod slot and a Deepgram transcription session — real per-call cost. The per-user extension:session-create cap bounds the blast radius of a single compromised extension token; extension:session-create-org adds an org-wide ceiling so an organization with many compromised tokens can't scale recordings past its aggregate budget. extension:session-stop is looser because legitimate clients may retry stop on transient errors, and extension:session-refresh-ws covers proactive wsToken refreshes before the 30-minute token TTL expires.

POST /api/extension/sessions is gated by two policies at once: the per-user extension:session-create cap is checked first, then the org-wide extension:session-create-org cap. Either one can deny the request with a 429.

Public API & MCP Tools

Every authenticated REST v1 request and every MCP tool/resource call is rate-limited. A token is an identity, so a user's REST and MCP traffic count against the same per-identity buckets.

PolicyApplies toLimitWindowIdentity key
api:ipAll REST v1 requests (pre-auth)10001 minIP
api:globalAll REST v1 + MCP tool/resource calls6001 minWorkspace user id
api:expensiveGlobal search, aggregation, duplicate detection901 minWorkspace user id
api:bulkImport + export jobs2010 minOrganization id
api:messaging-sendReal outbound sends (crm_send_message, email/LinkedIn/WhatsApp)301 hourWorkspace user id

Why these limits?

  • api:ip — A pre-auth ceiling. The token-verification path (JWT verify + token lookup + metadata load) is itself work, so capping per IP before auth stops floods of arbitrary Bearer ksr_* tokens. Generous, because office NATs share one IP — the real throttle is per-identity.
  • api:global — The per-identity ceiling across the entire data surface. Enforced at both chokepoints (REST withAuth and the MCP context builder), so a user's REST + MCP calls share one budget. 600/min (~10/s) comfortably covers an interactive AI agent or batch client while stopping a runaway loop.
  • api:expensive — Cost-asymmetric reads fan out across objects or scan large row sets, so they get a tighter cap. Enforced inside the shared handler, so REST, MCP, and the in-app AI agent all inherit it.
  • api:bulk — Import/export can move thousands of rows and spawn a worker; keyed per organization so the whole org shares the budget.
  • api:messaging-send — Only the actual send consumes it (previewing a message is free), keyed per user so a compromised token can't spam outbound mail.

The api:expensive, api:bulk, and api:messaging-send caps are enforced inside the shared handler that backs each operation, so the REST endpoint, the MCP tool, and the in-app AI agent are all throttled by one consistent rule. A request can therefore consume two buckets at once: the api:global ceiling plus the matching cost-class policy.

Handling 429 in Your Integration

TypeScript example:

async function callKasar(path: string, init: RequestInit, attempt = 0): Promise<Response> {
  const res = await fetch(`https://kasar.app${path}`, init)

  if (res.status === 429 && attempt < 3) {
    const retryAfter = Number(res.headers.get('Retry-After') ?? 60)
    await new Promise((r) => setTimeout(r, retryAfter * 1000))
    return callKasar(path, init, attempt + 1)
  }

  return res
}

Python example:

import time
import requests

def call_kasar(path, attempt=0, **kwargs):
    res = requests.request(url=f"https://kasar.app{path}", **kwargs)
    if res.status_code == 429 and attempt < 3:
        retry_after = int(res.headers.get("Retry-After", 60))
        time.sleep(retry_after)
        return call_kasar(path, attempt + 1, **kwargs)
    return res

Operational Notes

  • Fail-open on infrastructure issues. If the Redis backend is unreachable, requests are allowed through rather than blocked. Rate limiting is a defense-in-depth measure and never becomes a single point of failure for authentication.
  • Sliding window. The window slides with each request — not a fixed calendar bucket. A request made at t=0 ages out exactly windowSec later.
  • Policy IDs are stable. The X-RateLimit-Policy header value is part of the public contract. If you log it, you can rely on the identifier not changing.

Requesting Higher Limits

If your integration has a legitimate need for a higher ceiling (dedicated tenant, enterprise workflow), contact support with:

  1. The policy name (X-RateLimit-Policy)
  2. The expected peak request rate
  3. A short description of the integration

We review requests case-by-case.

On this page