XKOVA Docs

Rate Limits

The API limits how fast a caller can send requests so one integrator cannot crowd out another or overwhelm a value-moving path. Every response tells you where you stand, and a request over the limit is rejected with a clear signal to wait and retry.

How limits are applied

Limits use a fixed-window counter model: your quota allows a set number of requests per time window and resets as a whole when the window rolls over. All requests from an API key share a single per-key request quota set by the tenant's plan tier, so every endpoint, reads and value-moving writes alike, draws from the same window, and a burst of reads can consume the quota that writes would otherwise use. Unauthenticated requests are held to a separate per-IP abuse baseline.

  • Base: 60 requests per minute per API key.
  • Growth: 600 requests per minute per API key.
  • Scale: 3000 requests per minute per API key.
  • Unauthenticated: about 100 requests per 10 seconds per IP, an abuse baseline that applies before an API key is resolved.

A higher plan tier raises the per-minute request ceiling for your API key. Value-moving writes, such as creating a payment or a funding or off-ramp request, count against this same per-API-key quota as any other request. The unauthenticated sign-in, session, and sign-up endpoints are the exception: they are limited separately per route by IP to resist brute force. The sandbox runs deliberately tight limits so your integration learns to back off before it reaches production.

Response headers

Every non-rejected response carries the current state of the relevant bucket, so you can pace yourself without ever hitting the limit.

HeaderMeaning
X-RateLimit-LimitThe request quota for your API key's plan tier.
X-RateLimit-RemainingRequests you can still make before the bucket empties.
X-RateLimit-ResetWhen the bucket's window resets and accepts requests again.

When you exceed a limit

A request over the limit is rejected with 429 and the rate_limited error code. The response carries a Retry-After header with the number of seconds to wait before trying again.

HTTP/1.1 429 Too Many Requests
Retry-After: 2
Content-Type: application/json

{
  "code": "rate_limited",
  "message": "API rate limit exceeded.",
  "correlation_id": "..."
}

Backoff

Handle a 429 the same way every time:

  1. Read Retry-After and wait at least that long.
  2. Retry the request. If it fails again, increase the wait (exponential backoff) and add a small random jitter so parallel workers do not all retry on the same beat.
  3. Cap the total retries and surface a clear failure rather than looping forever.
Pair backoff with idempotency on writes. A value-moving request that timed out may have succeeded server-side, so retry it with the same idempotency key to avoid a double send. See Idempotency.
Two account-state signals are not throttles and must not be treated as a transient 429. A suspended tenant (tenant_suspended) is short-circuited on every request and returns 503 Service Unavailable. A read-only tenant (tenant_read_only) returns 403 Forbidden, and only on outbound money-movement routes, so reads and non-money-moving writes still succeed. Neither is retryable and neither carries a Retry-After header.

Related

See Errors and Correlation IDs for the full error envelope and which codes are retryable, Idempotency for safe write retries, and Pagination when paging quickly through a large result set.