Integración API

¿Banearán mi IP de la API de Binance? Estrategias de límite de frecuencia y pesos explicadas

Guía completa sobre el mecanismo de pesos y límites de frecuencia de la API de Binance: asignación de 6000 pesos por minuto, lista de pesos por endpoint, lectura de encabezados de respuesta, condiciones de activación de errores 429/418, implementación de cubeta de tokens y alternativas con WebSocket. Incluye código en Python.

El mecanismo central del límite de frecuencia de la API de Binance es la Cubeta de Pesos (Weight Bucket): cada IP tiene un máximo de 6000 pesos por minuto (en Spot). Cada endpoint descuenta entre 1 y 100 puntos de peso según su complejidad; si excedes el límite, recibirás un error 429 Too Many Requests. Las infracciones repetidas elevarán el error a 418, lo que resulta en un baneo de IP que puede durar desde 2 minutos hasta 3 días. Este artículo explica en cuatro pasos (cálculo de pesos, monitoreo de encabezados, control del cliente y alternativas WebSocket) cómo mantener tu estrategia estable sin activar el control de riesgos. Si aún no tienes una llave API, completa el KYC en el sitio oficial de Binance; si no tienes cuenta, puedes realizar un registro gratuito.

I. Las tres dimensiones del límite de frecuencia

Binance aplica tres tipos de restricciones independientes para una misma IP o llave API:

Dimensión Límite Spot Límite Futuros Respuesta por infracción
Peso de la solicitud (REQUEST_WEIGHT) 6000 / minuto 2400 / minuto 429
Número de órdenes (ORDERS) 100 / 10 seg; 200,000 / día 300 / 10 seg; 1200 / minuto 429
Número de conexiones (RAW_REQUESTS) 61,000 / 5 minutos 61,000 / 5 minutos 429
Baneo de IP Infracciones continuas Infracciones continuas 418

Concepto clave: Aunque el peso de un endpoint de orden sea solo 1, también consume la cubeta de ORDERS; si cualquiera de las dos cubetas alcanza su límite, serás restringido.

II. Consultar la cuota de peso actual

Puedes obtener la cuota en tiempo real de tu cuenta a través del campo rateLimits en GET /api/v3/exchangeInfo:

curl -s "https://api.binance.com/api/v3/exchangeInfo" | \
  jq '.rateLimits'

Respuesta:

[
  {"rateLimitType": "REQUEST_WEIGHT", "interval": "MINUTE", "intervalNum": 1, "limit": 6000},
  {"rateLimitType": "ORDERS", "interval": "SECOND", "intervalNum": 10, "limit": 100},
  {"rateLimitType": "ORDERS", "interval": "DAY", "intervalNum": 1, "limit": 200000},
  {"rateLimitType": "RAW_REQUESTS", "interval": "MINUTE", "intervalNum": 5, "limit": 61000}
]

Las cuentas de nivel VIP pueden solicitar un aumento del límite de peso, pero el estándar de 6000 es suficiente para el 90% de las estrategias.

III. Tabla comparativa de pesos de los endpoints comunes

Endpoint Peso Descripción
GET /api/v3/ping 1 Prueba de conectividad
GET /api/v3/time 1 Hora del servidor
GET /api/v3/exchangeInfo 20 Reglas de trading (cachear 1 hora es suficiente)
GET /api/v3/ticker/price (un solo símbolo) 1 Precio de un solo par
GET /api/v3/ticker/price (todos) 4 Obtener todos los precios a la vez
GET /api/v3/ticker/24hr (un solo símbolo) 1 Estadísticas 24h de un solo par
GET /api/v3/ticker/24hr (todos) 80 Estadísticas de todos los pares
GET /api/v3/depth limit=5/10/20/50/100 1 Libro de órdenes
GET /api/v3/depth limit=500 5 Libro de órdenes
GET /api/v3/depth limit=1000 10 Libro de órdenes
GET /api/v3/depth limit=5000 50 Libro de órdenes (usar con precaución)
GET /api/v3/klines 2 Velas (K-lines)
GET /api/v3/historicalTrades 5 Historial de operaciones
GET /api/v3/account 20 Saldo de la cuenta
GET /api/v3/openOrders (un solo símbolo) 6 Órdenes abiertas actuales
GET /api/v3/openOrders (todos) 80 Todas las órdenes abiertas
GET /api/v3/allOrders 20 Historial de órdenes
POST /api/v3/order 1 Colocar orden
DELETE /api/v3/order 1 Cancelar orden
DELETE /api/v3/openOrders 1 Cancelar todas las órdenes

