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.
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.
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.
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}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"}'Preview first if you want the fee/margin estimate: POST /v1/orders/preview.
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.
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.
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.
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.
Two per-key fixed 1-minute buckets:
| Bucket | Limit | Applies to |
|---|---|---|
| orders | 60 / min | place, modify, cancel, cancel-all, set leverage |
| reads | 300 / min | everything 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.
Honest HTTP status + { "error": { "code", "message" } }. Match on code.
| Code | HTTP | Meaning |
|---|---|---|
| service_disabled | 403 | The API (or the affiliate API) is not enabled. |
| missing_key | 401 | No Authorization: Bearer. |
| invalid_key | 401 | Unknown key. |
| wrong_key_kind | 403 | A trading key on an affiliate route, or another kind of key on a trading route. |
| key_suspended | 403 | Auto-suspended for abuse; retry after the window, or regenerate. |
| account_suspended | 403 | The owning user is disabled. |
| rate_limited | 429 | Bucket exhausted; see Retry-After. |
| invalid_request | 400 | Bad params or body (validation detail in message). |
| invalid_json | 400 | The body is not valid JSON. |
| bots_disabled | 403 | Bots are off platform-wide. |
| trading_halted | 503 | A platform or bot trading halt is on. Cancels still work. |
| bot_not_trading | 409 | The 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_stopped | 409 | A vault whose trading is stopped by Mutex: reduce-only orders and cancels only. |
| vault_closed | 409 | The vault is closed. |
| vault_not_open | 409 | The vault is not open yet. |
| wrong_desk | 409 | A vault read of the other desk: positions on a Spot vault, holdings on a Perps vault. |
| unknown_market | 404 / 400 | No such symbol: 404 on market data, 400 on order routes. |
| order_not_found | 404 | No order by that id / client_order_id. |
| duplicate_client_order_id | 409 | client_order_id already used on this account. |
| order_rejected | 4xx / 502 | The order was refused (reason in message); 502 when the venue refuses a cancel-all or a leverage change. |
| conflict | 409 | Modify/cancel raced venue state. |
| venue_unavailable | 502 | Upstream venue unreachable; retry. |
| internal_error | 500 | Unexpected; safe to retry idempotently. |
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.