◈   ⌘ api · Intermediate

Binance Corporate Subaccount API: Complete Setup Guide

Master Binance corporate subaccount API setup, authentication, and automation to manage multiple trading accounts programmatically with real code examples.

Uncle Solieditor · voc · 18.05.2026 ·views 2
◈   Contents
  1. → What Is a Binance Corporate Subaccount?
  2. → Setting Up Authentication for the Subaccount API
  3. → Core Subaccount Operations: Create, List, and Transfer
  4. → Monitoring Subaccount Positions and P&L in Real Time
  5. → Error Handling and Rate Limit Management
  6. → Frequently Asked Questions
  7. → Putting It All Together

Running multiple trading desks, managing client funds, or operating a prop firm means dealing with dozens — sometimes hundreds — of separate accounts. Binance's corporate subaccount API was built exactly for this: it lets a master account programmatically create subaccounts, move funds between them, query balances, and execute trades, all through a single authenticated entry point. If you've ever manually shuffled USDT between accounts at 2am before a volatile open, you already know why this matters.

What Is a Binance Corporate Subaccount?

A corporate subaccount on Binance is a child account linked to a master (broker) account. The master account holds administrative control: it can create subaccounts, enable trading permissions, transfer assets, and read all balances via the API. Each subaccount operates independently — it has its own API keys, order history, and positions — but the master account can oversee everything from a single dashboard or, better, through code.

This architecture is popular with trading firms, hedge funds, and high-volume signal providers. Platforms like Bybit and OKX offer similar structures, but Binance's subaccount API is arguably the most feature-complete, with endpoints covering spot, futures, margin, and options accounts under one umbrella.

Binance requires KYB (Know Your Business) verification before enabling corporate subaccount features. Personal accounts with standard KYC cannot access the subaccount creation endpoints — apply through Binance's institutional onboarding portal.

Setting Up Authentication for the Subaccount API

Binance uses HMAC-SHA256 signed requests. Every call to a private subaccount endpoint requires your master account API key in the header and a signature appended to the query string. The signature is computed over the full parameter string including a timestamp. Here's a clean Python setup that handles signing for every request automatically:

import hmac
import hashlib
import time
import requests
from urllib.parse import urlencode

API_KEY = 'your_master_api_key'
SECRET_KEY = 'your_master_secret_key'
BASE_URL = 'https://api.binance.com'

