◈   ⌘ api · Intermediate

Bybit API Error Codes: Complete Reference for Traders

Master Bybit API error codes with this practical reference guide. Learn what each code means, how to handle errors in code, and build more resilient trading bots.

Uncle Solieditor · voc · 06.05.2026 ·views 12
◈   Contents
  1. → How Bybit Structures Its API Error Codes
  2. → The Most Common Bybit API Error Codes Explained
  3. → Authentication Errors: Diagnosing API Key Problems
  4. → Order Errors: Why Your Trades Aren't Executing
  5. → Rate Limits and Retries: Not Getting Banned
  6. → Building a Robust Error Logging System
  7. → Frequently Asked Questions
  8. → Conclusion

If you've ever built a trading bot on Bybit, you've hit error codes. Maybe it was a 10001 on a rate limit, or a 33004 when your API key permissions were off. These aren't random numbers — each one tells you exactly what went wrong, if you know how to read them. This guide breaks down the full Bybit API error code system, shows you how to handle them in real code, and gets your bot recovering from failures instead of crashing at 3am.

How Bybit Structures Its API Error Codes

Bybit's V5 API (the current unified API as of 2024) returns errors in a consistent JSON envelope. Every response — success or failure — includes a retCode field. A retCode of 0 means success. Anything else is an error. The retMsg field gives you the human-readable description, and the result field may be empty or partial on failures.

import requests
import time
import hmac
import hashlib

API_KEY = 'your_api_key'
API_SECRET = 'your_api_secret'
BASE_URL = 'https://api.bybit.com'

