◈   ⌘ api · Intermediate

Binance API Error 1015: Rate Limit Explained for Traders

Hit Binance API error 1015? Learn what rate limits mean, why they trigger, and how to code around them without getting your IP banned.

Uncle Solieditor · voc · 18.05.2026 ·views 1
◈   Contents
  1. → What Is Binance API Error 1015?
  2. → Understanding Binance's Rate Limit Architecture
  3. → Reading Rate Limit Headers in Python
  4. → Handling Error -1015 with Exponential Backoff
  5. → Designing a Rate-Limit-Aware Bot Architecture
  6. → Using WebSocket to Reduce REST API Pressure
  7. → Frequently Asked Questions
  8. → Conclusion

You're running a bot, everything looks good, and then — boom — your requests start failing with HTTP 429 or a JSON body containing code -1015. Your IP might even get auto-banned. If this sounds familiar, you've hit Binance's rate limiting system. It's not a bug in your code. It's a hard wall that every trader hitting the API eventually runs into, and learning how to work with it is the difference between a stable bot and one that randomly dies at 3am.

What Is Binance API Error 1015?

Error code -1015 from Binance means 'Too many new orders.' It's specifically tied to order placement rate limits, not general request limits. Binance enforces several distinct rate limit buckets simultaneously: requests per minute (IP-based), orders per second, and orders per day. The -1015 error fires when you exceed the order frequency limit — by default, 10 orders per second or 100,000 orders per 24 hours on a single account.

This is separate from the general HTTP 429 Too Many Requests error, which covers all API endpoints. With -1015, the issue is specifically how fast your bot is trying to place, cancel, or modify orders. Even if your request rate looks fine, hammering the order endpoints triggers this error fast — especially during volatile markets when your strategy tries to rebalance every few hundred milliseconds.

If you receive a 418 status code (not 429), your IP has been auto-banned. Bans start at 2 minutes and escalate to days for repeat violations. The only fix is to wait it out — there's no API call to lift a ban.

Understanding Binance's Rate Limit Architecture

Binance uses a weight-based system rather than a simple requests-per-minute counter. Every endpoint has a weight value, and your rolling window accumulates these weights. Heavy endpoints like depth snapshots can cost 50 weight, while a simple ping costs 1. The first thing any serious Binance bot should do at startup is call GET /api/v3/exchangeInfo to inspect the current limits programmatically — don't hardcode them, because Binance adjusts limits over time.

Common Binance API Rate Limits (Spot)
Limit TypeDefault ValueWindow
Request Weight60001 minute
Orders (RAW)10010 seconds
Orders (RAW)200,00024 hours
New Orders (-1015)101 second
IP Ban TriggerRepeated 429sRolling

Every API response from Binance includes headers that tell you exactly where you stand: X-MBX-USED-WEIGHT-1M shows your current weight usage for the rolling minute window. Reading these headers on every response is the only reliable way to stay ahead of a ban. Platforms like Bybit and OKX have similar systems but with different weight values and window sizes — if you're trading across multiple venues, never assume one exchange's limits apply to another.

Reading Rate Limit Headers in Python

The most important habit you can build is inspecting response headers on every call. Here's a minimal setup that logs your weight usage and backs off automatically:

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

API_KEY = 'your_api_key'
SECRET_KEY = 'your_secret_key'
BASE_URL = 'https://api.binance.com'

def signed_request(method, path, params=None):
    params = params or {}
    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

    headers = {'X-MBX-APIKEY': API_KEY}
    url = f'{BASE_URL}{path}'

    response = requests.request(method, url, headers=headers, params=params)

    # Always inspect rate limit headers
    used_weight = int(response.headers.get('X-MBX-USED-WEIGHT-1M', 0))
    print(f'[Rate] Used weight this minute: {used_weight}/6000')

    if used_weight > 5000:
        print('[Rate] Approaching limit — sleeping 10s')
        time.sleep(10)

    if response.status_code == 429:
        retry_after = int(response.headers.get('Retry-After', 60))
        print(f'[Rate] 429 received — waiting {retry_after}s')
        time.sleep(retry_after)
        return None

    if response.status_code == 418:
        print('[Rate] IP banned — do not retry, wait for ban to expire')
        raise Exception('IP banned by Binance')

    response.raise_for_status()
    return response.json()