Trampa de rendimiento: No llames en bucle a ticker/24hr para cada símbolo individual; usa el endpoint masivo sin parámetros para reducir el peso de 300 símbolos × 1 = 300 a solo 80.

IV. Leer encabezados de respuesta para un límite de frecuencia adaptativo

Cada respuesta de una petición REST devuelve el peso utilizado actualmente. El cliente debe leerlo y ajustarse dinámicamente:

import requests, time

BASE_URL = "https://api.binance.com"

class RateLimiter:
    def __init__(self, max_weight=6000, safety_ratio=0.8):
        self.max_weight = max_weight
        self.safety = safety_ratio  # Solo usar el 80% para prevenir errores de arranque en frío
        self.used_weight = 0

    def update_from_headers(self, headers: dict):
        used = headers.get("X-MBX-USED-WEIGHT-1m")
        if used:
            self.used_weight = int(used)

    def should_wait(self) -> float:
        """Devuelve los segundos sugeridos de espera; 0 significa que se puede llamar directamente"""
        threshold = self.max_weight * self.safety
        if self.used_weight >= threshold:
            # Estimación de segundos hasta el reinicio del próximo minuto
            return 60 - (int(time.time()) % 60)
        return 0

limiter = RateLimiter()

def safe_get(path, params=None):
    wait = limiter.should_wait()
    if wait > 0:
        print(f"[Límite] {limiter.used_weight} de peso usado, pausando {wait}s")
        time.sleep(wait)
    r = requests.get(f"{BASE_URL}{path}", params=params, timeout=10)
    limiter.update_from_headers(r.headers)
    return r.json()

# Uso
data = safe_get("/api/v3/ticker/24hr")
print(f"Ocupación de peso actual: {limiter.used_weight}/6000")

V. Límite de frecuencia con cubeta de tokens (Control activo del cliente)

Una práctica más segura que mirar los encabezados de forma pasiva es usar una Cubeta de Tokens (Token Bucket) en el cliente:

import time, threading

class TokenBucket:
    def __init__(self, capacity=6000, refill_per_sec=100):
        self.capacity = capacity
        self.tokens = capacity
        self.refill = refill_per_sec  # 6000/60 = 100 por segundo
        self.last = time.time()
        self.lock = threading.Lock()

    def acquire(self, cost=1):
        with self.lock:
            now = time.time()
            elapsed = now - self.last
            self.tokens = min(self.capacity, self.tokens + elapsed * self.refill)
            self.last = now
            if self.tokens < cost:
                wait = (cost - self.tokens) / self.refill
                time.sleep(wait)
                self.tokens = 0
            else:
                self.tokens -= cost

bucket = TokenBucket(capacity=6000, refill_per_sec=100)

def call(path, weight):
    bucket.acquire(weight)
    return requests.get(f"{BASE_URL}{path}").json()

# Uso combinado al colocar una orden (peso 1) y consultar saldo (peso 20)
call("/api/v3/ticker/price", 1)
call("/api/v3/account", 20)

VI. Manejo correcto de los errores 429 y 418

1. Recibir un 429

def request_with_retry(method, url, **kwargs):
    for attempt in range(3):
        r = requests.request(method, url, **kwargs)
        if r.status_code == 429:
            retry_after = int(r.headers.get("Retry-After", 60))
            print(f"Límite activado, durmiendo {retry_after}s")
            time.sleep(retry_after)
            continue
        if r.status_code == 418:
            print("¡IP baneada, la aplicación debe detenerse!")
            raise SystemExit(1)
        return r
    raise Exception("Se superó el número máximo de reintentos")

El encabezado Retry-After indica los segundos exactos de espera; simplemente aplica un sleep.

2. Recibir un 418

El error 418 es una advertencia grave: Seguir enviando peticiones después de un 429 resultará en un baneo de IP que comienza en 2 minutos y puede llegar a 3 días en casos extremos. Si recibes este error, debes detener todas las peticiones inmediatamente y esperar al menos el tiempo indicado en Retry-After antes de reanudar.

VII. Alternativa con WebSocket: consumo de peso casi nulo

Mientras que REST consume peso con cada extracción de datos, WebSocket consume peso solo una vez al establecer la conexión; el envío de datos en tiempo real no consume peso:

