API REFERENCE
Rate Limiting
Default production limits scale to your contracted volume. Both global and per-action-class caps apply; whichever you hit first triggers a 429. Sandbox has separate, lower limits for integration testing.
Default limits
| Scope | Limit | Burst |
|---|---|---|
| Global (production, per tenant) | 200 req/sec | 400 |
| Per action class (production, per tenant) | 50 req/sec | 100 |
| Per user (production) | 10 verify/min per user_id | — |
| Sandbox (per tenant) | 20 req/sec | 40 |
Higher production limits are negotiated as part of your contract and apply automatically to your production keys.
Response headers
| Header | Value |
|---|---|
X-RateLimit-Limit | Your current limit |
X-RateLimit-Remaining | Calls left in the current window |
X-RateLimit-Reset | Unix seconds until window resets |
Retry-After | Present only on 429 responses, seconds to wait |
Recommended retry behavior
On 429, sleep for Retry-After seconds, then retry the request
with the same X-Lorica-Idempotency-Key. Don't invent your own
backoff — Retry-After is computed from your tenant's actual cap and current
usage and is always the correct minimum wait. Add jitter on top if you
have many concurrent retries.
# Python
import time
import requests
def verify_with_retry(payload, max_attempts=3):
for attempt in range(max_attempts):
r = requests.post(URL, headers=HEADERS, json=payload)
if r.status_code == 200:
return r.json()
if r.status_code == 429:
wait = int(r.headers.get("Retry-After", "1"))
time.sleep(wait + 0.5) # small jitter
continue
r.raise_for_status()
raise RuntimeError("exceeded retries") Operational note
Hitting the per-user 10/min cap usually indicates a UI bug — a real user
won't intentionally trigger ten verify calls in a minute. If you see
sustained per-user 429s, check your retry logic and the user-facing
error handling.