# Example: fetch open orders
orders = signed_request('GET', '/api/v3/openOrders', {'symbol': 'BTCUSDT'})
print(orders)

Handling Error -1015 with Exponential Backoff

When your bot gets -1015, the worst thing it can do is immediately retry. That just triggers more errors and accelerates toward an IP ban. The correct pattern is exponential backoff with jitter — each failed attempt doubles the wait time, with a small random component to prevent thundering herd problems if you're running multiple bot instances.

import random
import time
import requests

def place_order_with_backoff(symbol, side, quantity, price, max_retries=5):
    """
    Place a limit order with exponential backoff on rate limit errors.
    """
    base_delay = 1.0  # seconds

    for attempt in range(max_retries):
        try:
            params = {
                'symbol': symbol,
                'side': side,          # 'BUY' or 'SELL'
                'type': 'LIMIT',
                'timeInForce': 'GTC',
                'quantity': quantity,
                'price': price,
            }
            result = signed_request('POST', '/api/v3/order', params)

            if result is None:
                # 429 already handled inside signed_request
                continue

            # Check for -1015 in the response body
            if isinstance(result, dict) and result.get('code') == -1015:
                wait = base_delay * (2 ** attempt) + random.uniform(0, 0.5)
                print(f'[Order] -1015 rate limit hit, retrying in {wait:.1f}s (attempt {attempt+1}/{max_retries})')
                time.sleep(wait)
                continue

            return result  # success

        except requests.exceptions.HTTPError as e:
            if e.response.status_code in (429, 418):
                wait = base_delay * (2 ** attempt) + random.uniform(0, 1)
                print(f'[Order] HTTP {e.response.status_code} — waiting {wait:.1f}s')
                time.sleep(wait)
            else:
                raise

    raise Exception(f'Failed to place order after {max_retries} retries')

# Usage
result = place_order_with_backoff('ETHUSDT', 'BUY', '0.1', '2500.00')
if result:
    print(f"Order placed: {result['orderId']}")
Never cancel and re-place orders in a tight loop to 'refresh' your position. Every cancel counts toward your order rate limit just like a new order. Use modify order (PATCH /api/v3/order) on Binance Spot API v3 instead — it counts as one operation.

Designing a Rate-Limit-Aware Bot Architecture

Reactive error handling (catching -1015 after it fires) is only half the solution. Professional algo traders build rate limiting into the architecture from the start. The key concepts are: a token bucket for order rate limiting, request queuing, and a shared weight tracker across all threads or async tasks.

import asyncio
import time
from collections import deque

class RateLimiter:
    """
    Simple token bucket for Binance order rate limits.
    Default: 10 orders per second, 100000 orders per 24h.
    """
    def __init__(self, orders_per_second=8, orders_per_day=90000):
        # Use 80% of actual limits as safety margin
        self.ops = orders_per_second
        self.opd = orders_per_day
        self._second_window = deque()
        self._day_window = deque()

    def _prune(self, window, max_age_seconds):
        now = time.monotonic()
        while window and now - window[0] > max_age_seconds:
            window.popleft()

    async def acquire(self):
        while True:
            self._prune(self._second_window, 1.0)
            self._prune(self._day_window, 86400.0)

            if len(self._second_window) < self.ops and len(self._day_window) < self.opd:
                now = time.monotonic()
                self._second_window.append(now)
                self._day_window.append(now)
                return  # cleared to send

            # Wait a bit and retry
            await asyncio.sleep(0.05)

# Usage in an async bot
rate_limiter = RateLimiter()

async def safe_place_order(symbol, side, qty, price):
    await rate_limiter.acquire()  # blocks until within limits
    return place_order_with_backoff(symbol, side, qty, price)

