Binance Withdrawal Restrictions API: Complete Developer Guide
Learn how to query Binance withdrawal restrictions via API, handle limits programmatically, and build robust withdrawal automation with real code examples.
Learn how to query Binance withdrawal restrictions via API, handle limits programmatically, and build robust withdrawal automation with real code examples.
Withdrawal restrictions on Binance aren't arbitrary — they exist because of KYC verification levels, network congestion policies, security holds, and regional compliance rules. If you're building a trading bot or automating fund management, hitting a withdrawal restriction at the wrong moment can lock up capital and break your entire flow. The Binance API exposes everything you need to check restrictions before attempting a withdrawal, handle errors gracefully, and route funds intelligently. Here's how to do it properly.
Binance applies withdrawal restrictions across several dimensions. First, there's the account verification tier — unverified accounts face a 2 BTC daily withdrawal limit, while verified users get 100 BTC equivalent. Second, there are network-specific suspensions: Binance periodically halts withdrawals on specific blockchain networks during maintenance or during congestion events. Third, individual coins can be suspended separately from their networks. Finally, there are IP-based and 2FA-based security holds that trigger a 24-hour withdrawal freeze after account changes.
Compared to platforms like Bybit and OKX, which offer more granular API responses for restriction reasons, Binance's API requires you to cross-reference multiple endpoints to get the full picture. That said, it's entirely scriptable — and once you know the endpoints, you can build a comprehensive pre-flight check before initiating any withdrawal.
Always query withdrawal status BEFORE attempting a withdrawal. A failed withdrawal attempt doesn't just return an error — on Binance it can trigger additional security reviews and temporary holds.
Withdrawal-related endpoints require a signed request using your API secret. Your API key must have the 'Enable Withdrawals' permission enabled in the Binance dashboard. Never enable this permission on keys you use for read-only market data — keep separate keys for trading and withdrawal operations.
import hashlib
import hmac
import time
import requests
from urllib.parse import urlencode
API_KEY = 'your_api_key_here'
API_SECRET = 'your_api_secret_here'
BASE_URL = 'https://api.binance.com'
def sign_request(params: dict) -> str:
query_string = urlencode(params)
signature = hmac.new(
API_SECRET.encode('utf-8'),
query_string.encode('utf-8'),
hashlib.sha256
).hexdigest()
return signature
def get_signed_headers() -> dict:
return {'X-MBX-APIKEY': API_KEY}
def build_signed_params(params: dict = None) -> dict:
if params is None:
params = {}
params['timestamp'] = int(time.time() * 1000)
params['recvWindow'] = 5000
params['signature'] = sign_request(params)
return params
The `recvWindow` parameter is important — it defines how long (in milliseconds) a request remains valid after the timestamp. Keep it between 5000 and 60000ms. Lower values are more secure but require tighter clock synchronization between your server and Binance's servers.
The most important endpoint for checking withdrawal restrictions is `/sapi/v1/capital/config/getall`. This returns a list of all coins with their network configurations, including whether withdrawals are currently enabled or suspended per network.
def get_coin_withdrawal_status(coin: str = None) -> list:
"""
Fetch withdrawal status for all coins or a specific coin.
Returns list of coin configs with network-level restriction details.
"""
endpoint = '/sapi/v1/capital/config/getall'
params = build_signed_params()
try:
response = requests.get(
BASE_URL + endpoint,
headers=get_signed_headers(),
params=params,
timeout=10
)
response.raise_for_status()
data = response.json()
if coin:
data = [c for c in data if c['coin'].upper() == coin.upper()]
# Extract withdrawal restriction details per network
results = []
for coin_data in data:
for network in coin_data.get('networkList', []):
results.append({
'coin': coin_data['coin'],
'network': network['network'],
'withdraw_enabled': network['withdrawEnable'],
'withdraw_min': network['withdrawMin'],
'withdraw_fee': network['withdrawFee'],
'withdraw_integer_multiple': network['withdrawIntegerMultiple'],
'special_tips': network.get('specialTips', '')
})
return results
except requests.exceptions.HTTPError as e:
print(f'HTTP Error: {e.response.status_code} - {e.response.text}')
raise
except requests.exceptions.Timeout:
print('Request timed out — Binance API may be slow')
raise
# Example usage
usdt_networks = get_coin_withdrawal_status('USDT')
for net in usdt_networks:
status = 'OPEN' if net['withdraw_enabled'] else 'RESTRICTED'
print(f"{net['coin']} on {net['network']}: {status} | Min: {net['withdraw_min']} | Fee: {net['withdraw_fee']}")
The `withdrawEnable` boolean is the key field. When it's `false`, any withdrawal attempt on that network will fail immediately. The `specialTips` field sometimes contains human-readable explanations — maintenance windows, congestion notices, or regulatory holds. Always log this field because it often contains timing information about when the restriction is expected to lift.
Network status is only half the picture. You also need to verify your account's withdrawal capacity. The `/sapi/v1/account/info` endpoint returns your verification tier and trading level, while `/sapi/v1/capital/withdraw/history` lets you calculate how much of your daily limit you've already consumed.
from datetime import datetime, timedelta
def get_daily_withdrawal_usage(hours_back: int = 24) -> dict:
"""
Calculate total withdrawal amount in the last 24 hours.
Returns dict with total withdrawn and estimated remaining capacity.
"""
endpoint = '/sapi/v1/capital/withdraw/history'
start_time = int((datetime.utcnow() - timedelta(hours=hours_back)).timestamp() * 1000)
params = build_signed_params({
'startTime': start_time,
'limit': 1000
})
try:
response = requests.get(
BASE_URL + endpoint,
headers=get_signed_headers(),
params=params,
timeout=10
)
response.raise_for_status()
history = response.json()
# Sum completed and processing withdrawals only
active_statuses = {1, 2, 6} # email_sent, cancelled=3, rejected=4, processing=5, success=6
total_usdt_equivalent = 0.0
completed = [
w for w in history
if int(w['status']) in {1, 2, 6}
]
return {
'withdrawal_count': len(completed),
'withdrawals': completed,
'raw_history': history
}
except requests.exceptions.HTTPError as e:
if e.response.status_code == 403:
print('API key lacks withdrawal history permission')
raise
# Check account info for KYC tier
def get_account_api_restrictions() -> dict:
endpoint = '/sapi/v1/account/apiRestrictions'
params = build_signed_params()
response = requests.get(
BASE_URL + endpoint,
headers=get_signed_headers(),
params=params,
timeout=10
)
response.raise_for_status()
data = response.json()
return {
'ip_restrict': data.get('ipRestrict'),
'enable_withdrawals': data.get('enableWithdrawals'),
'enable_internal_transfer': data.get('enableInternalTransfer'),
'permits_universal_transfer': data.get('permitsUniversalTransfer'),
'trading_authorized': data.get('enableSpotAndMarginTrading')
}
restrictions = get_account_api_restrictions()
print(f"Withdrawals enabled on this API key: {restrictions['enable_withdrawals']}")
If 'enableWithdrawals' returns False from the apiRestrictions endpoint, your API key itself doesn't have withdrawal permissions — no amount of troubleshooting the request will fix this. You need to regenerate the key in the Binance dashboard with withdrawal permissions enabled.
Rather than attempting a withdrawal and handling the error, the professional approach is a pre-flight check that validates all conditions before submitting. This is especially important in automated systems where a failed withdrawal might not trigger an alert for minutes. On platforms like OKX and Gate.io, similar pre-check patterns are common in production trading bots precisely because they reduce unnecessary API calls and error cascades.
class WithdrawalPreflightError(Exception):
pass
def preflight_withdrawal_check(coin: str, network: str, amount: float) -> dict:
"""
Validates all conditions before attempting a withdrawal.
Raises WithdrawalPreflightError with specific reason if blocked.
Returns fee and minimum info if all checks pass.
"""
# Check 1: API key permissions
api_restrictions = get_account_api_restrictions()
if not api_restrictions['enable_withdrawals']:
raise WithdrawalPreflightError(
'API key does not have withdrawal permissions enabled'
)
# Check 2: Coin/network availability
coin_networks = get_coin_withdrawal_status(coin)
target_network = None
for net in coin_networks:
if net['network'].upper() == network.upper():
target_network = net
break
if not target_network:
raise WithdrawalPreflightError(
f'Network {network} not found for {coin}'
)
if not target_network['withdraw_enabled']:
tips = target_network.get('special_tips', 'No details available')
raise WithdrawalPreflightError(
f'Withdrawals suspended for {coin} on {network}. Reason: {tips}'
)
# Check 3: Minimum amount
min_amount = float(target_network['withdraw_min'])
if amount < min_amount:
raise WithdrawalPreflightError(
f'Amount {amount} is below minimum {min_amount} for {coin}/{network}'
)
fee = float(target_network['withdraw_fee'])
return {
'coin': coin,
'network': network,
'amount': amount,
'fee': fee,
'amount_after_fee': amount - fee,
'ready': True
}
# Usage
try:
check = preflight_withdrawal_check('USDT', 'TRX', 50.0)
print(f"Ready to withdraw. You will receive: {check['amount_after_fee']} USDT")
print(f"Network fee: {check['fee']} USDT")
except WithdrawalPreflightError as e:
print(f"Withdrawal blocked: {e}")
# Optionally alert via VoiceOfChain webhook or Telegram notification
This pattern integrates well with signal-driven trading systems. If you're using VoiceOfChain for real-time trading signals and want to automate fund movement after a signal fires, wrapping your withdrawal logic in a preflight check ensures the automation fails fast and loudly rather than silently dropping transactions.
Binance uses numeric error codes in their API responses. When a withdrawal fails, the error code tells you exactly what went wrong. Here are the most common ones traders encounter in production systems:
| Error Code | Meaning | Fix |
|---|---|---|
| -1100 | Illegal characters in parameter | Validate address format before submission |
| -1102 | Mandatory parameter missing | Check all required fields: coin, network, address, amount |
| -3044 | Withdrawals are not allowed for this asset | Check coin status via capital/config/getall |
| -3041 | Balance insufficient | Verify available balance minus fee |
| -3025 | This withdraw is not allowed. Please try again later | Account security hold — wait 24-48h after account changes |
| -1003 | Too many requests | Implement rate limiting: max 10 req/min on SAPI endpoints |
Error -3025 is particularly frustrating because the message is vague. It almost always means one of three things: a recent password change, a recently added withdrawal address (Binance holds these for 24 hours), or a recent API key modification. Unlike KuCoin which sends an email explaining the hold, Binance leaves you to diagnose it via the error code alone.
Building reliable withdrawal automation on Binance requires treating restrictions as a first-class concern, not an afterthought. The combination of pre-flight checks, proper error code handling, and dynamic fee fetching eliminates the most common failure modes. Keep your withdrawal logic decoupled from your trading logic — a network suspension on TRC-20 USDT shouldn't halt your entire trading bot when ERC-20 might still be available.
For production systems, add a monitoring layer that polls withdrawal status every few minutes and alerts you if a transaction stays in 'processing' status longer than expected. If you're running signal-based strategies through a platform like VoiceOfChain, this kind of automated fund routing between exchanges — Binance to Bybit for example — becomes a real competitive edge when you can execute it reliably and programmatically.