Skip to content

Rate Limiting

This guide explains how rate limits work on the Synthetix off-chain trading APIs and how to build clients that stay within limits.


Overview

Rate limits protect the system and ensure fair access for all users. Limits apply per subaccount and per IP address.

APIPer-subaccount limitPer-IP limit
REST (POST /v1/info, POST /v1/trade, GET /v1/exchange/status)Yes (trade actions only)Yes (all actions)
WebSocketYes (trade actions only)Yes (all actions)

How Limits Work

Limits use a token bucket model:

  • Each subaccount and IP has a bucket of tokens
  • Each request consumes tokens based on the action
  • Tokens refill over time
  • If a request would exceed available tokens, it is rejected with 429 Too Many Requests

Rate limit buckets (Mainnet)

Token costs are the same across environments. Bucket sizes may differ on Testnet. See Environments for environment-specific configuration.

Limit typeTokens per windowWindowScope
Per-subaccountFee-tier dependent10 secondsTrade actions only
Per-IP10,00010 secondsAll actions

Fee-tier rate limit budgets

REST and WebSocket trade actions use the subaccount's fee tier to determine the per-subaccount token budget. The default Mainnet budgets are:

Tier IDTokens per 10 seconds
tier_01,000
tier_11,200
tier_21,400
tier_31,600
tier_41,800
tier_52,000
tier_62,200
tier_72,500
top_tier2,500
market_maker5,000

Token costs — Trade actions (REST and WebSocket subaccount)

These costs apply to both REST POST /v1/trade and WebSocket trade actions. For placeOrders, cost = base cost × number of orders in the batch.

ActionBase costNotes
addDelegatedSigner100
cancelAllOrders2
cancelOrders2
createSubaccount100
getBalanceUpdates100
getDelegatedSigners20
getDelegationsForDelegate20
getFeeRate10
getFees10
getFundingPayments100
getOpenOrders10
getOrderHistory50
getPerformanceHistory100
getPortfolio10
getPositions10
getRateLimits20
getReferral20
getSubAccount20
getSubAccounts20
getTrades20
getTransfers10
getWithdrawableAmounts100
modifyOrder5
modifyOrderBatch5
placeIsolatedOrder5
placeOrders5Cost = 5 × number of orders in batch
removeAllDelegatedSigners100
removeDelegatedSigner100
scheduleCancel5
transferCollateral5
updateIsolatedMargin5
updateLeverage5
updateSubAccountName100
voluntaryAutoExchange100
withdrawCollateral5

Token costs — info and status actions (per-IP limit only)

These costs apply only to the per-IP bucket. Info and status actions do not consume from the per-subaccount bucket.

ActionCost
getCandles200
getCollaterals50
getExchangeStatus1
getFundingRate250
getFundingRateHistory1,000
getIsWhitelisted250
getLastTrades200
getMarketPrices200
getMarkets50
getMids50
getOpenInterest50
getOrderbook200
getSubAccountIds250

Trade actions also consume from the per-IP bucket using the same costs as the trade table above.


REST API

  • Endpoints: POST /v1/info, POST /v1/trade, GET /v1/exchange/status
  • Per-IP limit: Applies to all actions
  • Per-subaccount limit: Applies to trade actions only

WebSocket API

This includes WebSocket messages and the GET /v1/ws/exchange/status status route.

Trade actions must pass two limits:

  1. Per-IP limit — Applies to all actions. Checked first.
  2. Per-subaccount limit — Applies to trade actions only.

Info and status actions only consume from the per-IP bucket. Multiple WebSocket connections from the same IP share the same per-IP budget.


Rate Limit Response

When you exceed a limit, the API returns:

  • HTTP status: 429 Too Many Requests
  • Error code: RATE_LIMIT_EXCEEDED
  • Category: RATE_LIMIT
  • Retryable: true

REST example

{
  "success": false,
  "clientRequestId": "abc-123",
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "category": "RATE_LIMIT",
    "message": "Rate limit exceeded for action 'placeOrders'",
    "retryable": true
  }
}

WebSocket example (subaccount limit)

{
  "success": false,
  "clientRequestId": "abc-123",
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "category": "RATE_LIMIT",
    "message": "Rate limit exceeded for action 'placeOrders'"
  }
}

WebSocket example (IP limit)

{
  "success": false,
  "clientRequestId": "abc-123",
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "category": "RATE_LIMIT",
    "message": "IP rate limit exceeded"
  }
}

Implementation Notes for Bots

1. Use exponential backoff on 429

When you receive 429 with RATE_LIMIT_EXCEEDED, wait before retrying. Start with about 1 second and increase on repeated failures (e.g. 1s, 2s, 4s).

2. Pace placeOrders requests

Each order in a batch consumes 5 tokens (cost = 5 × batch size). A batch of 20 orders consumes 100 tokens. With a 1,000-token subaccount limit per 10 seconds, large batches can hit limits quickly. Consider:

  • Smaller batches
  • Spacing requests over time
  • Avoiding bursts of many orders at once

3. Share IP budget across clients

Per-IP limits apply across requests from the same IP. If you run multiple bots or connections from one server, they share the same IP budget.

4. Treat rate limits as retryable

Rate limit errors are marked retryable: true. Retry after a short delay instead of treating them as permanent failures.

5. Avoid hammering after a 429

Back off and reduce request rate after hitting a limit. Continuing at the same rate will keep triggering 429s.


Related Documentation