Binance API Signature Authentication and Secure Key Management
Binance API Signature Authentication and Secure Key Management
Getting {"code":-1022,"msg":"Signature for this request is not valid"} from Binance is one of the most frustrating debugging experiences. Here's how signing actually works and how to manage your keys securely.
How Binance Signing Works
Every private API call requires an HMAC-SHA256 signature. The process:
- Build your query string with all parameters
- Append
timestamp=<unix_ms> - Sign the entire query string with your API secret
- Append
signature=<hmac_hex>to the request
import hmac
import hashlib
import time
import requests
from urllib.parse import urlencode
class BinanceAuth:
BASE_URL = "https://api.binance.com"
def __init__(self, api_key: str, api_secret: str):
self.api_key = api_key
self.api_secret = api_secret.encode('utf-8')
self.session = requests.Session()
self.session.headers.update({
'X-MBX-APIKEY': self.api_key
})
def _sign(self, params: dict) -> dict:
"""Add timestamp and HMAC signature to params."""
params['timestamp'] = int(time.time() * 1000)
query_string = urlencode(params, doseq=True)
signature = hmac.new(
self.api_secret,
query_string.encode('utf-8'),
hashlib.sha256
).hexdigest()
params['signature'] = signature
return params
def get_account(self) -> dict:
"""Get account information (signed request)."""
params = self._sign({})
response = self.session.get(
f"{self.BASE_URL}/api/v3/account",
params=params
)
response.raise_for_status()
return response.json()
def place_order(self, symbol: str, side: str,
order_type: str, **kwargs) -> dict:
"""Place an order (signed request)."""
params = {
'symbol': symbol,
'side': side.upper(),
'type': order_type.upper(),
**kwargs
}
params = self._sign(params)
response = self.session.post(
f"{self.BASE_URL}/api/v3/order",
params=params
)
response.raise_for_status()
return response.json()
Common Signing Errors
| Error | Cause | Fix |
|---|---|---|
| -1022 Invalid signature | Wrong query string order | Use urlencode consistently |
| -1021 Timestamp outside recvWindow | Clock drift > 5s | Sync system clock with NTP |
| -1002 Unauthorized | Wrong API key header | Use X-MBX-APIKEY header |
| -2015 Invalid API-key | Key disabled or deleted | Regenerate on Binance dashboard |
Secure Key Storage
Never hardcode API keys. Use environment variables with a .env file:
import os
from pathlib import Path
def load_api_keys() -> tuple:
"""Load API keys from environment or .env file."""
api_key = os.getenv('BINANCE_API_KEY')
api_secret = os.getenv('BINANCE_API_SECRET')
if not api_key or not api_secret:
env_path = Path.home() / '.trading' / '.env'
if env_path.exists():
for line in env_path.read_text().splitlines():
if '=' in line and not line.startswith('#'):
key, val = line.strip().split('=', 1)
os.environ[key] = val
api_key = os.getenv('BINANCE_API_KEY')
api_secret = os.getenv('BINANCE_API_SECRET')
if not api_key or not api_secret:
raise ValueError("API keys not found")
return api_key, api_secret
# Security checklist for .env file:
# chmod 600 ~/.trading/.env
# Add to .gitignore
# Never commit to version control
IP Whitelisting
Always enable IP restrictions on Binance API keys:
- Go to Binance → API Management
- Edit your key → Restrict access to trusted IPs only
- For VPS: add your server's public IP
- For local dev: add your home IP (remember it changes)
This layered security approach — signing + IP whitelisting + encrypted storage — is the same pattern used across the ClawDUX platform to protect user credentials and agent API keys.
The core logic discussed in this article has been integrated into the ClawDUX API. Access ClawDUX-core for full permissions, or browse the marketplace to discover verified trading strategies.