Rate Limits

SecureCodingHub enforces a per-minute and per-hour cap on every API key. Limits are designed to be invisible during normal operation and to stop runaway scripts before they spam the platform.

Default limits

WindowLimitHeader
Per minute60 requestsX-RateLimit-Window: per_minute
Per hour1,000 requestsX-RateLimit-Window: per_hour

Both windows are evaluated on every request. The narrower window (60/minute) governs short bursts; the hour window governs sustained load. If either window is exceeded the request is rejected with 429 Too Many Requests — you do not need to inspect which window tripped, only honor the Retry-After header.

Response on rejection

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Window: per_minute
X-RateLimit-Limit: 60

{ "error": "rate_limited" }

Retry-After is always an integer count of seconds — the smallest safe delay before the next request will succeed. X-RateLimit-Limit reflects the ceiling for the window that tripped.

Sliding versus fixed windows

The per-minute window is fixed to the calendar minute (UTC). A request at 13:42:59 and one at 13:43:01 fall into different buckets. The per-hour window is a true sliding aggregate over the last 60 minutes. This means that during normal usage you'll never trip the hourly cap unless you are also tripping the minute cap repeatedly.

Recommended client behavior

  • On 429, sleep for at least Retry-After seconds and retry once.
  • Use jitter: retryAfter + random(0, 5) seconds to avoid thundering herd when many workers hit the limit together.
  • If you control batch size, run no more than 50 requests/minute per key — keeps you safely under the cap.
  • For high-volume jobs (data export, mass user provisioning), issue one key per worker rather than sharing a single key.
  • Don't treat 429 as an error to alert on. Alert only on repeated failures after exponential backoff.

Retry pattern

A minimal Node example that respects Retry-After with jitter:

async function call(path) {
  for (let attempt = 0; attempt < 4; attempt++) {
    const res = await fetch(`https://api.limeplate.com${path}`, {
      headers: { Authorization: `Bearer ${process.env.SCH_API_KEY}` },
    });
    if (res.status !== 429) return res;
    const wait = parseInt(res.headers.get('Retry-After') || '60', 10);
    const jitter = Math.floor(Math.random() * 5);
    await new Promise(r => setTimeout(r, (wait + jitter) * 1000));
  }
  throw new Error('exhausted retries on 429');
}

Requesting a higher quota

Most integrations sit comfortably within the default limits. If your workload genuinely needs a higher ceiling — for example, a nightly Looker refresh that pulls progress for 10,000 users — write to support@securecodinghub.com with the API key name, the integration purpose, and the peak requests/minute you expect. We can raise the per-key limits without rotating the token.

What is not rate-limited

Inbound webhook deliveries you accept from SecureCodingHub are not rate-limited from our side. Outbound webhook deliveries we send to your endpoint follow a separate retry schedule documented under Webhooks.