Rate Limits

RiskOS™ implements rate limiting to ensure optimal performance, prevent abuse, and maintain service reliability across all tenants.

Overview

Rate limits control the number of API requests your application can make within a specific time. These limits are allocated at the account level and help maintain system stability while ensuring fair resource allocation.


Rate limit tiers

Sandbox environment

  • Rate Limit: 10 requests per second (TPS)
  • Daily Quota: 1,000 requests per day
  • Purpose: Development, testing, and integration validation
  • Billing: Free tier included with all accounts

Production environment

  • Initial State: 0 TPS (all requests blocked)
  • Activation: Requires approval and configuration
  • Purpose: Live production traffic
  • Custom Limits: Tailored to your specific use case and volume requirements
📘

Production Access Required:

Production accounts start with rate limits disabled (0 TPS) as a safety measure. Contact Socure Support to activate your production environment with appropriate limits.


Rate limits per endpoint

Rate limits are enforced independently for each API endpoint and HTTP method. Every endpoint and method combination has its own limit, so the Sandbox limit of 10 TPS applies separately to each one — it is not a single shared pool across all operations.

For example, in Sandbox these three operations each get their own 10 TPS limit:

OperationMethodPath
Submit an evaluationPOST/api/evaluation
Update an evaluationPATCH/api/evaluation/{eval_id}
Retrieve an evaluationGET/api/evaluation/{eval_id}

Sending 10 POST requests per second does not consume any of the throughput available for PATCH or GET requests. Each combination is metered on its own.

Within each endpoint, the limit still applies at the account level, matching the scope described in Rate limits and subaccounts. It is not split by workflow or use case.


Requesting production access

Contact Socure Support with the following information:

  • Expected request volume (TPS and daily totals)
  • Peak usage patterns and time periods
  • Integration timeline and go-live date

Socure evaluates your request and updates your production limits to an approved throughput level.


Rate limits and subaccounts

Rate limits are allocated at the account level, not per individual API key. All API keys that belong to the same account — including the keys used by its subaccounts — draw from a single shared rate limit allocation.

This means issuing a separate API key for each subaccount does not give each subaccount its own throughput. Whether your subaccounts share one API key or each use their own key, their requests count against the same account-level limit.

📘

Multi-subaccount deployments:

Adding subaccounts or generating additional API keys doesn't automatically increase your rate limit allocation. If you need a higher combined limit — or a dedicated allocation for a specific subaccount — contact Socure Support to review and adjust your limits. This helps high-throughput subaccounts avoid 429 errors.


Rate limit response headers

RiskOS™ returns rate limit headers so your application can monitor usage and implement proactive throttling. The limit headers appear whenever an active rate limit policy applies to your account. The remaining-capacity headers appear when RiskOS™ evaluates the request against that limit, so they are omitted on some responses.

Limit headers

RiskOS™ returns these headers whenever an active rate limit policy applies to your account:

HeaderDescription
X-RateLimit-Limit-DayTotal number of requests allowed per day for your account
X-RateLimit-Limit-WindowTotal number of requests allowed in the current rate limit window

Remaining-capacity headers

RiskOS™ returns these headers when it evaluates the request against your limit — typically on a standard allowed response. RiskOS™ omits them on some responses, including batch endpoints and the day-limit path of a 429, so treat them as optional and check for their presence before relying on them:

HeaderDescription
X-RateLimit-Remaining-DayNumber of requests remaining in the current day
X-RateLimit-Remaining-WindowNumber of requests remaining in the current rate limit window

Additional header on 429 responses

HeaderDescription
X-Retry-AfterNumber of seconds to wait before retrying the request
📘

Proactive throttling:

Use the X-RateLimit-Remaining-Day and X-RateLimit-Remaining-Window headers to monitor your usage in real time. By tracking remaining capacity, your application can slow down or queue requests before hitting the limit — avoiding 429 errors entirely.


Zero-value or missing headers

If responses show X-RateLimit-Limit-Day: 0 and X-RateLimit-Limit-Window: 0 with no X-RateLimit-Remaining-* headers, your account does not have an active rate limit policy. RiskOS™ passes these requests through without rate limit enforcement, so you cannot trigger a 429 for retry testing. Contact Socure Support to provision your Sandbox or Production limits.


Handling rate limit exceeded

HTTP 429 response

When you exceed your rate limit, the API returns a 429 Too Many Requests status with a JSON body that identifies which limit you hit:

{
  "error": "rate_limit_exceeded",
  "message": "Per Day Rate limit exceeded. Please try again later.",
  "namespace": "acc-XXXXXXXXXX",
  "resource": "POST_EVALUATION",
  "retry_after_seconds": 3600,
  "usecase": "@all"
}

The body contains these fields:

FieldTypeDescription
errorstringAlways rate_limit_exceeded for a rate limit 429.
messagestringHuman-readable detail. Per Day Rate limit exceeded... indicates you hit the daily quota; a per-window (TPS) limit returns a message for the current window.
namespacestringThe account the limit applies to. Rate limits are enforced at the account level, so this value identifies your account.
resourcestringThe endpoint and method that was limited, such as POST_EVALUATION for POST /api/evaluation.
retry_after_secondsnumberHow many seconds to wait before retrying. A daily-quota 429 returns a fixed one-hour suggestion (3600); a per-window (TPS) 429 returns the window duration.
usecasestringThe scope of the limit that was exceeded. @all means the account-wide limit across all use cases.

A 429 carries the retry delay in two places — the retry_after_seconds field in the body and, when present, the X-Retry-After response header. Honor whichever your client receives. On a per-window (TPS) 429, the response also includes the standard rate limit headers:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit-Day: 1000
X-RateLimit-Limit-Window: 10
X-RateLimit-Remaining-Window: 0
X-Retry-After: 2

A per-window (TPS) 429 omits X-RateLimit-Remaining-Day, because the per-day quota is not evaluated once the window limit is hit. A daily-quota 429 includes X-RateLimit-Remaining-Day: 0 alongside the window headers. In both cases, read retry_after_seconds from the body for the wait time.


Exponential backoff

To prevent retry storms and ensure smooth recovery from rate limits or transient errors, implement exponential backoff with jitter.

Exponential backoff spaces out retries and randomizes delays to avoid simultaneous retry spikes across multiple clients.

Recommended pattern

When receiving a 429 or 5xx error:

  1. Read the retry delay — the retry_after_seconds body field or the X-Retry-After header (if present) — and wait that many seconds.
  2. If neither is present, retry using exponential backoff:
    • Start with a 1–2 second delay.
    • Double the delay after each retry (2s, 4s, 8s, 16s...).
    • Add a random "jitter" (±1s) to avoid retry collisions.
    • Stop after about 5 attempts or 1 minute.

Example implementation of exponential backoff with jitter:

import time, random

def retry_with_backoff(request_fn, max_attempts=5):
    for attempt in range(max_attempts):
        response = request_fn()
        if response.status_code not in (429, 500, 503):
            return response
        wait = (2 ** attempt) + random.uniform(0, 1)
        print(f"Rate limited. Retrying in {wait:.2f} seconds...")
        time.sleep(wait)
    raise Exception("Max retries exceeded")

Best practices

  • Always respect the retry delay (retry_after_seconds or X-Retry-After) when provided — it overrides your local delay.
  • Don't retry indefinitely; cap retries and log rate-limit events.
  • Queue non-urgent requests to smooth burst traffic.
  • Use idempotency keys to safely retry without duplicating requests.