MUTEX
Developers · Trading API

Trading API

A REST API for algo traders. A trading key drives one account: an API-type bot account or your Perps vault - it cannot touch your main perp account and cannot move money (no deposits, withdrawals, swaps, or transfers). The steps below set up a bot; for a vault, see Trade a vault. Base URL https://trade.mutex.trade.

Open interactive reference live OpenAPI spec · always in sync with the deployed API
Step 1

Create an API bot

In the web app, create a bot with template AI Agent + API (no strategy params - the engine is inert; you drive it). This gives you a dedicated sub-account the key will trade.

Step 2

Fund and execute it

Fund the bot's account in the web app (funding stays in the web app - never on this API). Balance shows up on GET /v1/account.

Then execute the bot in the web app. Until you do it is ready, and every order answers 409 bot_not_trading.

Step 3

Generate a key

In the app, open Settings, then the API tab, and generate the bot's trading key(an API bot lists there while it is ready, running or paused). It's shown once - copy it now (mtx_bot_…). Affiliates generate an mtx_aff_… key on the same tab instead - see the Affiliate API.

Check it's wired up (a vault's key answers "bot":null and its vault's id in "vault"):

curl https://trade.mutex.trade/v1/key \
  -H "Authorization: Bearer mtx_bot_…"
# {"kind":"trading","display_prefix":"mtx_bot_a1b2","bot":"0190…","vault":null,"created_at":1721480400000}
Step 4

Place your first order

curl -X POST https://trade.mutex.trade/v1/orders \
  -H "Authorization: Bearer mtx_bot_…" \
  -H "Content-Type: application/json" \
  -d '{"type":"limit","symbol":"BTC","side":"buy","size":"0.01",
       "price":"50000","time_in_force":"gtc","client_order_id":"algo-1"}'
  • Markets are plain coin symbols ("BTC"). Numbers are decimal strings on output; inputs accept strings or JSON numbers. Timestamps are Unix ms.
  • client_order_id (≤ 64 chars, not UUID-shaped) is your idempotency + lookup handle, unique per account (one bot or one vault). A duplicate is 409 duplicate_client_order_id before anything reaches the venue.
  • Attach take_profit / stop_loss (trigger prices) to an entry order (market or limit) and they are placed as reduce-only conditionals after the entry submits. Each comes back under its own key, as an order or as its own error. They are not linked: when one fires, cancel the other yourself.

Preview first if you want the fee/margin estimate: POST /v1/orders/preview.

Step 5

Read fills

curl "https://trade.mutex.trade/v1/fills?limit=50" \
  -H "Authorization: Bearer mtx_bot_…"
# {"fills":[{"fill_id":123456,"symbol":"BTC","side":"buy","role":"taker",
#            "size":"0.01","price":"50000","fee":"0.5","liquidation":false,
#            "created_at":1721480400000}],"next_before":"123456"}

Page with the cursor: pass the response's next_before as ?before=. Positions and balance are GET /v1/positions and GET /v1/account.

fee is the total USDC charged for that fill, the venue's fee plus the MUTEX fee: the same amount debited from the account's balance. It is null, never 0, on a venue row that cannot be priced.

Contract

Auth

Authorization: Bearer <key> on every route except market data (/v1/markets, /v1/candles) and the vaults' public reads (/v1/vaults*), which need no key. A trading key on an affiliate route is 403 wrong_key_kind, and so is any other key on a trading route. Keys never expire. A key dies when it is revoked or regenerated, when its bot run ends, or when its vault closes. On a suspected leak, revoke the key (regenerate in the app or ask an admin) - the bot or the vault keeps running.

Vaults

Trade a vault

A vault has a desk, its kind: perp or spot, picked when it is made. A lead trader can run one open vault on each desk.

Only a Perps vault has a trading key; a Spot vault's lead trader trades it in the app or with an agent key. The lead trader makes the key on the vault's Settings tab (it also lists in Settings, API). It is the same mtx_bot_… kind on the same routes, and it trades that vault and nothing else.

  • Every order is for the vault's depositors too. The list, the fills, the positions and cancel-all cover every order of the vault, also the ones placed in the app.
  • A vault's orders can change size or order_id by themselves: a withdrawal from the vault makes each of its positions and each resting order that is not reduce-only smaller, and an order may be cancelled and placed again smaller. Read GET /v1/orders before you act on an id you kept.
  • While Mutex stops the vault's trading, new orders and leverage changes answer 409 trading_stopped; reduce-only orders and cancels still work.
  • The key ends when the vault closes.

The vaults' public reads need no key. GET /v1/vaults?kind=spot (or perp) lists one desk's vaults; without kind, both. A Perps vault's positions are GET /v1/vaults/:id/positions and a Spot vault's holdings GET /v1/vaults/:id/holdings; each answers 409 wrong_desk on the other desk's vault.

Contract

Rate limits

Two per-key fixed 1-minute buckets:

BucketLimitApplies to
orders60 / minplace, modify, cancel, cancel-all, set leverage
reads300 / mineverything else with a key, preview included

Every keyed response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (seconds to reset). Over the limit → 429 rate_limited plus Retry-After (seconds) - back off until then. Sustained flooding (> 1000 429s in 10 min) auto-suspends the key for an hour (key_suspended); the third suspension in 24 h lasts until you regenerate the key or an admin re-enables it. The vaults' public reads have no key, so they count per IP: 300 requests a minute by default, with the same headers and the same 429 rate_limited. Every request also passes a per-IP flood limit at the edge (20 requests per second, burst 40); that 429 is a plain page, not the JSON error below.

Contract

Error codes

Honest HTTP status + { "error": { "code", "message" } }. Match on code.

CodeHTTPMeaning
service_disabled403The API (or the affiliate API) is not enabled.
missing_key401No Authorization: Bearer.
invalid_key401Unknown key.
wrong_key_kind403A trading key on an affiliate route, or another kind of key on a trading route.
key_suspended403Auto-suspended for abuse; retry after the window, or regenerate.
account_suspended403The owning user is disabled.
rate_limited429Bucket exhausted; see Retry-After.
invalid_request400Bad params or body (validation detail in message).
invalid_json400The body is not valid JSON.
bots_disabled403Bots are off platform-wide.
trading_halted503A platform or bot trading halt is on. Cancels still work.
bot_not_trading409The bot cannot place orders: ready (never executed), paused, stopping, stopped, liquidated, quarantined, or still provisioning. Reads and cancels only; message names the state and what clears it.
trading_stopped409A vault whose trading is stopped by Mutex: reduce-only orders and cancels only.
vault_closed409The vault is closed.
vault_not_open409The vault is not open yet.
wrong_desk409A vault read of the other desk: positions on a Spot vault, holdings on a Perps vault.
unknown_market404 / 400No such symbol: 404 on market data, 400 on order routes.
order_not_found404No order by that id / client_order_id.
duplicate_client_order_id409client_order_id already used on this account.
order_rejected4xx / 502The order was refused (reason in message); 502 when the venue refuses a cancel-all or a leverage change.
conflict409Modify/cancel raced venue state.
venue_unavailable502Upstream venue unreachable; retry.
internal_error500Unexpected; safe to retry idempotently.
Scope

Not on this API

Deposits, withdrawals, swaps, transfers, TWAP orders, WebSocket streams, and your main perp account. Slice your own algo orders; poll for market data. The full endpoint set, request/response schemas and examples live in the interactive reference.

Read-only reference