If you're building a Bybit trading bot in Python, you will hit errors. The Bybit V5 API is well-documented, but the error messages are often terse retCode integers that require digging through docs to decode.
This guide covers the 7 most common Bybit API Python errors — the exact error output, the root cause, and a working fix you can paste in.
All code uses the pybit library (pip install pybit). If you're evaluating different bot architectures, see how AutoTrading compares to freqtrade and Jesse.
Error 1: retCode: 10003 — Invalid API Key
Error output:
{"retCode": 10003, "retMsg": "API key is invalid.", "result": {}}
Root cause: You're using a testnet API key against the mainnet endpoint (or vice versa).
Fix: Bybit testnet and mainnet require separate API keys. The pybit HTTP session takes a testnet boolean — make sure it matches the key you generated.
from pybit.unified_trading import HTTP
# Testnet session — must use testnet keys
session = HTTP(
testnet=True,
api_key="YOUR_TESTNET_KEY",
api_secret="YOUR_TESTNET_SECRET",
)
# Mainnet session — must use mainnet keys
session = HTTP(
testnet=False,
api_key="YOUR_MAINNET_KEY",
api_secret="YOUR_MAINNET_SECRET",
)
Always start with testnet=True and paper-trade before switching to mainnet. If you don't have an account yet, create a free Bybit account here.
Error 2: retCode: 10006 — Rate Limit Exceeded
Error output:
{"retCode": 10006, "retMsg": "Too many visits!", "result": {}}
Root cause: Your bot is hitting the endpoint faster than Bybit's rate limit allows (varies by endpoint, typically 10–120 req/s).
Fix: Implement exponential backoff with jitter. Check the X-Bapi-Limit-Status response header to monitor your remaining quota.
import time
import random
def call_with_backoff(fn, max_retries=5, *args, **kwargs):
for attempt in range(max_retries):
result = fn(*args, **kwargs)
if result.get("retCode") == 10006:
wait = (2 ** attempt) + random.uniform(0, 1)
print(f"Rate limited. Waiting {wait:.1f}s...")
time.sleep(wait)
continue
return result
raise RuntimeError("Max retries exceeded")
Error 3: retCode: 10001 — Parameter Error (qty step size)
Error output:
{"retCode": 10001, "retMsg": "params error: qty is not correct", "result": {}}
Root cause: Your order quantity doesn't match the instrument's qtyStep or minOrderQty. Every Bybit instrument has minimum and step constraints.
Fix: Fetch instrument info first and round your quantity to the allowed step size.
from decimal import Decimal, ROUND_DOWN
def round_qty(qty: float, qty_step: str) -> str:
"""Round quantity down to the instrument's qtyStep."""
step = Decimal(qty_step)
rounded = Decimal(str(qty)).quantize(step, rounding=ROUND_DOWN)
return str(rounded)
# Fetch constraints
info = session.get_instruments_info(category="linear", symbol="BTCUSDT")
lot_filter = info["result"]["list"][0]["lotSizeFilter"]
qty_step = lot_filter["qtyStep"] # e.g. "0.001"
min_qty = lot_filter["minOrderQty"] # e.g. "0.001"
# Safe order quantity
raw_qty = 0.0037
safe_qty = round_qty(raw_qty, qty_step) # → "0.003"
Error 4: Signature Mismatch
Error output:
{"retCode": 10004, "retMsg": "error sign! verify the signature!", "result": {}}
Root cause: Your system clock is drifted — Bybit requires the request timestamp to be within 5,000ms of server time.
Fix: Sync your clock or use Bybit's server time to generate timestamps. pybit handles signing automatically, but if you're making raw requests, use this pattern:
import time
import requests
def get_bybit_server_time() -> int:
r = requests.get("https://api.bybit.com/v5/market/time")
return int(r.json()["result"]["timeNano"]) // 1_000_000 # → ms
def get_safe_timestamp() -> int:
"""Use server time if local clock may be drifted."""
return get_bybit_server_time()
On Linux/Oracle Cloud servers, keep NTP synced: sudo timedatectl set-ntp true.
Error 5: SSL: CERTIFICATE_VERIFY_FAILED
Error output:
requests.exceptions.SSLError: HTTPSConnectionPool(...):
SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
Root cause: Your certifi CA bundle is outdated or missing.
Fix: Update certifi and rebuild the SSL context.
pip install --upgrade certifi pybit requests
If you're on macOS and seeing this from the system Python, run the certificate installer:
/Applications/Python\ 3.x/Install\ Certificates.command
For Docker deployments, add to your Dockerfile:
RUN pip install --upgrade certifi
Error 6: WebSocket Silent Disconnect
Symptom: No error is raised, but your WebSocket stops receiving data after a few minutes.
Root cause: Bybit's WebSocket server requires a heartbeat ping every 20 seconds. Without it, the server silently drops the connection.
Fix: Send a ping message on a timer. pybit's WebSocket class handles this automatically if you use it correctly — the silent disconnect usually happens when wrapping the connection manually.
import asyncio
import json
import websockets
async def bybit_ws_with_heartbeat(url: str, subscription: dict):
async with websockets.connect(url) as ws:
await ws.send(json.dumps(subscription))
async def heartbeat():
while True:
await asyncio.sleep(20)
await ws.send(json.dumps({"op": "ping"}))
asyncio.create_task(heartbeat())
async for message in ws:
data = json.loads(message)
if data.get("op") == "pong":
continue # heartbeat ack, ignore
yield data
For production use, also implement reconnection logic — wrap the outer connection in a while True with an exponential backoff on websockets.ConnectionClosed.
Error 7: USDT vs USDC Margin Confusion
Error output:
{"retCode": 10001, "retMsg": "params error: the account type does not support this coin margin"}
Root cause: Bybit supports both USDT-margined and USDC-margined contracts on linear perpetuals. Mixing settleCoin causes parameter errors.
Fix: Explicitly set settleCoin to match your account's margin type. Check your Unified Trading Account's default settlement currency in Bybit's UI first.
# USDT-margined — most common for spot and linear futures
order = session.place_order(
category="linear",
symbol="BTCUSDT",
side="Buy",
orderType="Market",
qty="0.001",
# settleCoin defaults to USDT for BTCUSDT — usually fine to omit
)
# USDC-margined — explicit
order = session.place_order(
category="linear",
symbol="BTCPERP",
side="Buy",
orderType="Market",
qty="0.001",
settleCoin="USDC",
)
Rule of thumb: if the symbol ends in USDT, you're USDT-margined. If it ends in PERP, check your account default.
FAQ
Which pybit version should I use?
pybit >= 5.7.0 supports the Bybit V5 API. Earlier versions use the V3 endpoint, which Bybit has deprecated. Run pip install --upgrade pybit to get the latest.
How do I debug retCodes I don't recognize?
Check the Bybit V5 API error code reference. For retCode ranges: 10000–10099 are auth/general, 110000+ are order errors.
Is there a way to test without placing real orders?
Yes — Bybit's testnet (testnet.bybit.com) is a full sandbox with paper money. Generate separate API keys there and use testnet=True in your pybit session. You can also use orderType="Limit" with prices far from market to create "safe" test orders on mainnet that won't fill.
How do I handle multiple errors in a production bot?
Wrap each API call in a handler that checks retCode and routes to the appropriate recovery action. For rate limits (10006): backoff. For auth errors (10003, 10004): alert and halt. For parameter errors (10001): log and skip the trade. Never silently swallow errors — at minimum log retCode + retMsg + symbol + qty for every failed call.
Conclusion
These 7 errors cover the majority of what you'll hit building a Bybit trading bot with Python. The pattern is consistent: check retCode, look up the cause, apply the fix. Most are configuration issues (wrong key environment, wrong step size, expired cert) rather than logic bugs.
If you want to see how these fixes integrate into a production bot — with 3 trading domains, a FastAPI backend, and a Claude Haiku AI strategy gate — the full source is open:
→ hoon6653/autotrading on GitHub — Python 3.11, 927 tests, Oracle Cloud deployment
And if the AI gate concept is new to you, read: Claude Haiku as a Runtime Trading Gate, Not a Bot Builder — why REFUSE is the most valuable output a trading AI can produce.
Getting started with Bybit? Sign up for a free account to get your API keys and start building. If you're exploring alternatives, OKX is another solid exchange with a Python-friendly API.
Top comments (0)