> 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-handling/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://okx-demo.ferndocs.com/_mcp/server. # Error Handling ## Error handling Every OKX v5 REST response — success or failure — uses the same envelope, so you can handle results uniformly. Understanding this shape is the key to robust error handling. ## The response envelope All responses are JSON objects with three top-level fields: ```json { "code": "0", "msg": "", "data": [] } ``` | Field | Meaning | | ------ | -------------------------------------------------------------------------------------------- | | `code` | A string status code. `"0"` means the request succeeded. Any other value indicates an error. | | `msg` | A human-readable message. Empty on success; describes the problem on failure. | | `data` | An array of result objects. Present on success, and often on partial failures too. | Note that `code` is a **string**, not a number — compare against `"0"`, not `0`. ### A successful response ```json { "code": "0", "msg": "", "data": [ { "instId": "BTC-USDT", "last": "65000.1", "ts": "1597026383085" } ] } ``` ### A top-level error When the whole request fails (bad authentication, malformed parameters, rate limit), `code` is non-zero and `msg` explains why: ```json { "code": "50011", "msg": "Rate limit reached. Please refer to API documentation and throttle requests accordingly.", "data": [] } ``` ## Per-item errors on batch requests Batch endpoints (such as `POST /api/v5/trade/batch-orders`) can partially succeed: some items go through while others fail. In that case the **top-level** `code` may still be non-zero to signal that not everything succeeded, and each element of `data` carries its own per-item status via `sCode` and `sMsg`: * `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." } ] } ``` Always inspect **both** levels: check the top-level `code`, and for batch operations, check each item's `sCode` to learn which orders actually went through. ## A minimal handling pattern ```python resp = request("POST", "/api/v5/trade/batch-orders", orders) if resp["code"] != "0": # Something went wrong overall — inspect per-item results if present. for item in resp.get("data", []): if item.get("sCode") not in (None, "0"): print(f"item failed: {item['sCode']} {item['sMsg']}") # Also handle the top-level failure (auth, rate limit, validation). print(f"request error: {resp['code']} {resp['msg']}") else: for item in resp["data"]: # Even on code == "0", per-item sCode is authoritative for batches. ... ``` ## Representative error codes Codes fall into ranges by category — general errors around `500xx`, trading errors around `510xx`. A few you'll encounter often: | Code | Meaning | What to do | | ------- | --------------------------------------------- | -------------------------------------------------------------- | | `0` | Success. | Proceed. | | `50011` | Rate limit reached. | Back off and retry; batch and stream to reduce volume. | | `50102` | Timestamp request expired (clock skew > 30s). | Sync your system clock; regenerate the timestamp. | | `50111` | Invalid `OK-ACCESS-KEY`. | Check the API key value. | | `50113` | Invalid signature. | Recompute `OK-ACCESS-SIGN`; verify pre-hash string and secret. | | `51008` | Insufficient balance for the order. | Reduce size or fund the account. | | `51000` | Parameter error. | Fix the request parameters named in `msg`. | | `51603` | Order does not exist. | Verify the order/`clOrdId` before amending or cancelling. | This is a small sample — each endpoint documents the specific codes it can return. Treat `msg` / `sMsg` as the authoritative human-readable explanation. ## Troubleshooting checklist * **Compare `code` as a string** (`"0"`), not an integer. * **Authentication failures (`501xx`)** almost always trace back to signing: wrong secret, mismatched timestamp, unsigned query string, or a re-serialized body. See [REST authentication](/docs/authentication/rest-authentication). * **`50102` (expired timestamp)** means clock drift — run NTP. * **`50011` / HTTP 429** means you're hitting [rate limits](/docs/reference/rate-limits) — throttle and back off. * **Partial batch failures** won't show up if you only check the top-level `code`; always read per-item `sCode`. * **Empty `data` with a non-zero `code`** is normal for a fully failed request — the reason is in `msg`. > The response envelope, error codes, and troubleshooting.