def generate_signature(params: str, timestamp: str) -> str:
    param_str = timestamp + API_KEY + '5000' + params
    return hmac.new(
        API_SECRET.encode('utf-8'),
        param_str.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

def get_wallet_balance():
    endpoint = '/v5/account/wallet-balance'
    timestamp = str(int(time.time() * 1000))
    params = 'accountType=UNIFIED'
    
    sign = generate_signature(params, timestamp)
    
    headers = {
        'X-BAPI-API-KEY': API_KEY,
        'X-BAPI-TIMESTAMP': timestamp,
        'X-BAPI-RECV-WINDOW': '5000',
        'X-BAPI-SIGN': sign
    }
    
    response = requests.get(
        f'{BASE_URL}{endpoint}?{params}',
        headers=headers
    )
    data = response.json()
    
    if data['retCode'] != 0:
        print(f"Error {data['retCode']}: {data['retMsg']}")
        return None
    
    return data['result']

balance = get_wallet_balance()
print(balance)

Notice the pattern: check retCode first, then handle the result. This is the foundation of every reliable Bybit integration. Now let's look at the specific codes you'll actually encounter.

The Most Common Bybit API Error Codes Explained

Bybit groups error codes into logical ranges. Understanding the groupings helps you triage problems fast — authentication errors sit in one range, order-related errors in another, and system-level issues in a third.

Bybit API Error Code Reference — Most Common Codes
Error CodeCategoryMeaningCommon Cause
0SuccessRequest successfulN/A
10001CommonParameter errorMissing or wrong param type
10002CommonRequest expiredTimestamp drift > recv_window
10003AuthInvalid API keyWrong key or key deleted
10004AuthInvalid signatureWrong secret or signing method
10006Rate limitToo many requestsExceeding API rate limits
10007AuthIP not whitelistedCalling from non-whitelisted IP
10010AuthExpired API keyKey past expiration date
33004AuthKey lacks permissionAPI key missing required scope
110001OrderOrder not foundWrong order ID or already filled
110007OrderInsufficient balanceNot enough funds in account
110012OrderPrice too lowOrder price below minimum
110013OrderQty too smallOrder size below minimum
110043OrderSet leverage firstLeverage not set for symbol
131001WithdrawWithdrawal amount too lowBelow chain minimum
3100196PositionCross/isolated conflictMargin mode mismatch
Error 10002 (request expired) is one of the most misunderstood. It happens when your system clock drifts more than the recv_window value (default 5000ms) from Bybit's servers. Fix it by syncing your system time with NTP, or by fetching Bybit's server time at startup and calculating an offset.

Authentication Errors: Diagnosing API Key Problems

Authentication errors (10003, 10004, 10007, 33004) account for probably 60% of issues developers hit when first integrating the Bybit API. They look similar but have very different root causes. Compared to Binance, which surfaces authentication errors with slightly different structure, Bybit's V5 API is quite explicit — the retMsg usually tells you exactly what's wrong.

# Signature debugging helper — compare your string to what Bybit expects
import time
import hmac
import hashlib

def debug_signature(api_key: str, api_secret: str, params: str):
    timestamp = str(int(time.time() * 1000))
    recv_window = '5000'
    
    # This is the EXACT string Bybit V5 expects you to sign
    raw_string = timestamp + api_key + recv_window + params
    print(f'String to sign: {raw_string}')
    
    signature = hmac.new(
        api_secret.encode('utf-8'),
        raw_string.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    print(f'Generated signature: {signature}')
    print(f'Timestamp used: {timestamp}')
    
    return signature

# For POST requests, params is the JSON body string
# For GET requests, params is the query string
debug_signature('YOUR_KEY', 'YOUR_SECRET', 'accountType=UNIFIED')

Order Errors: Why Your Trades Aren't Executing

Order-related errors in the 110xxx range are where most bot developers spend their debugging time. Unlike simple authentication failures, order errors often require understanding Bybit's trading rules for each specific instrument. Platforms like Bybit and OKX both enforce minimum order sizes and price precision rules at the API level — your bot needs to handle these gracefully rather than crashing.

import requests
import time
import hmac
import hashlib
import json

BASE_URL = 'https://api.bybit.com'

def place_order(api_key: str, api_secret: str, symbol: str, 
                side: str, qty: str, order_type: str = 'Market'):
    endpoint = '/v5/order/create'
    timestamp = str(int(time.time() * 1000))
    recv_window = '5000'
    
    body = {
        'category': 'spot',
        'symbol': symbol,
        'side': side,
        'orderType': order_type,
        'qty': qty
    }
    body_str = json.dumps(body)
    
    raw = timestamp + api_key + recv_window + body_str
    sign = hmac.new(api_secret.encode(), raw.encode(), hashlib.sha256).hexdigest()
    
    headers = {
        'X-BAPI-API-KEY': api_key,
        'X-BAPI-TIMESTAMP': timestamp,
        'X-BAPI-RECV-WINDOW': recv_window,
        'X-BAPI-SIGN': sign,
        'Content-Type': 'application/json'
    }
    
    response = requests.post(f'{BASE_URL}{endpoint}', 
                             headers=headers, data=body_str)
    data = response.json()
    
    # Handle specific order errors
    error_handlers = {
        110007: lambda: print('Insufficient balance — check your wallet'),
        110012: lambda: print('Price too low — check symbol min price'),
        110013: lambda: print(f'Qty too small for {symbol} — check minOrderQty'),
        110043: lambda: print('Set leverage first for this symbol'),
        10006:  lambda: (print('Rate limited — sleeping 1s'), time.sleep(1))
    }
    
    ret_code = data['retCode']
    if ret_code != 0:
        handler = error_handlers.get(ret_code)
        if handler:
            handler()
        else:
            print(f'Unhandled error {ret_code}: {data["retMsg"]}')
        return None
    
    return data['result']

# Example usage
result = place_order('YOUR_KEY', 'YOUR_SECRET', 'BTCUSDT', 'Buy', '0.001')
if result:
    print(f'Order placed: {result["orderId"]}')

The error_handlers dict pattern is particularly useful because it lets you add handling for new error codes without refactoring your flow logic. When you encounter a new code in production logs, just add an entry.

Rate Limits and Retries: Not Getting Banned

Error 10006 is the rate limit code, and hitting it repeatedly can get your IP or API key temporarily blocked. Bybit's rate limits vary by endpoint — the order endpoints are more restrictive than market data endpoints. Unlike Binance which uses a weight-based system, Bybit uses a requests-per-second model. Check the X-Bapi-Limit-Status and X-Bapi-Limit-Reset-Timestamp headers in every response to stay ahead of limits before you hit them.

const axios = require('axios');
const crypto = require('crypto');

const API_KEY = 'your_api_key';
const API_SECRET = 'your_api_secret';
const BASE_URL = 'https://api.bybit.com';

// Exponential backoff retry wrapper for Bybit API calls
async function bybitRequest(endpoint, params = {}, method = 'GET', retries = 3) {
  const timestamp = Date.now().toString();
  const recvWindow = '5000';
  const queryString = new URLSearchParams(params).toString();
  
  const rawString = timestamp + API_KEY + recvWindow + queryString;
  const signature = crypto
    .createHmac('sha256', API_SECRET)
    .update(rawString)
    .digest('hex');
  
  const headers = {
    'X-BAPI-API-KEY': API_KEY,
    'X-BAPI-TIMESTAMP': timestamp,
    'X-BAPI-RECV-WINDOW': recvWindow,
    'X-BAPI-SIGN': signature
  };
  
  for (let attempt = 0; attempt < retries; attempt++) {
    try {
      const response = await axios({
        method,
        url: `${BASE_URL}${endpoint}`,
        params: method === 'GET' ? params : undefined,
        data: method === 'POST' ? params : undefined,
        headers
      });
      
      const { retCode, retMsg, result } = response.data;
      
      // Log rate limit status from headers
      const limitRemaining = response.headers['x-bapi-limit-status'];
      const limitReset = response.headers['x-bapi-limit-reset-timestamp'];
      if (limitRemaining && parseInt(limitRemaining) < 10) {
        console.warn(`Rate limit warning: ${limitRemaining} requests remaining`);
      }
      
      if (retCode === 10006) {
        // Rate limited — exponential backoff
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`Rate limited. Retrying in ${delay}ms (attempt ${attempt + 1})`);
        await new Promise(r => setTimeout(r, delay));
        continue;
      }
      
      if (retCode === 10002) {
        // Timestamp error — sync time offset
        console.error('Timestamp error — check system clock sync');
        throw new Error(`Timestamp error: ${retMsg}`);
      }
      
      if (retCode !== 0) {
        throw new Error(`Bybit API error ${retCode}: ${retMsg}`);
      }
      
      return result;
      
    } catch (err) {
      if (attempt === retries - 1) throw err;
      await new Promise(r => setTimeout(r, 1000 * (attempt + 1)));
    }
  }
}

// Usage
bybitRequest('/v5/market/tickers', { category: 'spot', symbol: 'BTCUSDT' })
  .then(data => console.log('BTC ticker:', data.list[0].lastPrice))
  .catch(err => console.error('Failed:', err.message));
If you're running bots on multiple exchanges simultaneously — say Bybit for futures and Binance for spot — consider using a signal platform like VoiceOfChain to coordinate your entry signals. Getting a signal and then hammering the API to execute it wastes rate limit budget. Queue your orders and execute them in batches when signals arrive.

Building a Robust Error Logging System

The difference between a bot that survives months in production and one that silently fails after a week is usually logging. When Bybit returns an error code, you need to know which order triggered it, what the parameters were, and whether it's happening repeatedly. Exchanges like Gate.io and KuCoin have similar requirements — a good error logging pattern works across all of them.

Structure your logs around three things: the error code, the full request parameters, and the timestamp. This lets you replay failed requests in testing and identify patterns — like 110007 (insufficient balance) errors that only happen during high volatility, or 10006 (rate limit) clusters that reveal your bot is spiking requests during signal events. If you're getting trade signals from VoiceOfChain or similar platforms, correlate your error timestamps with signal events to find these patterns.

Frequently Asked Questions

What does Bybit error code 10004 mean and how do I fix it?
Error 10004 means your request signature is invalid. The most common cause is signing the wrong string — Bybit V5 requires you to concatenate timestamp + api_key + recv_window + query_string in exactly that order before HMAC-SHA256 signing. Double-check your string construction and ensure you're using the correct API secret, not the API key.
Why do I keep getting error 10002 (request expired) even with a fresh timestamp?
Error 10002 fires when your system clock is out of sync with Bybit's servers by more than your recv_window value (default 5000ms). Run NTP sync on your server, or fetch Bybit's server time via /v5/market/time at startup and compute a local offset to apply to all subsequent timestamps.
What's the difference between Bybit error 10003 and 10004?
Error 10003 means the API key string itself doesn't exist or was deleted — Bybit can't find the key. Error 10004 means Bybit found the key but the signature you computed doesn't match what it expects. If you're getting 10003, check that you're sending the right key value. If you're getting 10004, your signing logic is the problem.
How do I handle Bybit rate limit errors (10006) in my trading bot?
Implement exponential backoff: on a 10006 response, wait 1 second before retrying, then 2 seconds, then 4 seconds. Also monitor the X-Bapi-Limit-Status response header proactively — when it drops below 10 remaining, slow your requests before hitting the limit. High-frequency bots should use Bybit's WebSocket API for market data instead of polling REST endpoints.
What does error 33004 mean and how is it different from 10003?
Error 33004 means your API key exists and authenticated correctly, but it doesn't have permission for the action you're trying to perform. A read-only key trying to place an order returns 33004. The fix is to recreate the API key on Bybit with the correct permission scopes (Trade, Withdraw, etc.) enabled.
Can Bybit API error codes change between API versions?
Yes — Bybit's V3 and V5 APIs use different error code ranges and structures. The V5 API (current) uses the retCode/retMsg envelope consistently across all endpoints. If you're seeing unexpected codes, verify you're hitting V5 endpoints (paths starting with /v5/) and not legacy V3 endpoints which have different response schemas.

Conclusion

Bybit API error codes aren't obstacles — they're a communication channel. Each code tells you exactly what went wrong if you take the time to read it correctly. The authentication errors (10003, 10004, 33004) tell you about your credentials and key configuration. The order errors (110xxx) tell you about instrument rules and account state. The system errors (10002, 10006) tell you about timing and throughput. Build your bot to treat every non-zero retCode as information, not just as a failure to log and ignore. Pair your error handling with real-time market context — platforms like VoiceOfChain can help you understand whether a failed order was a technical error or just a market condition that made your signal invalid before it could execute. Solid error handling is what separates a bot that runs for a week from one that runs for a year.

◈   more on this topic
◉ basics Mastering the ccxt library documentation for crypto traders ⌂ exchanges Mastering the Binance CCXT Library for Crypto Traders ⌬ bots Best Crypto Trading Bots 2025: Profitable AI-Powered Strategies