import json, websocket

def on_message(ws, message):
    data = json.loads(message)
    print(f"{data['s']} Precio: {data['c']}, Vol 24h: {data['v']}")

ws = websocket.WebSocketApp(
    "wss://stream.binance.com:9443/stream?streams=btcusdt@ticker/ethusdt@ticker",
    on_message=on_message
)
ws.run_forever()

Comparativa de costos: Consultar el ticker de 10 pares cada segundo vía REST = 600 veces/minuto × peso 1 = 600 pesos; suscripción vía WebSocket = 0 pesos.

VIII. Sugerencias prácticas para la optimización de pesos

  1. Cachear exchangeInfo: Actualizarlo una vez por hora es suficiente; hacerlo más seguido solo desperdicia 20 de peso cada vez.
  2. Priorizar consultas masivas: Obtener todos los datos con /ticker/24hr sin parámetros ahorra un 90% de peso comparado con consultar cada símbolo individualmente.
  3. Limitar la profundidad del libro: Para órdenes limitadas, un limit=20 es suficiente (peso 1); evita solicitar 5000 niveles.
  4. Usar userDataStream para el estado de órdenes: Es más eficiente y consume cero peso comparado con hacer GET /order periódicamente.
  5. Cancelar todas las órdenes masivamente: El endpoint DELETE /openOrders?symbol=BTCUSDT tiene un peso de 1, siendo más ahorrativo que cancelar una por una.
  6. Límite de frecuencia por tramos horarios: Las 00:00, 08:00 y 16:00 UTC son ventanas de liquidación donde el consumo de peso es más tenso; ajusta tu estrategia para evitar esos picos.

IX. Preguntas frecuentes FAQ

P1: ¿El peso se calcula por IP o por llave API?

R: Principalmente por IP. Varias llaves bajo una misma IP comparten la cubeta de pesos; cambiar de IP puede evadir el límite de IP, pero las restricciones de órdenes se siguen aplicando por cuenta. No se recomienda usar proxies públicos para cambiar de IP, ya que Binance puede detectarlo.

P2: ¿Qué diferencia hay entre X-MBX-USED-WEIGHT y X-MBX-USED-WEIGHT-1m?

R: X-MBX-USED-WEIGHT-1m es el recomendado oficialmente e indica la ventana de 1 minuto; X-MBX-USED-WEIGHT es un campo heredado y su valor es idéntico. Guíate por el que tiene el sufijo -1m.

P3: Me han dado un 418, ¿cuánto debo esperar?

R: 2 minutos por la primera infracción, pudiendo escalar a 5/15/60 minutos y hasta 3 días en casos extremos. El encabezado Retry-After da el tiempo exacto. Optimiza tu código inmediatamente tras la recuperación para evitar infracciones repetidas y bloqueos acumulativos.

P4: ¿Cómo unificar el cálculo de pesos en llamadas concurrentes multihilo?

R: Usa una cubeta de tokens global a nivel de proceso (ver sección V) o una cubeta distribuida en Redis (necesario para despliegues en múltiples máquinas). Un threading.Lock es suficiente para una sola máquina.

P5: ¿Son iguales las reglas de peso en la Testnet (testnet.binance.vision)?

R: Las reglas de peso en la Testnet son más relajadas (usualmente 10 veces superiores a la Mainnet), pero la estructura de las reglas es idéntica. No evalúes el rendimiento de la Mainnet basándote en la Testnet; valida siempre con poco flujo en la Mainnet antes del despliegue total.

Después de revisar las estrategias de límite de frecuencia, vuelve a la navegación de categorías para entrar en «Integración API» y ver tutoriales sobre WebSocket y firmas.

Continuar explorando

¿Sigues con dudas sobre el uso de Binance? Vuelve a la página de categorías para encontrar otros tutoriales sobre el mismo tema.

Categorías

Tutoriales relacionados

¿Cómo solicitar la API de Binance? Guía para generar claves y firmas 2026-04-14 ¿Cómo usar la API Spot de Binance? Código listo para ejecutar desde cero hasta tu primera orden 2026-04-14 ¿Cuál es la diferencia entre la API de Binance Futures y Spot? Comparativa de endpoints, parámetros y pesos 2026-04-14 ¿Cómo suscribirse a cotizaciones por WebSocket en Binance? Código de nivel de producción para flujos individuales y combinados 2026-04-14