Rate Limits

Sliding-window per-key. Sandbox is 10× production. Load-test partners can upgrade to 50×.

Default tiers

Endpoint category Production sk_live_* Sandbox sk_test_*
Read (GET /v1/markets, GET /v1/candles, etc.) 60 req/min 600 req/min
Account (GET /v1/account/*) 30 req/min 300 req/min
Trade (POST /v1/orders, DELETE /v1/orders/{id}) 30 req/min 300 req/min
Create (POST /v1/markets) 10 req/min 100 req/min
Faucet (POST /v1/sandbox/faucet) n/a 1 req/hour
WebSocket connections per key 10 concurrent 100 concurrent

Response headers

Every response includes:

X-RateLimit-Limit:     60
X-RateLimit-Remaining: 47
X-RateLimit-Reset:     1786190460  (UNIX seconds when window resets)

On 429 RATE_LIMITED you additionally get:

Retry-After: 13    (seconds until you can retry)

Upgrading tier

Partners running load tests, MMs, or high-frequency strategies can request higher tiers:

TierMultiplierRequires
DefaultAny API key
PartnerSigned partner agreement
Market Maker20×MM agreement + committed spread
Load Test (sandbox only)up to 50×Email partners@predictasiax.com with test plan

Circuit breaker

Sandbox has a fleet-level circuit breaker: if VM CPU exceeds 80% for 30 seconds, all sandbox rate limits temporarily drop to production baseline regardless of tier. This protects sandbox availability for all partners. Circuit resets automatically when CPU falls below 60% for 60 seconds. WS event circuit_breaker fires when triggered.

IP-level burst

Unauthenticated public reads (no X-Api-Key) are limited per source IP: 30 req/min. Mint an API key to raise your quota by the tier multiplier.

Backoff code (Python)

import time, requests

def retry_with_backoff(fn, max_tries=5):
    for attempt in range(max_tries):
        r = fn()
        if r.status_code == 429:
            wait = int(r.headers.get("Retry-After", 2 ** attempt))
            time.sleep(wait)
            continue
        return r
    raise RuntimeError("max retries exhausted")

r = retry_with_backoff(lambda: requests.get(
    "https://predictasiax.com/api/v1/markets",
    headers={"X-Api-Key": "sk_live_ABC123"}
))
print(r.json())

WebSocket message throttle

Server-to-client messages have no rate cap. Client-to-server messages (SUBSCRIBE / UNSUBSCRIBE / AUTH / LOCALE) are limited to 10 msgs/sec per connection to prevent DoS. Excess messages are silently dropped after a warning log. Well-behaved clients batch subscriptions into a single SUBSCRIBE call:

# Good:
{"method":"SUBSCRIBE","params":["fast_tick","signals","orderbook","account"]}

# Bad (4 messages instead of 1):
{"method":"SUBSCRIBE","params":["fast_tick"]}
{"method":"SUBSCRIBE","params":["signals"]}
{"method":"SUBSCRIBE","params":["orderbook"]}
{"method":"SUBSCRIBE","params":["account"]}
Don't retry on 4xx (except 429). A 400 MISSING_FIELD won't fix itself with retry — you'll just waste quota. See Retry policy.