> This page is for Documentation.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://okx-demo.ferndocs.com/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`.