Rate limits are enforced by the REST API and counted in three kinds
of independent bucket:
ip — per source IP (behind the ingress; respects
X-Forwarded-For set by the trusted load balancer). Cheap edge
defence that applies to anonymous and authenticated traffic alike.
An IPv6 caller is counted per /64 prefix, not per address, so every
address in one /64 shares a bucket. IPv4 is counted per address.
wallet — per your key’s associatedWallet (single_wallet
partners) or per the X-User-Wallet header value (multi_wallet
partners). Prevents a single wallet identity from exceeding the
platform’s per-user ceiling regardless of how the calls are
distributed across your keys.
partner — per partner, shared by all of your API keys
(partner-only endpoints such as GET /api/platform/maintenance).
Scaled by your rate tier like wallet buckets.
Every bucket that applies to an endpoint (see the tables below) must
pass. The first exhausted bucket returns 429, with Retry-After set
to that bucket’s wait. The X-RateLimit-Limit / -Remaining /
-Reset headers describe the endpoint’s own bucket (the wallet
bucket where one applies), not necessarily the tightest one. A shared
ip bucket, such as the 6000/min bucket that every /api/orders/*
call from one IP counts against, can run out while these headers
still show wallet headroom. If many wallets trade from one IP, budget
for the ip buckets yourself.
Your partner record carries a rate tier (rateLimitPerMin, default
600). On API-key requests it scales every wallet-scoped limit in
the tables below: effective limit = floor(listed limit ×
rateLimitPerMin / 600), minimum 1. The default 600 gives the
listed values; 1200 doubles every wallet limit. ip-scoped limits
are the same for every caller and are never scaled.
Limits
Order placement / cancellation and the vault split / merge / convert
signatures use per-second wallet limits (anti-burst); other paths
use per-minute windows (a few, such as PUT /api/me/auto-redeem,
longer). The two scopes (ip and wallet) are independent — each
request must pass every applicable bucket.
Trading (writes)
Order reads
Market data
Portfolio (/me/*)
Discovery
Limits above are testnet defaults. Staging and mainnet may run
tighter or looser values — your onboarding runbook contains the
authoritative table for the environment you are connected to.
Counter behaviour
Most buckets are fixed windows: the window starts with the first
request and the count resets when it ends. The wallet buckets of
POST /api/orders/place, /place-batch, /cancel and /cancel-batch
are token buckets instead: the listed limit is the burst size, and it
refills continuously at limit ÷ window (for place, a burst of 20,
then 20 per second sustained). Every request you make either
decrements X-RateLimit-Remaining or, once the bucket is empty,
reports a real countdown to refill.
ip buckets key on the real client IP, not the immediate TCP
peer. We honour X-Forwarded-For from the trusted ingress hop, so
each client is bucketed by the public IP it connects from, not by the
platform’s own edge proxies. Clients that share one public egress IP
(a corporate proxy or NAT) share one ip bucket. If your traffic transits an
extra proxy hop your onboarding manager hasn’t seen, raise it
during integration so we can adjust the trusted-hop count.
Every rate-limited response includes:
X-RateLimit-Reset is delta-seconds, not a Unix epoch. On
fixed-window buckets it is the seconds until the window resets
(60 means “the limit resets in 60 seconds”). On the token-bucket
order endpoints it is 0 while you still have budget and, on a
429, the seconds until the next request fits.
- The HTTP header
Retry-After is set to the same delta-seconds on
every 429.
- On
429 Too Many Requests, the response body uses the standard
error envelope:
error.retryAfterSec carries the same seconds-to-wait as the
Retry-After header, for clients that cannot read response headers.
Retry guidance
- On
429: wait Retry-After (header value, in seconds) before
retrying. Do not retry sooner — the bucket has not refilled.
- Backoff on 5xx: exponential backoff with jitter, starting at
200ms, capped at 10s. Maximum 3 attempts for idempotent calls
(GET, DELETE). Never re-sign a write to retry it after a 5xx —
a new signature is a second order or withdrawal. Resend the
identical request instead. For
POST /api/orders/place and
/place-batch, the same signed order under the same
clientOrderId returns the original result (see
placement_unknown in Error codes). For
POST /api/withdrawals/request, the same body with the same signed
salt never creates a second withdrawal.
- Circuit-break on persistent 5xx: if your error rate exceeds
50% over 30 seconds, stop sending new requests for 60 seconds.
Higher limits
Partners with justified high-throughput needs (market makers, volume
traders) can request a higher rate tier at onboarding:
wallet-scoped ceilings are raised through your partner rate tier
(rateLimitPerMin), which scales every wallet-scoped limit
together (see the top of this page).
ip-scoped limits are the same for every caller behind an IP; they
are not raised per partner or per IP.
- Platform-wide ceilings cannot be exceeded; they protect the shared
matcher.
Contact partners@predictstreet.com
with your expected peak throughput profile.