Referencia de Códigos de Error
Catálogo completo de códigos de error de la API C2C de Binance con 8 categorías de clasificación, patrones de recuperación y soluciones probadas en producción con más de 500K llamadas a la API.
Prioridad de Clasificación
Decodificador del Error -9000
El error -9000 es un código envoltorio. El error real está embebido en el string del mensaje:
- El mensaje contiene
"187049"/"187040"/"187031"→ RETRY_WITH_VOLUME_SYNC - El mensaje contiene
"rango limitado"/"limited range"→ PRICE_RANGE_LIMIT - En cualquier otro caso → RETRIABLE_WITH_BACKOFF
Errores a Nivel de Transporte
Estos son códigos de estado HTTP manejados en la capa de transporte, antes de que classify_binance_error() se ejecute.
| Código | Nombre | Acción |
|---|---|---|
| 418 | I'm a teapot (IP Ban) | Transport-level HTTP status. Handled before app-level error classification. Indicates temporary IP ban — requires significant cooldown (hours). |
| 429 | Rate Limit Exceeded | Can appear as both HTTP status code and Binance app code. Check Retry-After header. Triggers circuit breaker recording. |
Categorías de Error
Time Sync Errors
high-1021-1022Re-synchronize server time via GET /api/v3/time, wait 0.3s, then retry the request with a fresh timestamp.
Note: -1000 was previously classified here but is actually RETRIABLE_WITH_BACKOFF. Some methods enable per-method sync via BINANCE_RETRY_SYNC_ON_1000.
Ver código de recuperación
async def handle_time_sync_error(client, original_request):
"""Recovery: sync time + retry with fresh timestamp."""
await client.sync_time() # GET /api/v3/time
await asyncio.sleep(0.3)
# Rebuild request with fresh timestamp — do NOT reuse stale one
return await original_request()Retriable (Backoff)
high-1000429-9000Exponential backoff with jitter. For 429: read Retry-After header. Circuit breaker opens after threshold failures.
-1000 has a per-method override: when BINANCE_RETRY_SYNC_ON_1000=true, the method also syncs time before retrying. Error -9000 is a wrapper — check the message string for wrapped 187xxx codes before classifying as backoff.
Ver código de recuperación
def calculate_backoff(attempt: int, base_ms=250, cap_ms=30000) -> float:
"""Exponential backoff with full jitter."""
max_backoff = min(cap_ms, base_ms * (2 ** attempt))
return random.uniform(0, max_backoff) / 1000.0
# attempt=1: 0-250ms, attempt=2: 0-500ms, ..., capped at 30s
# For 429: also check resp.headers.get("Retry-After")Volume Sync Errors
critical187031187049187040Sleep (configurable via ERROR_187XXX_RETRY_SLEEP_SEC, default 0.3s), re-fetch ad state, retry. Prevention: use price-only payload.
Error 187031 can also arrive wrapped inside -9000. The classifier checks the -9000 message string for '187031', '187049', or '187040' and reclassifies accordingly.
Ver código de recuperación
async def handle_volume_sync(client, adv_no: str, price: str):
"""
CRITICAL: Use price-only updates to PREVENT this error entirely.
CORRECT: {"advNo": adv_no, "price": price}
WRONG: {"advNo": adv_no, "price": price, "surplusAmount": amount}
"""
sleep_sec = float(os.getenv("ERROR_187XXX_RETRY_SLEEP_SEC", "0.3"))
await asyncio.sleep(sleep_sec)
# Re-fetch current ad state
ad = await client.get_ad_detail(adv_no)
# Retry with price-only payload
return await client.update_ad(adv_no, price=price)Business Errors (Non-Retriable)
medium-2019-2015-3026-1013-1111300-second cooldown block. Log the error. Do not retry — these require manual intervention.
Ver código de recuperación
NON_RETRIABLE_PAUSE_SEC = 300 # 5 minutes
if classify_error(error) == "NON_RETRIABLE_BUSINESS":
_non_retriable_block_until = time.time() + NON_RETRIABLE_PAUSE_SEC
_non_retriable_last_code = error.code
log_event("business_error", code=error.code, message=error.message)
# Do NOT retry — requires manual fix (insufficient funds, invalid key, etc.)Price Overlap
high187055Find a valid price outside the exclusion zone (current price ± 4 ticks). Max 5 attempts in 60 seconds, then 30-second cooldown.
Ver código de recuperación
async def handle_overlap(target_price, my_prices, tick):
"""
Binance rejects prices within ±4 ticks of your own ads.
Example: ad at 41.37, tick=0.01 → zone [41.33, 41.41]
"""
# Pre-avoid: check before first attempt
if is_in_exclusion_zone(target_price, my_prices, tick):
target_price = find_valid_price_outside_exclusion(
target_price, my_prices, tick
)
# Rate limit: max 5 overlap retries in 60s
if overlap_count > 5:
await asyncio.sleep(30)
returnAd/Merchant Offline
medium8322983230Mark ad as paused, record reason and timestamp. Check every 15 seconds if ad came back online. Manual recovery via your application's admin interface.
Ver código de recuperación
if error.code in (83229, 83230):
ad_paused = True
ad_paused_reason = (
"Business closed" if error.code == 83229
else "Taking a break"
)
# Check every 15s if merchant came back online
# Manual recovery: clear the pause state via your admin interfaceDynamic Price Range
highParse the allowed range from the error message, clamp target price to the effective range (intersection of user limits and Binance range), retry.
Detected by message content, not by error code. Binance may wrap this in -9000 or other codes. Keywords: 'rango limitado', 'limited range', 'price range'.
Ver código de recuperación
async def handle_price_range(error_message, target, user_min, user_max):
"""
Binance imposes ~±10% of reference price. Detection:
1. Parse from message: "rango limitado de: 34.77-42.49"
2. Fallback: estimate from USD rate × ±9.5%
"""
parsed = parse_binance_price_range(error_message)
if parsed:
low, high = parsed
else:
usd_rate = await get_usd_rate(fiat)
low, high = usd_rate * 0.905, usd_rate * 1.095
# Effective range = intersection of user and Binance ranges
effective_low = max(user_min, low)
effective_high = min(user_max, high)
chosen = max(effective_low, min(target, effective_high))
# Cache for pre-validation (TTL: ENGINE_BINANCE_RANGE_TTL_SEC)
return chosenUnknown Errors
lowConservative exponential backoff. Log with full context for investigation.
Ver código de recuperación
if classify_error(error) == "UNKNOWN":
log_event("unknown_binance_error",
code=error.code,
message=error.message,
endpoint=endpoint,
)
backoff = calculate_backoff(attempt)
await asyncio.sleep(backoff)Fórmula de Backoff
sleep = random(0, min(cap_ms, base_ms × 2^attempt))base_ms = 250, cap_ms = 30,000
Manejo de HTTP 404
HTTP 404 responses are handled locally per endpoint, NOT as a global error classification. For EP-2 (getDetailByNo): 404 triggers a negative cache (TTL=60s, max 1024 entries). A sliding window tracker (threshold=2, window=600s) can mark an ad as "permanently dead" after 3600s of persistent 404 responses.
Error 187049: La Solución de Solo Precio
El error más frecuente en el desarrollo con la API C2C de Binance. Ocurre cuando surplusAmount se envía junto con price en una actualización de anuncio. Binance valida el surplus contra un valor en caché que puede haber cambiado debido a operaciones activas.
Solución: Envía SOLO advNo + price al actualizar el precio.
Ver EP-7: Update Ad para todos los detalles.
Todos los Códigos de Error
| Código | Nombre | Categoría | Env Var |
|---|---|---|---|
| -1021 | Timestamp out of recvWindow | RETRY_WITH_SYNC | — |
| -1022 | Invalid signature | RETRY_WITH_SYNC | — |
| -1000 | Unknown error | RETRIABLE_WITH_BACKOFF | — |
| -9000 | System error (wrapper) | RETRIABLE_WITH_BACKOFF | — |
| -1102 | Mandatory parameter missing | NON_RETRIABLE_BUSINESS | — |
| -1104 | Unknown parameter | NON_RETRIABLE_BUSINESS | — |
| 187049 | Ad status error | RETRY_WITH_VOLUME_SYNC | — |
| 187040 | Ad status error (variant) | RETRY_WITH_VOLUME_SYNC | — |
| 187031 | Ad update failed | RETRY_WITH_VOLUME_SYNC | — |
| 187055 | Price overlap | SPECIAL_HANDLER | — |
| -2019 | Margin insufficient | NON_RETRIABLE_BUSINESS | — |
| -2015 | Invalid API key/permissions | NON_RETRIABLE_BUSINESS | — |
| -3026 | Order already completed | NON_RETRIABLE_BUSINESS | — |
| -1013 | Invalid quantity | NON_RETRIABLE_BUSINESS | — |
| -1111 | Precision over maximum | NON_RETRIABLE_BUSINESS | — |
| 83229 | Business closed | AD_OFFLINE | — |
| 83230 | Taking a break | AD_OFFLINE | — |