Skip to content

Rate Limits

KINGSTONE enforces per-partner rate limits to ensure fair usage and protect the system from abuse. Rate limits are applied at the API key level, meaning all requests from the same partner share the same limit pool.

Default Limits

ResourceDefault LimitWindow
Spin requests300 per minuteSliding window
All other API calls600 per minuteSliding window

These defaults apply to all partners. Predigy can adjust limits per partner based on expected traffic volume. Contact your account representative if you need higher limits.

Rate Limit Headers

Every response includes rate limit information in the headers:

HeaderDescriptionExample
X-RateLimit-LimitMaximum requests allowed in the current window300
X-RateLimit-RemainingRequests remaining in the current window287
X-RateLimit-ResetUnix timestamp when the window resets1711497600

Use these headers to track your usage and avoid hitting the limit.

What Happens When You Hit the Limit

When you exceed the rate limit, the API returns:

HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1711497600
json
{
  "message": "Spin rate limit exceeded",
  "errorCode": "KS-4291"
}

The Retry-After header tells you how many seconds to wait before retrying.

Error Codes

Error CodeDescription
KS-4291Spin rate limit exceeded — too many spin requests
KS-4292API rate limit exceeded — too many non-spin API requests

Best Practices

Respect the Headers

Read X-RateLimit-Remaining before each request. If the remaining count is low, slow down proactively instead of waiting for a 429 response.

typescript
// Example: Check rate limit headers from a raw fetch response
const response = await fetch(url, { headers: { 'X-API-Key': apiKey } });
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining') || '0');

if (remaining < 10) {
  console.warn(`Rate limit almost exhausted: ${remaining} requests remaining`);
}

Implement Exponential Backoff

When you receive a 429 response, wait and retry with increasing delays:

typescript
import { KingstoneApiError } from '@kingstone/sdk';

async function spinWithBackoff(client, request, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await client.spin(request);
    } catch (error) {
      if (error instanceof KingstoneApiError && error.statusCode === 429 && attempt < maxRetries) {
        const delay = 1000 * Math.pow(2, attempt); // 1s, 2s, 4s
        console.log(`Rate limited. Retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
}

Spread Traffic Evenly

If you need to process a batch of spins (for example, during a high-traffic promotion), spread them evenly over time instead of bursting them all at once. A simple approach:

typescript
// Process 200 spins over 60 seconds instead of all at once
const delayBetweenSpins = 300; // milliseconds

for (const player of players) {
  await client.spin({ gameId: 1, playerId: player.id, wagerUsd: 0.25 });
  await new Promise(resolve => setTimeout(resolve, delayBetweenSpins));
}

Use a Request Queue

For production systems with variable load, use an in-memory request queue with a rate-limited consumer:

  1. Incoming spin requests go into a queue.
  2. A worker pulls from the queue at a steady rate (e.g., 4 per second for a 300/minute limit).
  3. Responses are matched back to the original request via a callback or promise.

This prevents bursts from triggering rate limits even during traffic spikes.

Sandbox vs. Production

Rate limits are the same in sandbox and production. This ensures your integration handles rate limiting correctly before going live.

Next Steps

KINGSTONE by Predigy Inc.