> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://okx-demo.ferndocs.com/docs/reference/error-codes/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://okx-demo.ferndocs.com/_mcp/server. # Error Codes ## Overview Every OKX v5 REST response — success or failure — uses the same JSON envelope: ```json { "code": "0", "msg": "", "data": [] } ``` * `code` — a **string** status code. `"0"` means the request succeeded; any other value is an error. * `msg` — a human-readable explanation. Empty on success, populated on failure. * `data` — an array of result objects. Compare `code` as a string (`"0"`), not the integer `0`. The `msg` / `sMsg` text is the authoritative, up-to-date explanation for any code — the tables below summarize the documented meaning, but always surface the returned message to callers. > **Success** > > Code `"0"` is the only success value. On batch endpoints the top-level `code` can be `"0"` while individual items still fail, so treat a top-level `"0"` as "the request was accepted", not "every item succeeded". Always inspect per-item `sCode` as well. ### Per-item codes on batch endpoints Batch endpoints — `POST /api/v5/trade/batch-orders`, `POST /api/v5/trade/cancel-batch-orders`, `POST /api/v5/trade/amend-batch-orders`, and others — can partially succeed. Each element of `data` carries its own status: * `sCode` — the status code for that individual item (`"0"` = that item succeeded). * `sMsg` — the message for that individual item. ```json { "code": "1", "msg": "Operation failed.", "data": [ { "clOrdId": "order-a", "ordId": "312269865356374016", "sCode": "0", "sMsg": "" }, { "clOrdId": "order-b", "ordId": "", "sCode": "51008", "sMsg": "Order failed. Insufficient balance." } ] } ``` The top-level `code` is `"1"` ("Operation failed") when any item fails, and `"2"` ("Bulk operation partially succeeded") when some succeed and some fail. Read both levels. | Code | HTTP | Message / Meaning | | ---- | ---- | ----------------------------------------------------------------------------------------------- | | `0` | 200 | Success. The request (and, for batches, every item) succeeded. | | `1` | 200 | Operation failed. Used at the top level of a batch when all items failed; inspect each `sCode`. | | `2` | 200 | Bulk operation partially succeeded. Some items succeeded, some failed; inspect each `sCode`. | --- ## Public / General (50000–50999) General request, routing, and system-level errors that apply across all endpoints. | Code | HTTP | Message / Meaning | | ------- | ---- | ------------------------------------------------------------------------------------------ | | `50000` | 400 | Body for POST request cannot be empty. | | `50001` | 503 | Service temporarily unavailable. Try again later. | | `50002` | 400 | JSON syntax error in the request body. | | `50004` | 400 | API endpoint request timeout (does not mean the request succeeded or failed; check state). | | `50005` | 410 | API endpoint is offline or unavailable. | | `50006` | 400 | Invalid `Content-Type`. Use `application/json`. | | `50007` | 403 | Account blocked. | | `50008` | 401 | User does not exist. | | `50009` | 403 | Account is suspended (frozen). | | `50010` | 400 | User ID is empty. | | `50011` | 429 | Rate limit reached. Throttle and retry. | | `50012` | 400 | Account status invalid. | | `50013` | 429 | Systems are busy. Please try again later. | | `50014` | 400 | Required parameter cannot be empty. | | `50015` | 400 | Either parameter `x` or `y` is required. | | `50016` | 400 | Parameter mismatch (paired parameters are inconsistent). | | `50017` | 400 | Position frozen due to ADL (auto-deleveraging). Operable after ADL finishes. | | `50018` | 400 | Currency is frozen due to ADL. | | `50019` | 400 | Account is frozen due to ADL. | | `50020` | 400 | Position frozen due to liquidation. Operable once liquidation completes. | | `50021` | 400 | Currency is frozen due to liquidation. | | `50022` | 400 | Account is frozen due to liquidation. | | `50023` | 400 | Funding fee frozen. Operable once settlement completes. | | `50024` | 400 | Parameters `x` and `y` cannot be provided at the same time. | | `50025` | 400 | Parameter `x` count exceeds the limit. | | `50026` | 500 | System error. Try again later. | | `50027` | 403 | This account is restricted from trading. Contact customer support. | | `50028` | 400 | Unable to take the order; account is restricted from opening positions. | | `50029` | 400 | This instrument is unavailable for this account (risk control). | | `50030` | 403 | You do not have permission to use this API endpoint. | | `50032` | 403 | Account has been set to prohibit transactions for this currency. | | `50033` | 403 | Account has been set to prohibit transactions for this business line. | | `50035` | 400 | This endpoint requires that the API key be bound to an IP address. | | `50036` | 400 | `expTime` cannot be earlier than the current system time. | | `50037` | 400 | Order has expired. | | `50038` | 400 | This feature is unavailable in demo trading. | | `50039` | 400 | The parameter `before` cannot be used with `begin`/`end`. | | `50040` | 400 | Too frequent operations. Please try again later. | | `50041` | 400 | User ID is not eligible for this feature (whitelist only). | | `50044` | 400 | Must select one broker type. | | `50060` | 400 | For the security of your funds, a preset action is required. Please try again later. | | `50061` | 429 | Order requests have been rate-limited. Reduce request frequency. | | `50062` | 400 | This feature is currently unavailable. | --- ## Rate limiting (50011, 50013, 50061) Rate-limit responses arrive with HTTP `429`. Back off using exponential delay, batch where possible, and prefer WebSocket streams over REST polling. | Code | HTTP | Message / Meaning | | ------- | ---- | -------------------------------------------------------------------------------------------------- | | `50011` | 429 | Rate limit reached. Please refer to the API documentation and throttle requests accordingly. | | `50013` | 429 | Systems are busy. Please try again later. Server-side throttling under load. | | `50061` | 429 | Order placement/amend/cancel requests exceeded the sub-account order rate limit. Reduce frequency. | See [Rate limits](/docs/reference/rate-limits) for per-endpoint budgets and the `sub-account` order-count limits. --- ## API authentication & keys (50100–50119) Authentication failures almost always trace back to the signature or the request headers. See [REST authentication](/docs/authentication/rest-authentication) for how `OK-ACCESS-SIGN` is computed. | Code | HTTP | Message / Meaning | | ------- | ---- | ---------------------------------------------------------------------------------- | | `50100` | 400 | API frozen. Contact customer support. | | `50101` | 401 | APIKey does not match the current environment (e.g. live key used against demo). | | `50102` | 401 | Timestamp request expired. Clock skew exceeded 30 seconds; sync your clock. | | `50103` | 401 | Request header `OK-ACCESS-KEY` cannot be empty. | | `50104` | 401 | Request header `OK-ACCESS-PASSPHRASE` cannot be empty. | | `50105` | 401 | Request header `OK-ACCESS-PASSPHRASE` is incorrect. | | `50106` | 401 | Request header `OK-ACCESS-SIGN` cannot be empty. | | `50107` | 401 | Request header `OK-ACCESS-TIMESTAMP` cannot be empty. | | `50108` | 401 | Broker ID does not exist. | | `50109` | 401 | Matching broker ID does not exist. | | `50110` | 401 | Your IP address is not in the allowlist bound to this API key. | | `50111` | 401 | Invalid `OK-ACCESS-KEY`. | | `50112` | 401 | Invalid `OK-ACCESS-TIMESTAMP` (format not ISO-8601 / not parseable). | | `50113` | 401 | Invalid signature. Recompute `OK-ACCESS-SIGN` from the pre-hash string and secret. | | `50114` | 401 | Invalid authorization / authentication failed. | | `50115` | 405 | Invalid request method for this endpoint. | | `50116` | 401 | Fastapi cannot be used for this endpoint. | | `50118` | 400 | You must complete broker withdrawal-verification setup before using this feature. | | `50119` | 401 | API key does not exist. | > **Warning** > > `50113` (invalid signature) and `50102` (expired timestamp) are the two most common auth failures. `50113` means the pre-hash string, HTTP method, path, query string, or body you signed does not match what was sent — re-serialize the body exactly once and sign the raw string. `50102` means clock drift; run NTP so your timestamp is within 30 seconds of OKX server time. --- ## Account (50000-range trade / 51000–51999 subset) Errors returned when placing, amending, or cancelling orders — covering parameter validation, balance, leverage, and position-mode conflicts. These appear at the top level for single orders and as `sCode` on batch items. | Code | HTTP | Message / Meaning | | ------- | ---- | ---------------------------------------------------------------------------------- | | `51000` | 400 | Parameter error. The parameter named in `msg` is missing or malformed. | | `51001` | 400 | Instrument ID (`instId`) does not exist. | | `51002` | 400 | Instrument ID does not match the underlying. | | `51003` | 400 | Either `clOrdId` or `ordId` is required. | | `51004` | 400 | Order amount exceeds the current tier's maximum position limit. | | `51005` | 400 | Order amount exceeds the maximum order size. | | `51006` | 400 | Order price is outside the allowable price-limit range. | | `51007` | 400 | Order placement failed: order value is below the minimum (`1 USDT`). | | `51008` | 400 | Order failed. Insufficient account balance. | | `51009` | 400 | Order placement blocked: account is in trading-suspended state. | | `51010` | 400 | Account level too low. Current account mode does not support this instrument type. | | `51011` | 400 | Duplicate order ID (`clOrdId` already used). | | `51012` | 400 | Token does not exist. | | `51014` | 400 | Index does not exist. | | `51015` | 400 | Instrument ID does not match the instrument type. | | `51016` | 400 | Duplicate `clOrdId`. | | `51020` | 400 | Order amount must be greater than the minimum lot size. | | `51023` | 400 | Position does not exist. | | `51024` | 400 | Trading account is suspended. | | `51025` | 400 | Order count exceeds the limit. | | `51046` | 400 | Take-profit trigger price must be higher than the last price. | | `51047` | 400 | Stop-loss trigger price must be lower than the last price. | | `51119` | 400 | Order placement failed due to insufficient balance. | | `51120` | 400 | Order quantity is less than the minimum available amount for closing. | | `51121` | 400 | Order count must be a multiple of the lot size. | | `51122` | 400 | Order price must be higher than or equal to the minimum price increment. | --- ## Trade / order (51100–51999 subset) Order-state and order-management errors — amend, cancel, close, and margin/leverage conflicts. | Code | HTTP | Message / Meaning | | ------- | ---- | -------------------------------------------------------------------------------- | | `51100` | 400 | Trading amount does not meet the minimum, or exceeds the maximum. | | `51103` | 400 | Number of pending orders for the instrument exceeds the limit. | | `51108` | 400 | Positions exceed the limit for the market-maker/close-out operation. | | `51109` | 400 | No available offer to match the order (no counterparty). | | `51115` | 400 | Cancellation failed: the order has already been filled or cancelled. | | `51116` | 400 | Order price or trigger price exceeds the allowable limit. | | `51117` | 400 | Pending close-orders count exceeds the limit. | | `51121` | 400 | Order count must be a multiple of the lot size. | | `51124` | 400 | You can only place limit orders during the pre-market call-auction period. | | `51127` | 400 | Available balance is zero. | | `51131` | 400 | Insufficient balance. | | `51132` | 400 | Your position amount is negative and cannot be closed with this order. | | `51133` | 400 | Reduce-only cannot increase the position size. | | `51136` | 400 | Close-position amount exceeds available position amount. | | `51201` | 400 | Value of per market order cannot exceed 1,000,000 USDT. | | `51202` | 400 | Market order amount exceeds the maximum amount. | | `51203` | 400 | Order amount exceeds the limit for the instrument. | | `51277` | 400 | TP trigger price cannot be higher than the last price. | | `51278` | 400 | SL trigger price cannot be lower than the last price. | | `51279` | 400 | TP trigger price cannot be lower than the last price. | | `51280` | 400 | SL trigger price cannot be higher than the last price. | | `51400` | 400 | Cancellation failed: the order does not exist. | | `51401` | 400 | Cancellation failed: the order is already cancelled. | | `51402` | 400 | Cancellation failed: the order is already completed (filled). | | `51403` | 400 | Cancellation failed: this order type does not support cancellation. | | `51404` | 400 | Order amendment unavailable: the order is in the pre-market call-auction period. | | `51405` | 400 | Cancellation failed: you have no pending orders. | | `51406` | 400 | Cancellation failed: the number of orders exceeds the batch limit (max 20). | | `51408` | 400 | Pair and order do not match; cannot cancel across different instruments. | | `51410` | 400 | Cancellation already in progress; do not resubmit. | | `51503` | 400 | Order amendment failed: the order does not exist or is already completed. | | `51506` | 400 | Order amendment unavailable for this order type. | | `51509` | 400 | Amendment failed: the requested change was not accepted. | | `51603` | 400 | Order does not exist. Verify `ordId` / `clOrdId` before amend or cancel. | --- ## Funding & withdrawal (58000–58999 subset) Errors from the asset/funding endpoints: transfers, deposits, and withdrawals. | Code | HTTP | Message / Meaning | | ------- | ---- | -------------------------------------------------------------------------- | | `58002` | 400 | Please activate Options trading before transferring related assets. | | `58004` | 400 | Account is blocked from transfers (e.g. deferred settlement). | | `58005` | 400 | Transfer amount exceeds the maximum limit for the currency. | | `58006` | 400 | Service unavailable for this token in your region. | | `58007` | 400 | Abnormal state of your account; transfer is not allowed. | | `58100` | 400 | The trading account withdrawal function is disabled. | | `58101` | 400 | Transfer suspended for the account. | | `58102` | 429 | Too frequent withdrawal requests. Try again later. | | `58103` | 400 | Sub-account does not have transfer permissions. | | `58104` | 400 | Withdrawal request rejected due to a risk-control review. | | `58105` | 400 | The internal transfer amount exceeds the daily limit. | | `58110` | 400 | Withdrawals are suspended for this currency due to instability. | | `58111` | 400 | Withdrawal is unsupported for the selected chain of this currency. | | `58112` | 400 | Withdrawal failed. Please check your account state and try again. | | `58115` | 400 | Sub-account transfers are not allowed for this currency. | | `58116` | 400 | Transfer amount exceeds the maximum. | | `58120` | 400 | Withdrawal services are unavailable. Please try again later. | | `58121` | 400 | The withdrawal amount is below the minimum withdrawal threshold. | | `58124` | 400 | Withdrawal request has expired. | | `58125` | 400 | Non-tradable assets can only be withdrawn in full. | | `58126` | 400 | Non-tradable assets can only be withdrawn to an OKX account. | | `58127` | 400 | The withdrawal address is not whitelisted. | | `58128` | 400 | Withdrawal address is invalid or not on the selected chain. | | `58129` | 400 | Insufficient balance to cover the withdrawal fee. | | `58131` | 400 | Insufficient available balance for the withdrawal. | | `58200` | 400 | Withdrawal from this account is not allowed. | | `58207` | 400 | Withdrawal address is not on the address allowlist. | | `58208` | 400 | Withdrawal failed: please add a withdrawal address in the security center. | | `58350` | 400 | Insufficient balance. | --- ## WebSocket (60000–60999) Returned on the WebSocket channel as `event: "error"` frames. Login, subscription, and channel errors dominate this range. See [WebSocket](/docs/realtime/web-socket) for connection and subscription details. | Code | HTTP | Message / Meaning | | ------- | ---- | ----------------------------------------------------------------------------------- | | `60001` | — | `OK-ACCESS-KEY` cannot be empty in the login request. | | `60002` | — | `OK-ACCESS-SIGN` cannot be empty. | | `60003` | — | `OK-ACCESS-PASSPHRASE` cannot be empty. | | `60004` | — | Invalid `OK-ACCESS-TIMESTAMP`. | | `60005` | — | Invalid `OK-ACCESS-KEY`. | | `60006` | — | Timestamp request expired (clock skew). Resync and re-login. | | `60007` | — | Invalid sign (signature mismatch on login). | | `60008` | — | This channel does not support subscription over this connection type. | | `60009` | — | Login failed. | | `60010` | — | Already logged in. | | `60011` | — | Please log in before subscribing to private channels. | | `60012` | — | Illegal request. The message format or JSON is invalid. | | `60013` | — | Invalid `args`. The subscription arguments are malformed. | | `60014` | — | Requests too frequent. Slow down subscription/login attempts. | | `60015` | — | Connections too frequent. Reduce reconnection rate. | | `60016` | — | Buffer is full. The connection is being closed; reconnect. | | `60017` | — | Invalid url path. | | `60018` | — | Wrong channel: the requested channel does not exist, or `arg` fields are incorrect. | | `60019` | — | Invalid op. The `op` field must be one of `subscribe`, `unsubscribe`, or `login`. | | `60020` | — | APIKey subscription limit has been reached. | | `60021` | — | The connection has reached its subscription limit. | | `60022` | — | Bulk login partially succeeded. | | `60023` | — | Bulk login requests too frequent. | | `60024` | — | Wrong passphrase in the login request. | | `60026` | — | Batch login by APIKey and token cannot be used at the same time. | | `60027` | — | Parameter is empty in the request. | | `60028` | — | This operation is not supported by this URL. | | `60029` | — | The `op` (operation) is not supported. | | `60030` | — | The channel does not support this instrument type. | | `63999` | — | Login failed due to an internal system error; reconnect and retry. | > **Info** > > The WebSocket also returns transport-level close events. `4004` (no data received in 30s — send a `ping`), `4005` (buffer full), and `4008` (too many requests) are connection-level codes, distinct from the application-level `600xx` error frames above. Send a `ping` at least every 30 seconds to keep the socket alive. --- ## Handling pattern Handle top-level and per-item codes uniformly: ```python resp = request("POST", "/api/v5/trade/batch-orders", orders) if resp["code"] != "0": # Overall failure (auth, rate limit, validation) or a partial batch failure. for item in resp.get("data", []): scode = item.get("sCode") if scode not in (None, "0"): print(f"item failed: {scode} {item['sMsg']}") print(f"request-level: {resp['code']} {resp['msg']}") else: for item in resp["data"]: # Even when top-level code == "0", per-item sCode is authoritative for batches. if item.get("sCode", "0") != "0": print(f"item failed: {item['sCode']} {item['sMsg']}") ``` For a conceptual walkthrough of the envelope and a troubleshooting checklist, see [Error handling](/docs/reference/error-handling). > A categorized reference of OKX v5 REST and WebSocket error codes.