Skip to navigation

Error Handling

The response envelope, error codes, and troubleshooting.

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:

{
"code": "0",
"msg": "",
"data": []
}
FieldMeaning
codeA string status code. "0" means the request succeeded. Any other value indicates an error.
msgA human-readable message. Empty on success; describes the problem on failure.
dataAn 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

{
"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:

{
"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.
{
"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

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:

CodeMeaningWhat to do
0Success.Proceed.
50011Rate limit reached.Back off and retry; batch and stream to reduce volume.
50102Timestamp request expired (clock skew > 30s).Sync your system clock; regenerate the timestamp.
50111Invalid OK-ACCESS-KEY.Check the API key value.
50113Invalid signature.Recompute OK-ACCESS-SIGN; verify pre-hash string and secret.
51008Insufficient balance for the order.Reduce size or fund the account.
51000Parameter error.Fix the request parameters named in msg.
51603Order 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.
  • 50102 (expired timestamp) means clock drift — run NTP.
  • 50011 / HTTP 429 means you’re hitting 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.