BotRadixBotRadixDocumentation

Errors, limits and versioning

Error format, rate limits, headers and the versioning policy.

Error format#

Every error from /api/dex/v1 has the same JSON shape and an HTTP status that matches it:

json
{
  "success": false,
  "error": {
    "schema_version": "botradix-dex-error-v1",
    "code": "SCOPE_NOT_ALLOWED",
    "message": "…",
    "retryable": false,
    "details": {},
    "request_id": "dxr_…"
  },
  "request_id": "dxr_…",
  "api_version": "v1",
  "api_schema_revision": "botradix-dex-api-v1.1"
}

Use error.code in your logic (messages can change) and keep request_id when you contact support. retryable: true means the same request may succeed later.

Common errors#

HTTPCodeMeaning
400(validation codes)A field is missing, malformed or out of range. prepare validates the body before the key
400IDEMPOTENCY_KEY_REQUIRED/orders/prepare needs a valid idempotency key
401API_KEY_REQUIREDNo API key was sent
401API_KEY_INVALIDThe key is malformed or wrong
401API_KEY_INACTIVEThe key is revoked or expired
403SCOPE_NOT_ALLOWEDThe key lacks the scope this endpoint or action needs
403IP_NOT_ALLOWEDThe request IP is not in the key's allowlist
403delegated_executor_binding_requiredDelegated execution needs an executor bound to the key
409IDEMPOTENCY_KEY_CONFLICTThe idempotency key was used with a different payload in the last 24 hours
409DUPLICATE_CLIENT_ORDER_IDThe client_order_id was used with a different payload
409active_api_key_limit_reachedThe wallet already has 10 active keys
429RATE_LIMITEDA per-key limit was exceeded
503REALTIME_STREAM_ASYNC_WORKER_REQUIREDThe SSE stream is not enabled on this service

Version 2.2 codes#

CodeMeaningWhat to do
COMMITTED_EXECUTOR_REQUIREDOn a market with V2 risk rules, opening, increasing or inverting through the delegated route is executed committed to a price observed after your order, and only a protocol executor can do it. Your executor is not oneUse the BotRadix executor bound to your key
INVERT_NEEDS_AGENTOn a market with V2 risk rules, the inversion cannot be sent as a direct wallet transactionUse your trading key (or the delegated route with a protocol executor)
INTENT_CREATED_AT_BEFORE_RECEPTIONThe order's commitment time is earlier than its reception by the service (2 s margin); nothing is sentPrepare it again
WALLET_MARGIN_NEEDS_AGENTA market of your cross account has its session closed: the isolated margin comes from your wallet, signed by your wallet or co-signed by your wallet together with your trading key. "Margin +" needs the trading keyEnable the trading key and retry
WALLET_MARGIN_NEEDS_WALLET_SIGNATUREMargin from your wallet needs your wallet's signature for this operation; a trading session cannot bring itRepeat the operation and sign it with your wallet (or use your trading key, with your wallet's co-signature)
WALLET_COSIGN_REQUIREDYour wallet's co-signature for margin from your wallet is missingConfirm again and sign with your wallet
WALLET_COSIGN_SIGNATURE_INVALIDThe co-signature does not match this wallet or this operation; nothing movedPrepare it again; if it repeats, write to support@botradix.com
WALLET_COSIGN_INVALIDThe prepared co-signature does not match the operation; nothing movedPrepare it again
WALLET_MARGIN_BALANCE_REQUIREDWith that session closed, your wallet lacks enough USDC for the margin (and, for an open, the largest fees and, for a limit or stop order, the bond). details.wallet_margin_usdc gives the amountAdd USDC to your wallet or wait for the session to reopen
LIQUIDITY_CAPACITY (also on /orders/execute)The vault refused the risk: its share for this market and direction is full, or an order that joins a position with a backed take profit would push it over (the preview does not always anticipate this). Since v2.2 the delegated route answers it with HTTP 400 instead of a 503 with VAULT_CAPACITY; details.max_open_usdc gives the room when it can be estimatedTry the opposite side, a smaller size or later

When inverting next to an opposite-side net position with the same key, the response may return that net position's id: there is no DexPositionOpened, but a DexPositionChanged of the net position.

Why a prepared action is not executable#

When prepare succeeds but execution.ready is false, execution.reason explains it:

ReasonMeaning
withdraw_margin_insufficientThe withdrawal would leave too little free margin. Withdraw less or close positions
oracle_unavailableA current market price is not available on chain right now; try again shortly
funding_unavailable / funding_staleThe market's funding data must be updated before new positions can open
market_session_closedThe market is outside its trading session
price_out_of_boundsThe price moved beyond your allowed slippage; prepare again
price_tick_rejectedThe price does not match the market tick size
contract_limits_rejectedThe order exceeds a contract limit (size, leverage) or has expired
vault_capacity_unavailableNot enough liquidity capacity for this position right now
market_risk_pausedNew positions are paused on this market
market_not_bound / dex_not_operationalThe market is not enabled for trading yet
invalid_take_profit / invalid_stop_lossTP or SL is on the wrong side of the current price
intent_expiredThe preparation expired; prepare again
simulation_unavailableThe pre-trade simulation could not run; prepare again in a moment
contract_preflight_failedThe contract would reject the action under current conditions

Rate limits#

Each API key has its own limits, counted in 60-second windows:

LimitDefault
All requests3,600 per minute
/orders/prepare900 per minute and 40 per second
Account reads (/account, /positions…, /orders…, /collateral, /transactions, /trades, /orders/execute)3,000 per minute
/candles900 per minute

Per-key quotas can be set when a key is created or rotated, up to these caps: requests 30,000, orders 12,000, burst 1,000, account reads 20,000 and historical reads 12,000 per minute.

On top of the per-key limits, the network edge allows up to 100 requests per second per IP address (bursts of 200) and 60 per second per API key (bursts of 120). Edge rejections are plain 429 responses without the JSON body.

Responses include X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds when the window resets) for the general per-key bucket. 429 responses carry no Retry-After: back off exponentially with jitter (for example 1, 2, 4, 8 seconds).

Retries#

  • Retry on 429, on 5xx and when error.retryable is true, with exponential backoff.
  • Reuse the same idempotency key, with the same body, when you retry a prepare or an execute, so a lost response does not create a second preparation or a second order.
  • Never blindly retry a broadcast transaction: check /transactions or /native/executions/{tx_hash} first.

Response headers#

HeaderValue
X-BotRadix-API-Versionv1
X-BotRadix-API-Schemabotradix-dex-api-v1.1
X-Request-IDYour request ID, or one generated by the server (dxr_…)
Cache-Controlno-store

Versioning policy#

  • /api/dex/v1 is the stable integration contract. New fields and endpoints can be added to v1 without notice; your client should ignore fields it does not know.
  • Changes that would break existing integrations are published under a new version.
  • GET /meta returns the current schema revision and capabilities.
  • Unversioned endpoints used by the web terminal (/api/dex/… without v1) can change at any time.

Last updated: 2026-10-12