Skip to main content

Scheduled rate-limit migration

Action for trading integrations: obtain a verified uid-scoped JWT for each account you trade before the November 4 cutover. At cutover, all wallet-only JWTs—including those issued earlier—immediately move to the 1 request/second sustained allowance. There is no grace period or grandfathering. Test the scoped flow on the same network as your trading account.
Relay is scheduled to complete its account-scoped rate-limit migration on Wednesday, November 4, 2026 at 12:00 PM Eastern Time (EST, UTC−05:00; 17:00 UTC). At that time, requests without a verified account-scoped token will use a 1 request/second sustained per-wallet allowance, including requests made with wallet-only JWTs issued before cutover. There is no grace period or exception for existing tokens. Those tokens remain valid, but their requests use the lower quota because Relay applies the limit when it processes each request, not when it issues the JWT. The new sustained rate is a scheduled change, not a claim that the lower rate is already active. The post-cutover burst allowance has not been announced; do not assume an instantaneous one-request ceiling. For account-scoped trading, the mainnet and testnet configuration currently specifies these per-replica defaults: Both verified-account buckets must allow a trading submission. A submission costs one request unit, not one per order in a batch. A request admitted to the limiter still counts if a later policy check or simulation rejects it. Subaccounts share their root’s budgets; separate signer keys, WebSocket connections, REST calls, or JWT renewals do not create extra root allowance within one replica. These are not guaranteed end-to-end throughput figures. Existing limits also apply, including 100 requests/second per IP, 200 requests/second per identity, 500 WebSocket messages/second, and a 100-request per-method burst. An additional shared wallet-tier bucket is configured at 1,000 requests/second with a 1,000-request burst; it does not replace the per-wallet limit. Account-specific policy overrides may change an effective allowance. Current buckets are in-memory per Relay replica, not a globally distributed hard quota. Limits are enforced server-side and can change for existing tokens and connections without JWT renewal. Plan to handle throttling, not to multiply connections to evade it. “Requests/second” is the sustained token refill rate; burst is a separate number of requests that can be admitted in a short interval when credit is available. A RATE_LIMITED response is ordinary throttling, not an outage.

Move to account-scoped login

  1. Confirm the trading wallet has live AccountCore TRADE authority for the exact account ID it will trade. For a child account, use that child’s ID.
  2. Call the unchanged public POST /auth/challenge for the wallet and sign the exact returned message.
  3. Exchange the signed challenge at public POST /auth/token with an additional uid string, for example "10". It must be a positive canonical uint40 decimal value, without leading zeroes or whitespace. Token exchange shows the request.
  4. Store the new JWT securely and use it for that account. Re-login with uid for each traded account/subaccount: adding an account ID to a submission cannot scope an already-issued unscoped JWT. Set the signed trading header’s accountId to the token’s exact uid. For WebSocket, also set the required upgrade query account_id to that same ID.
  5. Exercise the same signed submissions on testnet, handle RATE_LIMITED with bounded retry, and reconcile broadcasts or uncertain results before submitting another logical action.
Relay verifies the challenge wallet’s current on-chain trading authorization for uid and resolves its root when issuing the token. A successful scoped JWT has a signed kuru_account claim with uid, root_uid, chain_id, and account_core string fields. These are Relay-issued facts; clients cannot self-assert a root ID or gain a higher allowance by merely changing a request body. The token response envelope remains accessToken, tokenType, expiresAt, and wallet; it does not return the quota or root as separate response fields. Token issuance neither registers an account nor grants on-chain permission. Renewal rechecks trading permission; contracts still enforce authorization at execution. Trading admission does not add an authorization RPC for each submission. Methods without a trading header—including signer onboarding/revocation, subaccount creation, and stored-trigger or TWAP cancellation/finalization—use the wallet-based quota even with a scoped JWT. For AccountCore operations, the signed owner/administrator authorizer remains independent of the JWT wallet. Do not put uid on POST /auth/challenge, rename signed accountId, or replace WebSocket account_id with uid. The pinned @toxicflow-labs/[email protected] Relay authenticate()/exchangeToken() methods do not send uid. Until an SDK release supports scoped login, perform the two public authentication calls directly and supply the resulting token to the SDK client’s accessToken/setAccessToken option, or use the documented REST/WebSocket requests directly. See Relay SDK authentication.

When Relay throttles a request

REST returns HTTP 429 with code: "RATE_LIMITED", retryable: true, and retryAfterMs. The equivalent WebSocket request is rejected without closing the connection solely because of the new account limiter. Honor the reported delay, use bounded backoff with jitter, and pace future requests. A retried WebSocket submission needs a new envelope id, because replaying the same id can replay the old rejection. The envelope id is not a signed intent nonce, clientOrderId, or durable idempotency key; retain the original signed identity and reconcile state before retrying. Never blindly resubmit a BROADCAST or an uncertain result. Outcome pushes and transaction.subscribe do not consume the transaction quota, though WebSocket frame limits still apply. See Relay errors and outcomes.