🎸
🚀 Beta Running
PYNGUP: Rebellion against toxic productivity
Beta limited to 100 spots. Tasks become social commitments instead of lonely to-dos.
Learn how to securely configure and authenticate with the Binance API for cryptocurrency trading bots. This comprehensive guide covers API key generation, security best practices, rate limiting, and your first successful API calls.
Binance offers the most comprehensive API ecosystem for automated trading:
Configure permissions based on your trading bot needs:
| Permission | Description | Recommended |
|---|---|---|
| Read Info | Account balance, order history | ✅ Always |
| Enable Spot Trading | Buy/sell cryptocurrencies | ✅ For trading |
| Enable Futures | Futures trading access | ⚠️ Advanced only |
| Enable Withdrawals | Withdraw funds | ❌ Disable for security |
Limit API access to specific IP addresses for maximum security:
curl ifconfig.meUpdate your .env file with Binance credentials:
# .env file - NEVER commit to version control
BINANCE_API_KEY=your_api_key_here
BINANCE_SECRET_KEY=your_secret_key_here
# Environment settings
BINANCE_TESTNET=true
BINANCE_SANDBOX_URL=https://testnet.binance.vision/api
BINANCE_LIVE_URL=https://api.binance.com/api
# Trading configuration
DEFAULT_SYMBOL=BTCUSDT
BASE_CURRENCY=USDT
QUOTE_CURRENCY=BTC
Create src/config/binance_config.py:
import os
from dotenv import load_dotenv
load_dotenv()
class BinanceConfig:
"""Binance API configuration management"""
# API Credentials
API_KEY = os.getenv('BINANCE_API_KEY')
SECRET_KEY = os.getenv('BINANCE_SECRET_KEY')
# Environment settings
TESTNET = os.getenv('BINANCE_TESTNET', 'true').lower() == 'true'
# API URLs
SANDBOX_URL = 'https://testnet.binance.vision/api'
LIVE_URL = 'https://api.binance.com/api'
@property
def base_url(self):
return self.SANDBOX_URL if self.TESTNET else self.LIVE_URL
# Rate limiting
REQUEST_WEIGHT_LIMIT = 1200 # per minute
ORDER_RATE_LIMIT = 10 # per second
# Default trading pair
DEFAULT_SYMBOL = os.getenv('DEFAULT_SYMBOL', 'BTCUSDT')
def validate_credentials(self):
"""Validate API credentials are present"""
if not self.API_KEY or not self.SECRET_KEY:
raise ValueError("Binance API credentials not found in environment")
if len(self.API_KEY) < 64 or len(self.SECRET_KEY) < 64:
raise ValueError("Invalid Binance API credentials format")
return True
Create src/exchanges/binance_client.py:
import ccxt
import hmac
import hashlib
import time
import requests
from typing import Dict, List, Optional
from src.config.binance_config import BinanceConfig
class BinanceClient:
"""Secure Binance API client with error handling"""
def __init__(self, config: BinanceConfig = None):
self.config = config or BinanceConfig()
self.config.validate_credentials()
# Initialize CCXT client
self.client = ccxt.binance({
'apiKey': self.config.API_KEY,
'secret': self.config.SECRET_KEY,
'sandbox': self.config.TESTNET,
'enableRateLimit': True,
'options': {
'defaultType': 'spot', # spot, margin, future
}
})
# Rate limiting tracking
self.last_request_time = 0
self.request_count = 0
def _rate_limit(self):
"""Implement basic rate limiting"""
current_time = time.time()
if current_time - self.last_request_time < 0.1: # 100ms between requests
time.sleep(0.1)
self.last_request_time = current_time
def test_connection(self) -> bool:
"""Test API connection and credentials"""
try:
self._rate_limit()
# Test with server time (no auth required)
server_time = self.client.fetch_time()
print(f"✅ Server time: {server_time}")
# Test authenticated endpoint
account = self.client.fetch_balance()
print(f"✅ Account access successful")
print(f"💰 USDT Balance: {account.get('USDT', {}).get('free', 0)}")
return True
except ccxt.AuthenticationError as e:
print(f"❌ Authentication failed: {e}")
return False
except ccxt.NetworkError as e:
print(f"❌ Network error: {e}")
return False
except Exception as e:
print(f"❌ Unexpected error: {e}")
return False
def get_account_info(self) -> Dict:
"""Get account information and balances"""
try:
self._rate_limit()
account = self.client.fetch_balance()
# Filter out zero balances
balances = {
currency: balance
for currency, balance in account.items()
if isinstance(balance, dict) and balance.get('total', 0) > 0
}
return {
'success': True,
'balances': balances,
'timestamp': time.time()
}
except Exception as e:
return {
'success': False,
'error': str(e),
'timestamp': time.time()
}
def get_ticker(self, symbol: str = None) -> Dict:
"""Get current price ticker for symbol"""
symbol = symbol or self.config.DEFAULT_SYMBOL
try:
self._rate_limit()
ticker = self.client.fetch_ticker(symbol)
return {
'success': True,
'symbol': symbol,
'price': ticker['last'],
'bid': ticker['bid'],
'ask': ticker['ask'],
'volume': ticker['baseVolume'],
'timestamp': ticker['timestamp']
}
except Exception as e:
return {
'success': False,
'error': str(e),
'symbol': symbol,
'timestamp': time.time()
}
def get_order_book(self, symbol: str = None, limit: int = 10) -> Dict:
"""Get order book for symbol"""
symbol = symbol or self.config.DEFAULT_SYMBOL
try:
self._rate_limit()
order_book = self.client.fetch_order_book(symbol, limit)
return {
'success': True,
'symbol': symbol,
'bids': order_book['bids'][:limit],
'asks': order_book['asks'][:limit],
'timestamp': order_book['timestamp']
}
except Exception as e:
return {
'success': False,
'error': str(e),
'symbol': symbol,
'timestamp': time.time()
}
def place_limit_order(self, symbol: str, side: str,
amount: float, price: float) -> Dict:
"""Place a limit order"""
try:
self._rate_limit()
order = self.client.create_limit_order(
symbol=symbol,
side=side, # 'buy' or 'sell'
amount=amount,
price=price
)
return {
'success': True,
'order_id': order['id'],
'symbol': symbol,
'side': side,
'amount': amount,
'price': price,
'status': order['status'],
'timestamp': order['timestamp']
}
except ccxt.InsufficientFunds as e:
return {
'success': False,
'error': 'Insufficient funds',
'details': str(e),
'timestamp': time.time()
}
except ccxt.InvalidOrder as e:
return {
'success': False,
'error': 'Invalid order',
'details': str(e),
'timestamp': time.time()
}
except Exception as e:
return {
'success': False,
'error': str(e),
'timestamp': time.time()
}
def cancel_order(self, order_id: str, symbol: str) -> Dict:
"""Cancel an open order"""
try:
self._rate_limit()
result = self.client.cancel_order(order_id, symbol)
return {
'success': True,
'order_id': order_id,
'symbol': symbol,
'status': result['status'],
'timestamp': time.time()
}
except Exception as e:
return {
'success': False,
'error': str(e),
'order_id': order_id,
'timestamp': time.time()
}
def get_open_orders(self, symbol: str = None) -> Dict:
"""Get all open orders"""
try:
self._rate_limit()
orders = self.client.fetch_open_orders(symbol)
return {
'success': True,
'orders': orders,
'count': len(orders),
'timestamp': time.time()
}
except Exception as e:
return {
'success': False,
'error': str(e),
'timestamp': time.time()
}
Create test_binance_connection.py:
#!/usr/bin/env python3
"""
Test Binance API connection and basic functionality
"""
from src.exchanges.binance_client import BinanceClient
from src.config.binance_config import BinanceConfig
def test_api_connection():
"""Test basic API connectivity"""
print("🔧 Testing Binance API Connection...\n")
try:
# Initialize client
config = BinanceConfig()
client = BinanceClient(config)
print(f"🌍 Environment: {'Testnet' if config.TESTNET else 'Live Trading'}")
print(f"🔗 API URL: {config.base_url}")
# Test connection
if not client.test_connection():
print("❌ Connection test failed!")
return False
# Test account access
print("\n📊 Testing account access...")
account_info = client.get_account_info()
if account_info['success']:
print("✅ Account information retrieved")
balances = account_info['balances']
if balances:
print("\n💰 Current balances:")
for currency, balance in balances.items():
free = balance.get('free', 0)
locked = balance.get('used', 0)
total = balance.get('total', 0)
if total > 0:
print(f" {currency}: {total:.8f} (Free: {free:.8f}, Locked: {locked:.8f})")
else:
print(" No balances found (empty account)")
else:
print(f"❌ Account access failed: {account_info['error']}")
return False
# Test market data
print("\n📈 Testing market data access...")
ticker = client.get_ticker('BTCUSDT')
if ticker['success']:
print(f"✅ Market data retrieved")
print(f" BTC/USDT Price: ${ticker['price']:,.2f}")
print(f" Bid: ${ticker['bid']:,.2f}")
print(f" Ask: ${ticker['ask']:,.2f}")
print(f" 24h Volume: {ticker['volume']:,.2f} BTC")
else:
print(f"❌ Market data failed: {ticker['error']}")
return False
# Test order book
print("\n📋 Testing order book access...")
order_book = client.get_order_book('BTCUSDT', limit=5)
if order_book['success']:
print("✅ Order book retrieved")
print(" Top 5 Bids:")
for price, amount in order_book['bids'][:5]:
print(f" ${price:,.2f} - {amount:.6f} BTC")
print(" Top 5 Asks:")
for price, amount in order_book['asks'][:5]:
print(f" ${price:,.2f} - {amount:.6f} BTC")
else:
print(f"❌ Order book failed: {order_book['error']}")
return False
print("\n🎉 All tests passed! Binance API is ready for trading.")
return True
except Exception as e:
print(f"❌ Test failed with error: {e}")
return False
def test_paper_trading():
"""Test paper trading functionality"""
print("\n🧪 Testing paper trading (simulation only)...")
client = BinanceClient()
# Get current price
ticker = client.get_ticker('BTCUSDT')
if not ticker['success']:
print("❌ Cannot get current price for testing")
return False
current_price = ticker['price']
print(f"📊 Current BTC price: ${current_price:,.2f}")
# Simulate buy order (10% below market)
test_buy_price = current_price * 0.9
test_amount = 0.001 # 0.001 BTC
print(f"🧪 Simulating buy order:")
print(f" Amount: {test_amount} BTC")
print(f" Price: ${test_buy_price:,.2f}")
print(f" Total: ${test_buy_price * test_amount:.2f}")
if BinanceConfig().TESTNET:
print("✅ Safe to test on testnet")
# Uncomment below to test actual order placement on testnet
# result = client.place_limit_order('BTCUSDT', 'buy', test_amount, test_buy_price)
# print(f"Order result: {result}")
else:
print("⚠️ Live trading detected - skipping actual order placement")
return True
if __name__ == "__main__":
success = test_api_connection()
if success:
test_paper_trading()
else:
print("\n❌ Fix connection issues before proceeding to trading")
print("\nTroubleshooting checklist:")
print("1. Verify API keys in .env file")
print("2. Check API key permissions on Binance")
print("3. Ensure IP address is whitelisted")
print("4. Confirm 2FA is enabled on account")
| Limit Type | Restriction | Impact |
|---|---|---|
| Request Weight | 1,200 per minute | General API calls |
| Order Rate | 10 per second | Order placement/cancellation |
| Raw Requests | 6,000 per 5 minutes | Total API requests |
Create src/utils/rate_limiter.py:
import time
from collections import deque
from typing import Dict
class RateLimiter:
"""Advanced rate limiting for Binance API"""
def __init__(self):
self.request_times = deque()
self.order_times = deque()
self.weight_used = 0
self.weight_reset_time = time.time() + 60
def can_make_request(self, weight: int = 1) -> bool:
"""Check if request can be made without hitting limits"""
current_time = time.time()
# Reset weight counter every minute
if current_time > self.weight_reset_time:
self.weight_used = 0
self.weight_reset_time = current_time + 60
# Check weight limit
if self.weight_used + weight > 1200:
return False
# Check raw request limit (6000 per 5 minutes)
self._clean_old_requests(current_time, 300) # 5 minutes
if len(self.request_times) >= 6000:
return False
return True
def can_make_order(self) -> bool:
"""Check if order can be placed (10 per second)"""
current_time = time.time()
self._clean_old_orders(current_time, 1) # 1 second
return len(self.order_times) < 10
def record_request(self, weight: int = 1):
"""Record a request for rate limiting"""
current_time = time.time()
self.request_times.append(current_time)
self.weight_used += weight
def record_order(self):
"""Record an order for rate limiting"""
current_time = time.time()
self.order_times.append(current_time)
def _clean_old_requests(self, current_time: float, window: int):
"""Remove old requests outside time window"""
cutoff = current_time - window
while self.request_times and self.request_times[0] < cutoff:
self.request_times.popleft()
def _clean_old_orders(self, current_time: float, window: int):
"""Remove old orders outside time window"""
cutoff = current_time - window
while self.order_times and self.order_times[0] < cutoff:
self.order_times.popleft()
def get_wait_time(self, weight: int = 1) -> float:
"""Get time to wait before next request"""
if self.can_make_request(weight):
return 0
# Calculate wait time based on weight limit
time_until_reset = self.weight_reset_time - time.time()
return max(0, time_until_reset)
Create src/utils/error_handler.py:
import time
import logging
from typing import Callable, Any
class BinanceErrorHandler:
"""Handle Binance API errors with automatic retries"""
def __init__(self, max_retries: int = 3, base_delay: float = 1.0):
self.max_retries = max_retries
self.base_delay = base_delay
self.logger = logging.getLogger(__name__)
def with_retry(self, func: Callable, *args, **kwargs) -> Any:
"""Execute function with automatic retry on recoverable errors"""
last_exception = None
for attempt in range(self.max_retries + 1):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
error_code = getattr(e, 'code', None)
# Don't retry on authentication errors
if error_code in [-1022, -2015]: # Invalid signature, auth errors
self.logger.error(f"Authentication error: {e}")
raise e
# Don't retry on insufficient funds
if error_code == -2010:
self.logger.error(f"Insufficient funds: {e}")
raise e
# Retry on rate limits and network errors
if error_code == -1003 or "network" in str(e).lower():
if attempt < self.max_retries:
delay = self.base_delay * (2 ** attempt) # Exponential backoff
self.logger.warning(f"Retrying in {delay}s after error: {e}")
time.sleep(delay)
continue
# Log and potentially retry other errors
if attempt < self.max_retries:
delay = self.base_delay * (2 ** attempt)
self.logger.warning(f"Attempt {attempt + 1} failed: {e}. Retrying in {delay}s")
time.sleep(delay)
else:
self.logger.error(f"All {self.max_retries + 1} attempts failed")
raise last_exception
raise last_exception
Easy environment switching in your .env:
# Development (Testnet)
BINANCE_TESTNET=true
BINANCE_API_KEY=testnet_api_key
BINANCE_SECRET_KEY=testnet_secret_key
# Production (Live Trading)
# BINANCE_TESTNET=false
# BINANCE_API_KEY=live_api_key
# BINANCE_SECRET_KEY=live_secret_key
Symptom: "Invalid signature" errors
Solutions:
Symptom: "Request weight exceeded" errors
Solutions:
Symptom: Timeout or connection errors
Solutions:
Your Binance API is now configured and ready for trading! In the next article, we'll implement our first trading strategy:
Test your setup first! Run the connection test script and ensure all functionality works before proceeding to live trading strategies.
Remember: Always start with testnet, test thoroughly, and never risk more than you can afford to lose. The next article covers implementing your first profitable grid trading strategy.
Nikolai Fischer is the founder of Kommune3 (since 2007) and a leading expert in Drupal development and tech entrepreneurship. With 17+ years of experience, he has led hundreds of projects and achieved #1 on Hacker News. As host of the "Kommit mich" podcast and founder of skillution, he combines technical expertise with entrepreneurial thinking. His articles about Supabase, modern web development, and systematic problem-solving have influenced thousands of developers worldwide.
Comments