async def main():
    # Fire multiple orders without worrying about -1015
    tasks = [
        safe_place_order('BTCUSDT', 'BUY', '0.001', '65000'),
        safe_place_order('ETHUSDT', 'BUY', '0.1', '2500'),
        safe_place_order('SOLUSDT', 'BUY', '1', '150'),
    ]
    results = await asyncio.gather(*tasks)
    for r in results:
        print(r)

asyncio.run(main())

This architecture works equally well if you're trading on KuCoin or Gate.io alongside Binance — instantiate a separate RateLimiter per exchange with that exchange's specific limits. KuCoin, for instance, uses a different weight system for its private endpoints and has stricter per-second limits on order placement than Binance does.

Using WebSocket to Reduce REST API Pressure

A huge portion of rate limit problems come from polling: bots that call GET /api/v3/ticker/price every 500ms to track price, or spam GET /api/v3/openOrders to monitor fills. Every one of those REST calls burns weight. The fix is switching to WebSocket streams, which push data to you rather than requiring repeated polling.

Binance's WebSocket streams are free and don't count against your API weight budget. For real-time price data, order book updates, and your own account's order fills, WebSockets are strictly better than REST polling. Tools like VoiceOfChain use this approach to monitor signals across multiple pairs simultaneously — subscribing to live streams rather than hitting REST endpoints thousands of times per hour. If your bot is burning through rate limits on price-check calls, switching to WebSocket is usually a 10x improvement with minimal code changes.

For your account's order updates specifically — fills, cancels, new order confirmations — use the User Data Stream via a listen key. This eliminates polling GET /api/v3/openOrders entirely, which at weight 3 per call adds up fast on a busy bot. Coinbase Advanced Trade API uses a similar WebSocket-first model, and traders familiar with that API will find Binance's streams conceptually identical.

Frequently Asked Questions

What is the difference between Binance error -1015 and HTTP 429?
Error -1015 is a JSON-level error meaning you exceeded the order placement rate specifically. HTTP 429 is a transport-level error meaning you exceeded the general request weight limit. Both need backoff, but -1015 means slow your order submission specifically, while 429 means slow all API calls.
How long does a Binance IP ban last?
The first ban is usually 2 minutes. Repeated violations escalate: 5 minutes, then 30 minutes, then hours, and eventually days. Bans are automatic and Binance support generally won't lift them early. The Retry-After header in a 429 response tells you the minimum wait time.
Does using the Binance testnet avoid rate limits?
The testnet (testnet.binance.vision) has the same rate limit system as production. It's useful for testing your backoff logic without risking a production ban, but don't assume testnet results mean you can push higher rates on mainnet — the limits are similar.
Can I increase my Binance API rate limits?
Not directly. Binance does offer higher limits for market makers under specific programs, but retail traders can't request increases. The practical solution is to optimize your bot to use fewer API calls — switch polling to WebSockets, batch requests where possible, and use GET /api/v3/exchangeInfo to check current limits programmatically.
Do cancels count toward the -1015 order rate limit?
Yes. Order cancellations count against the same RAW order rate limit as new orders. If your strategy frequently cancels and replaces orders, you can hit -1015 without ever placing a net new position. Use the modify order endpoint (PATCH) instead of cancel-and-replace to cut your order count in half.
Should I use a VPN to reset my IP and avoid bans?
No — this will make things worse. If you're getting banned due to rate limit abuse, switching IPs doesn't fix the underlying problem, and Binance can ban your API key independent of IP. Fix the rate limiting in your code first. Using a VPN just delays the next ban by minutes.

Conclusion

Binance API error -1015 is a design constraint, not a failure mode. Every serious algo trader hits it eventually, and the ones who handle it gracefully do so by building rate awareness into their architecture from day one: read the response headers, use a token bucket, switch polling to WebSockets, and back off exponentially when you do hit limits. The code patterns above give you a working foundation — adapt the RateLimiter class to whichever exchange you're on, whether that's Binance, Bybit, or Gate.io, by plugging in that exchange's specific limits from their documentation. If you're monitoring multiple pairs for entry signals, tools like VoiceOfChain handle the market data layer so your bot can focus purely on execution — that alone cuts your API weight consumption significantly. Get the rate limiting right once, and you'll never think about it again.

◈   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