API reference
Error codes.
Every error answers with the same envelope: a stable code to match on, a message for people, the request id to quote to support, and whether a retry can work.
| Code | HTTP | What happened | What to do |
|---|---|---|---|
invalid_request | 400 | The request is malformed. | Fix what details names. |
invalid_parameter | 400 | A query or path parameter is out of range or the wrong kind. | Use a value the reference lists for it. |
unknown_field | 400 | The request carries something this route does not know, or a parameter twice. | Remove what details lists. |
invalid_cursor | 400 | The cursor was changed, or it belongs to another list or filter. | Pass next_cursor exactly as the previous page gave it, with the same filters. |
invalid_json | 400 | The body could not be read as JSON, or it is nested too deep. | Send a JSON object. |
missing_api_key | 401 | The request has no Authorization header. | Send Authorization: Bearer with your key. |
invalid_api_key | 401 | The key is mistyped, unknown, or not sent as a Bearer token. | Copy the key again from your dashboard. |
expired_api_key | 401 | The key was made with an end date that has passed. | Make a new key in your dashboard. |
revoked_api_key | 401 | The key was revoked, by you or by our team. | Make a new key in your dashboard. |
invalid_token | 401 | The token is mistyped, unknown, or not sent as a Bearer token. | Sign in to the app again. |
expired_token | 401 | Access tokens of AI assistants last an hour; any app's access ends on its end date. | Refresh the token, or sign in to the app again. |
revoked_token | 401 | The access was ended, by you, by a new password, or by our team. | Sign in to the app again. |
wrong_resource | 401 | An app's token works only on the host it was asked for (details.expected_resource names this one). | Sign in again asking for this host. |
wrong_environment | 401 | A live key was sent to the sandbox host, or a sandbox key to the live host. | Call the host named in details.expected_host. |
ip_not_allowed | 403 | The key only works from the addresses you listed. | Call from a listed address, or add this one in your dashboard. |
insufficient_permission | 403 | A read key was used for a trading call. | Use a key with trade access. |
api_disabled | 403 | API access is not open to you, or it was suspended. | Contact support. |
invalid_size_step | 400 | The size must be a whole number of size steps. | Use one of the sizes in details.nearest. |
invalid_price_tick | 400 | Prices must be whole ticks. | Use one of the prices in details.nearest. |
price_out_of_band | 422 | Limit and stop prices must stay within a band around the last price (details.band_pct). | Bring the price closer to the market. |
stop_already_triggered | 422 | A stop must be above the last price for a buy, below it for a sell. | Use a market or limit order, or move the stop. |
post_only_would_fill | 422 | A post-only limit must rest: its price is already through the market. | Move the price, or drop post_only. |
limit_inside_spread | 422 | A limit already through the last price fills at once at the best price, which is beyond your limit here (it sits inside the spread). | Move the limit past the best price, or wait for the market. |
order_too_small | 422 | Orders below a minimum value are refused (details.min_notional_usd). | Place a larger order. |
working_order_cap | 422 | Working orders per account and per customer are capped (details). | Cancel some, then try again. |
client_order_id_conflict | 409 | Your client_order_id is unique per environment; the order that has it is in order_id. | Use a new client_order_id, or read the existing order. |
order_not_working | 409 | It filled, was canceled or ended before your request (details.status). | Read the order to see its state. |
idempotency_key_reused | 422 | The same key came with a different body. | Use a new Idempotency-Key for a new action. |
operation_in_progressRetry later | 409 | A retry came while the first was not finished. | Retry after the Retry-After header. |
key_market_not_allowed | 422 | The key is limited to some markets (details.markets). | Use a key that may trade it, or change the key's markets. |
key_reduce_only | 422 | The key is reduce-only: every order it places must be reduce_only. | Set reduce_only, or use another key. |
key_max_order_notional | 422 | The key has a largest order value (details.max_order_usd). | Place a smaller order. |
key_max_position_notional | 422 | The key has a largest position value (details.max_position_usd). | Place a smaller order. |
key_loss_stop_active | 422 | Trading with the key is stopped on the account until the next trading day. | Wait for the next trading day (details.unlocks_at). |
insufficient_margin | 422 | The order needs more margin than is available (details). | Place a smaller order, lower the leverage, or close positions. |
exposure_cap_exceeded | 422 | Your open value on a market, across your accounts of the same product, is capped. | Place a smaller order. |
reduce_only_violation | 422 | A reduce-only order may only reduce or close a position. | Check the position's side and size. |
slippage_exceeded | 422 | The best price was beyond worst_price (or max_slippage_pct) when the order arrived. | Allow more slippage, or use a limit order. |
no_priceRetry later | 503 | The market has no price to fill against yet. | Retry after the Retry-After header. |
stale_quoteRetry later | 503 | The latest price is too old to trade against. | Retry after the Retry-After header. |
market_closed | 422 | The market does not trade now. | Try again when it opens. |
market_not_offered | 422 | The market is not offered for new positions. | Choose another market. |
market_halted | 422 | Trading in the market is stopped for now. | Try again later. |
position_liquidating | 422 | A position in liquidation takes no new order. | Wait until it is closed. |
account_paused | 422 | A trading rule paused the account (details.until when known). | Wait until it resumes. |
account_not_tradable | 422 | The account passed, failed, or was closed (details.reason). | Use another account. |
insufficient_balance | 422 | The account balance does not cover this. | Reduce the order. |
margin_mode_locked | 409 | A position or working order is open on the market. | Close them, then change the mode. |
invalid_leverage | 400 | Leverage is a whole number from 1 to the market's max_leverage (details). | Choose a leverage within it. |
liquidation_risk | 422 | The change would move the liquidation price beyond the market. | Choose a lower leverage or keep more margin. |
position_not_isolated | 409 | Only an isolated position has its own margin to change. | Switch the market to isolated first. |
engine_busyRetry later | 503 | The engine is under load and took nothing. | Retry after the Retry-After header. |
order_rejected | 422 | The engine refused the order for a reason without its own code. | Check the order, or contact support with the request id. |
account_not_found | 404 | The account does not exist in this environment, or the key does not reach it. | List your accounts with GET /v1/accounts. |
market_not_found | 404 | The market is not offered. | List the markets with GET /v1/markets. |
order_not_found | 404 | The order is not on this account. | List the account's orders. |
position_not_found | 404 | The account holds nothing on that market now. | List the open positions. |
route_not_found | 404 | The path does not exist on this host. | Check the path against the reference. |
method_not_allowed | 405 | The path exists, with other methods (see the Allow header). | Use a method the Allow header lists. |
payload_too_large | 413 | The body is over the size limit. | Send a smaller body. |
uri_too_long | 414 | The URL is over the length limit. | Shorten the query. |
unsupported_media_type | 415 | A body was sent without Content-Type: application/json. | Send JSON with that header. |
sandbox_limit_reached | 409 | Each customer may open a fixed number of sandbox accounts, ever. | Keep using the ones you have. |
account_not_readyRetry later | 409 | The account exists but is not open on the trading engine yet. | Retry after the Retry-After header. |
sandbox_openingRetry later | 409 | The account was reserved and is being opened. | Retry after the Retry-After header. |
idempotency_key_required | 400 | Calls that create something need a key that makes a retry safe. | Send Idempotency-Key with a new unique value per action. |
invalid_idempotency_key | 400 | The key is empty, too long, or has characters it cannot. | Send 1 to 128 printable characters. |
rate_limitedRetry later | 429 | A request budget is used up (details.scope says which). | Wait for Retry-After, then spread your requests. |
not_readyRetry later | 503 | The API is starting and has not loaded your keys yet. | Retry after the Retry-After header. |
state_unavailableRetry later | 503 | The trading engine did not give a complete answer in time; nothing partial is returned. | Retry after the Retry-After header. |
engine_unavailableRetry later | 503 | The trading engine did not answer. | Retry after the Retry-After header. |
limiter_unavailableRetry later | 503 | The rate limiter is unavailable, so nothing passes unmeasured. | Retry after the Retry-After header. |
sandbox_disabled | 503 | Sandbox accounts are not being opened. | Try again later. |
environment_disabled | 503 | This host takes no requests at the moment. | Check GET /v1/status. |
temporarily_unavailableRetry later | 503 | A service this call needs did not answer. | Retry after the Retry-After header. |
capacityRetry later | 503 | The API is busy. | Retry after the Retry-After header. |
internal_errorRetry later | 500 | An error we did not expect. | Retry; if it lasts, contact support with the request id. |