def sign_params(params: dict) -> dict:
    params['timestamp'] = int(time.time() * 1000)
    query_string = urlencode(params)
    signature = hmac.new(
        SECRET_KEY.encode('utf-8'),
        query_string.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    params['signature'] = signature
    return params

def get_headers() -> dict:
    return {'X-MBX-APIKEY': API_KEY}

def api_get(endpoint: str, params: dict = None) -> dict:
    params = sign_params(params or {})
    response = requests.get(
        f'{BASE_URL}{endpoint}',
        params=params,
        headers=get_headers()
    )
    response.raise_for_status()
    return response.json()

def api_post(endpoint: str, params: dict = None) -> dict:
    params = sign_params(params or {})
    response = requests.post(
        f'{BASE_URL}{endpoint}',
        params=params,
        headers=get_headers()
    )
    response.raise_for_status()
    return response.json()

Store your keys in environment variables, never hardcode them. Use python-dotenv or a secrets manager in production. Note that the master API key must have the 'Sub Account' permission enabled — go to Binance API Management and check that box explicitly, it's off by default.

Core Subaccount Operations: Create, List, and Transfer

Once authentication is wired up, the three operations you'll use constantly are: listing existing subaccounts, querying their balances, and transferring funds between master and sub. Here's how all three look in practice:

# List all subaccounts under master
def list_subaccounts(page: int = 1, limit: int = 200) -> list:
    data = api_get('/sapi/v1/sub-account/list', {
        'page': page,
        'limit': limit
    })
    return data.get('subAccounts', [])

# Get subaccount spot balances
def get_subaccount_balances(email: str) -> list:
    data = api_get('/sapi/v3/sub-account/assets', {
        'email': email
    })
    # Filter out zero balances
    return [
        asset for asset in data.get('balances', [])
        if float(asset['free']) > 0 or float(asset['locked']) > 0
    ]

# Transfer from master to subaccount
def transfer_to_sub(to_email: str, asset: str, amount: float) -> dict:
    result = api_post('/sapi/v1/sub-account/universalTransfer', {
        'toEmail': to_email,
        'fromAccountType': 'SPOT',
        'toAccountType': 'SPOT',
        'asset': asset,
        'amount': str(amount)
    })
    return result

# Transfer from subaccount back to master
def transfer_from_sub(from_email: str, asset: str, amount: float) -> dict:
    result = api_post('/sapi/v1/sub-account/universalTransfer', {
        'fromEmail': from_email,
        'fromAccountType': 'SPOT',
        'toAccountType': 'SPOT',
        'asset': asset,
        'amount': str(amount)
    })
    return result

# Example usage
if __name__ == '__main__':
    subs = list_subaccounts()
    for sub in subs[:5]:
        print(f"Email: {sub['email']}, Active: {sub['isFreeze']}")
        balances = get_subaccount_balances(sub['email'])
        for b in balances:
            print(f"  {b['asset']}: {b['free']} free, {b['locked']} locked")

The universalTransfer endpoint handles cross-account moves between spot, USDT-M futures, COIN-M futures, and margin wallets. Always specify both fromAccountType and toAccountType — omitting either returns a 400 error that wastes debugging time.

Transfer limits apply per subaccount per day. On Binance, the default daily transfer limit for subaccounts is tied to the subaccount's KYC level. If transfers are failing silently, check the subaccount's verification status first before debugging the code.

Monitoring Subaccount Positions and P&L in Real Time

For risk management across a portfolio of subaccounts, you need a way to poll positions and unrealized P&L programmatically. Combine this with a signal layer — like VoiceOfChain, which delivers real-time order flow and whale movement signals — and you can build a system that monitors aggregate exposure across all subs and alerts when total net delta breaches a threshold.

import asyncio
from typing import Optional

# Get futures positions for a subaccount
def get_sub_futures_positions(email: str) -> list:
    try:
        data = api_get('/sapi/v2/sub-account/futures/positionRisk', {
            'email': email,
            'futuresType': 1  # 1 = USDT-M, 2 = COIN-M
        })
        # Filter to open positions only
        return [
            pos for pos in data
            if float(pos.get('positionAmt', 0)) != 0
        ]
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 400:
            # Subaccount may not have futures enabled
            print(f'Futures not enabled for {email}')
            return []
        raise

# Aggregate unrealized PnL across all subaccounts
def aggregate_portfolio_pnl(emails: list) -> dict:
    total_pnl = 0.0
    breakdown = {}
    
    for email in emails:
        positions = get_sub_futures_positions(email)
        sub_pnl = sum(float(p.get('unRealizedProfit', 0)) for p in positions)
        breakdown[email] = round(sub_pnl, 4)
        total_pnl += sub_pnl
    
    return {
        'total_unrealized_pnl': round(total_pnl, 4),
        'by_account': breakdown
    }

# Run a simple monitoring loop
def monitor_portfolio(emails: list, interval_seconds: int = 30):
    import time
    while True:
        report = aggregate_portfolio_pnl(emails)
        print(f"[{time.strftime('%H:%M:%S')}] Total PnL: ${report['total_unrealized_pnl']:+.2f}")
        for email, pnl in report['by_account'].items():
            if abs(pnl) > 100:  # Only show accounts with significant exposure
                print(f'  {email}: ${pnl:+.2f}')
        time.sleep(interval_seconds)

This kind of monitoring loop is the backbone of any serious multi-account operation. Pair it with VoiceOfChain's signal feed to get context on why positions are moving — whether it's a large exchange outflow, a whale accumulation pattern, or a fear/greed shift — rather than just watching numbers change blindly.

Error Handling and Rate Limit Management

Binance's API has strict rate limits, and subaccount endpoints are often heavier-weight than standard trading endpoints. The /sapi/ namespace (which most subaccount endpoints use) has separate weight buckets from /api/. Hitting the limit returns a 429, and a second violation within the window returns a 418 — an IP ban. Build defensive handling from day one.

import time
import logging
from functools import wraps

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('binance_sub')

def retry_on_rate_limit(max_retries: int = 3, backoff_factor: float = 2.0):
    """Decorator to handle 429/418 rate limit responses with exponential backoff."""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except requests.exceptions.HTTPError as e:
                    status = e.response.status_code
                    if status == 429:
                        retry_after = int(e.response.headers.get('Retry-After', 60))
                        wait = retry_after * (backoff_factor ** attempt)
                        logger.warning(f'Rate limited (429). Waiting {wait:.0f}s before retry {attempt+1}/{max_retries}')
                        time.sleep(wait)
                    elif status == 418:
                        logger.error('IP banned (418). Stop all requests immediately.')
                        raise  # Do not retry a ban
                    elif status == 400:
                        error_body = e.response.json()
                        logger.error(f'Bad request: code={error_body.get("code")}, msg={error_body.get("msg")}')
                        raise  # Bad params, no point retrying
                    else:
                        raise
            raise RuntimeError(f'Max retries ({max_retries}) exceeded')
        return wrapper
    return decorator

# Apply decorator to your API calls
@retry_on_rate_limit(max_retries=3)
def safe_list_subaccounts() -> list:
    return api_get('/sapi/v1/sub-account/list', {'limit': 200}).get('subAccounts', [])

# Track used weight from response headers
def api_get_with_weight_tracking(endpoint: str, params: dict = None) -> dict:
    params = sign_params(params or {})
    response = requests.get(
        f'{BASE_URL}{endpoint}',
        params=params,
        headers=get_headers()
    )
    used_weight = response.headers.get('X-SAPI-Used-IP-Weight-1m', 'unknown')
    logger.debug(f'Used SAPI weight (1m): {used_weight}')
    response.raise_for_status()
    return response.json()

Each subaccount endpoint has a documented request weight. Listing subaccounts costs 1 weight, fetching futures positions costs 10 per call. When you're looping over 50+ subaccounts polling positions every 10 seconds, weight adds up fast. Cache balances where freshness isn't critical, and use WebSocket streams for position updates instead of polling where possible.

Common Subaccount API Endpoints and Their Request Weights
EndpointMethodWeightDescription
/sapi/v1/sub-account/listGET1List all subaccounts
/sapi/v3/sub-account/assetsGET1Get spot balances for a sub
/sapi/v2/sub-account/futures/positionRiskGET10Get futures positions for a sub
/sapi/v1/sub-account/universalTransferPOST1Transfer assets between accounts
/sapi/v2/sub-account/futures/accountGET10Get futures account summary for a sub

Frequently Asked Questions

Do I need a corporate account to use Binance subaccount API?
Yes. The subaccount creation and management endpoints require a verified corporate or broker account. Standard individual accounts with basic KYC cannot create subaccounts. You need to apply through Binance's institutional portal and complete KYB verification.
Can subaccounts on Binance trade independently with their own API keys?
Yes. Each subaccount can generate its own API keys scoped to specific permissions (read, trade, withdraw). The subaccount keys operate independently of the master account keys, so you can give them to strategies or clients without exposing master credentials.
How is Binance subaccount API different from Bybit or OKX sub-accounts?
All three offer subaccount APIs with similar concepts, but Binance's is the most mature with broader endpoint coverage including cross-margin and options. OKX has strong sub-account support for its unified account model, while Bybit's sub-account API is well-documented but limited to a smaller number of managed accounts on lower tiers.
What's the maximum number of subaccounts I can create?
The default limit is typically 200 subaccounts per master account on Binance, but broker accounts with higher institutional tiers can request increases. Contact Binance's institutional team directly if you need to exceed the default limit for your operation.
How do I handle futures positions in subaccounts via API?
Use /sapi/v2/sub-account/futures/positionRisk with the sub's email and specify futuresType (1 for USDT-M, 2 for COIN-M). Note that futures must be explicitly enabled for each subaccount before this endpoint returns data — you'll get a 400 error otherwise.
Is there a sandbox or testnet for Binance subaccount API?
Binance's testnet (testnet.binance.vision) supports basic spot trading but does not fully replicate the /sapi/ subaccount endpoints. For testing, use small amounts on mainnet with a dedicated test subaccount, or mock the HTTP responses in your test suite.

Putting It All Together

The Binance corporate subaccount API is genuinely powerful once you get past the initial setup friction. Authentication follows a consistent signing pattern, the endpoint structure is logical once you know the /sapi/ vs /api/ split, and the transfer system is flexible enough to handle complex fund flows between spot, futures, and margin wallets across dozens of accounts.

The real leverage comes from combining API control with good signal sources. Using VoiceOfChain alongside your subaccount management code means you're not just moving funds — you're moving them in response to real market context: on-chain flow data, exchange order book imbalances, and whale activity patterns that give your multi-account operation an informational edge. Whether you're running a fund, a prop desk, or a managed account service, that combination of automated account management and quality market signals is what separates systematic operators from those still doing it by hand.

◈   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