Referencia

Códigos de error

Un error siempre dice qué estaba mal y, cuando es posible, qué debería haberse escrito en su lugar. Esta página enumera los casos, canal por canal, y separa lo que merece un reintento de lo que no.

Un agente de IA conectado por MCP recibe estos mismos códigos: una llamada de herramienta fallida empieza por Error (HTTP NNN) y termina con el código estable, para que el agente de IA pueda corregir su propia llamada. Ver el servidor MCP más abajo.

#La forma de un error

Todo error tiene una sola forma, sea cual sea la capa que rechaza (pasarela, servicio o validación): status: "error", server_time y un objeto error con un code estable, un message en inglés y, cuando un parámetro es el culpable, param. error.details, un objeto presente cuando el error lleva datos estructurados, da lo mismo para el código: suggestion, accepted, la ventana de tu plan en un history_depth, retry_after_s en los rechazos temporales. Nada va junto a error.

{
  "status": "error",
  "server_time": "2026-09-30T16:07:27.808Z",
  "error": {
    "code": "invalid_symbol",
    "message": "Unknown symbol 'BTCUSD'. Did you mean 'BTCUSDT'? Accepted values: ADAUSDT, AVAXUSDT, BNBUSDT, BTCUSDT, DOGEUSDT, ETHUSDT, GRAMUSDT, HBARUSDT, HYPEUSDT, LINKUSDT, LTCUSDT, NEARUSDT, SOLUSDT, SUIUSDT, TAOUSDT, TRXUSDT, UNIUSDT, USDCUSDT, USDTUSDC, XLMUSDT, XMRUSDT, XRPUSDT.",
    "param": "symbol",
    "details": {
      "suggestion": "BTCUSDT",
      "accepted": [
        "ADAUSDT",
        "AVAXUSDT",
        "BNBUSDT",
        "BTCUSDT",
        "DOGEUSDT",
        "ETHUSDT",
        "GRAMUSDT",
        "HBARUSDT",
        "HYPEUSDT",
        "LINKUSDT",
        "LTCUSDT",
        "NEARUSDT",
        "SOLUSDT",
        "SUIUSDT",
        "TAOUSDT",
        "TRXUSDT",
        "UNIUSDT",
        "USDCUSDT",
        "USDTUSDC",
        "XLMUSDT",
        "XMRUSDT",
        "XRPUSDT"
      ]
    }
  }
}

Los rechazos ligados al plan tienen la misma forma, más error.details.retry_after_s en los rechazos temporales (también en la cabecera Retry-After). La cabecera X-Deny-Reason nombra el motivo: rate_limited, quota_exceeded, plan_family, plan_websocket, missing_api_key, invalid_api_key o history_depth.

{
  "status": "error",
  "server_time": "2026-09-30T16:07:27.808Z",
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded: your plan allows a limited number of requests per minute. Retry after the Retry-After delay.",
    "details": {
      "retry_after_s": 2
    }
  }
}

El 403 de profundidad lleva X-Deny-Reason: history_depth y error.code: history_depth, como los demás rechazos ligados al plan; su mensaje indica el limit máximo utilizable en esa temporalidad. Las ventanas por plan están en la página Límites de uso.

Los códigos de error

Decide según error.code, nunca según el texto del mensaje: el código es un contrato, el mensaje puede reformularse. Añadir un código no es un cambio incompatible; renombrarlo o eliminarlo, sí.

error.codeCausa
depth_too_largeLa profundidad de un campo del snapshot supera su máximo documentado.
edge_rate_limitedDemasiadas peticiones desde esta dirección IP en el borde (anti-flood: 20 por segundo y por dirección, ráfagas de 40). Reintenta tras 1 segundo.
history_depthEl historial solicitado se remonta más atrás de lo que permite tu plan.
https_requiredPlain HTTP is refused: call the same URL over https://. A key sent over http:// travelled unencrypted: rotate it.
insufficient_dataNo hay historial suficiente para calcular el indicador solicitado.
internal_errorError inesperado del servidor. Un reintento con espera creciente puede funcionar; si persiste, notifícalo con la cabecera X-Request-Id de la respuesta.
invalid_api_keyLa clave de API falta, no es válida o está revocada.
invalid_assetEsta familia no sirve el activo. El error enumera los valores aceptados.
invalid_channelWebSocket: el nombre del canal está mal formado o es desconocido; el error enumera las formas aceptadas.
invalid_depthLa profundidad de un campo del snapshot no es un entero positivo.
invalid_indicatorEl cuerpo de POST /v1/indicators no es válido (id, type o parameters).
invalid_messageWebSocket: el mensaje no es un objeto JSON legible.
invalid_parameterUn parámetro tiene un tipo o un valor no válido.
invalid_seriesLa serie macro no existe. Consulta el catálogo /series de la familia.
invalid_symbolEl símbolo no se sirve. El error enumera los valores aceptados (ver /v1/symbols).
invalid_time_rangesince_ms / until_ms no son milisegundos epoch válidos, o since_ms > until_ms (until_ms es inclusivo), o since_ms >= before_ms.
invalid_timeframeEsta ruta no acepta esa temporalidad.
method_not_allowedLa ruta existe, pero no con este método HTTP.
missing_api_keyNo API key was sent: put it in the X-API-KEY header (or api_key in the WebSocket URL).
missing_parameterFalta un parámetro obligatorio.
not_applicableWebSocket: el canal no tiene sentido para este activo (un canal de futuros de una stablecoin). La API REST responde a esa petición con 200 y unavailable: not_applicable.
not_authenticatedWebSocket: llegó un mensaje distinto de auth en una sesión aún no autenticada (raro: la clave se verifica en el handshake, donde una clave ausente responde 401 invalid_api_key).
not_foundRuta desconocida.
plan_familyTu plan no incluye esta familia de datos de mercado: el mensaje la nombra, y GET /v1/limits lista las familias de tu clave.
plan_websocketTu plan no incluye el flujo WebSocket.
quota_exceededLa cuota mensual de tu plan está agotada. Retry-After (y error.details.retry_after_s) dan los segundos hasta que se reinicie, el primer día del mes (UTC).
rate_limitedSe ha superado el límite por minuto de tu plan. Reintenta pasados Retry-After segundos.
repeated_parameterUn parámetro aparece más de una vez.
service_unavailableIndisponibilidad temporal. Reintenta pasados Retry-After segundos.
timeframe_not_servedLa serie existe, pero no sirve esta temporalidad (una serie diaria pedida en 1h). Una temporalidad que la ruta nunca sirve es invalid_timeframe.
unknown_fieldUn nombre de campo de ?fields= o del snapshot no existe.
unknown_parameterLa ruta no declara este parámetro.
upstream_timeoutLa petición ha tardado demasiado. Reduce la ventana o la profundidad y vuelve a intentarlo.
TypeScript
const response = await fetch(url, { headers: { "X-API-KEY": key } });
const body = await response.json();

if (body.status === "error") {
  switch (body.error.code) {
    case "rate_limited":
    case "service_unavailable":
      // Transient: wait Retry-After seconds, then retry.
      break;
    case "history_depth":
      // Ask for less history, or upgrade: error.message gives the max limit.
      break;
    case "invalid_symbol":
    case "unknown_parameter":
      // Fix the request: error.param names the culprit, error.message the fix.
      break;
    default:
      throw new Error(body.error.code + ": " + body.error.message);
  }
}

#Códigos HTTP

CódigoCausaSolución
401
Clave de API rechazada
La clave de API está ausente, es inválida o está revocada (error.code: invalid_api_key). La respuesta lleva WWW-Authenticate: ApiKey header="X-API-KEY".Comprueba que la cabecera X-API-KEY se envía de verdad: algunos clientes HTTP la pierden tras una redirección. Crea o copia una clave desde la consola.
403
Rechazada por el plan
La clave es válida pero el plan no cubre la petición; la cabecera X-Deny-Reason lleva el mismo código que error.code: plan_family (familia de datos de mercado fuera del plan), plan_websocket (flujo WebSocket fuera del plan) o history_depth (una profundidad de historial más allá de la ventana del plan).En plan_family y plan_websocket, el dato existe pero no está en tu plan. En history_depth, error.message indica el límite máximo que puedes usar.
404
Ruta desconocida
La ruta no existe (error.code: not_found). El mensaje propone la ruta más cercana cuando la hay.Revisa la ruta: el mensaje da la ruta más cercana cuando la hay; todas las rutas están en el contrato.
405
Método no permitido
La ruta existe, pero no con este método (error.code: method_not_allowed); la cabecera Allow lista los que acepta.Usa GET (POST solo para /v1/indicators); HEAD se acepta en toda ruta GET, y OPTIONS (el preflight CORS) en toda ruta.
422
Parámetro ausente, desconocido o no válido
Un símbolo, un activo o una serie desconocidos o desactivados, una profundidad por encima del límite del campo, un parámetro de indicador fuera de rango, symbol ausente, temporalidad fuera de la lista, campo multitemporalidad solicitado sin el sufijo @tf, campo desconocido en ?fields=, parámetro que la ruta no declara o parámetro repetido, since_ms o until_ms expresado en segundos o fuera de rango, cuerpo JSON no válido.Ante un campo o un parámetro desconocido, la respuesta sugiere el nombre más parecido. En una temporalidad, enumera los valores aceptados.
429
Velocidad o cuota superada
O más peticiones por minuto de las que permite tu plan, o una ráfaga demasiado densa (X-Deny-Reason: rate_limited, Retry-After: la espera hasta la próxima ficha); o la cuota mensual de la cuenta está agotada (X-Deny-Reason: quota_exceeded, Retry-After: los segundos hasta que se reinicie).En rate_limited, espera los segundos que indica Retry-After. En quota_exceeded, el contador se reinicia el primer día del mes (UTC); también puedes cambiar de plan desde la consola.
503
Indisponibilidad momentánea
Una dependencia de lectura se está recuperando. En el handshake WebSocket, el servicio de flujo ha alcanzado su capacidad, sumando todas las cuentas: no es el límite de conexiones de tu plan, que cierra con 4003.Reinténtalo tras el plazo que indica Retry-After o, si no está, con un plazo creciente. El error es transitorio por naturaleza.
500
Error inesperado
Un fallo del servicio, nunca causado por la petición.Reinténtalo; si la respuesta persiste, notifícala con la cabecera X-Request-Id de la respuesta.
504
Petición demasiado larga
La consulta tardó demasiado para la ventana o la profundidad pedida (error.code: upstream_timeout).Reduce limit o el intervalo de tiempo (since_ms / until_ms) y vuelve a intentarlo: repetir la misma petición tardaría lo mismo.

Las confusiones más frecuentes

SíntomaCausa real
403 aunque la clave sea válidaLee X-Deny-Reason (el error.code). plan_family: la familia de este endpoint no está en tu plan. plan_websocket: el flujo no está. history_depth: la petición va más allá de la ventana del plan, y error.details da el mayor limit permitido. invalid_api_key sin razón aparente: un cliente HTTP que pierde las cabeceras personalizadas tras una redirección; llama a la URL final.
429 aunque el ritmo sea bajoLo que se ha agotado es la cuota mensual de la cuenta (X-Deny-Reason: quota_exceeded, Retry-After: segundos hasta el reinicio), no el límite de velocidad. Se reinicia el primer día del mes, en UTC, o en cuanto cambia el plan.
422 sin mensaje claro en un snapshotUn campo multitemporalidad pedido sin su sufijo @tf, o lo contrario en un campo de forma única.
422 en una profundidad que parece razonableCada campo del snapshot tiene su propio límite, en su propia unidad. El CVD llega hasta 200 ventanas, los diferenciales entre mercados hasta 50.
422 en una temporalidad válida en otro sitioLos instrumentos macro intradía rechazan 30m y la semana solo existe en las velas y los indicadores.
Una respuesta vacía que no es un errorUna stablecoin en una métrica de futuros: la clave unavailable dice cuál de los tres motivos se aplica. (Las rutas de opciones y ETF solo sirven BTC y ETH: otro activo es un 422 invalid_asset que los enumera.)
422 en un parámetro de apariencia inofensivaCada ruta rechaza un parámetro que no declara y un parámetro pasado dos veces; el snapshot hace lo mismo con una clave desconocida. La respuesta sugiere el nombre más parecido: un parámetro mal escrito nunca se ignora en silencio.
422 en un since_ms que parece correctosince_ms y until_ms están en milisegundos. Una marca de tiempo en segundos, microsegundos o nanosegundos se rechaza antes de ejecutar nada, igual que un límite negativo, invertido o anterior al 2009-01-03 — salvo since_ms=0, que significa el inicio de los datos.

#Indicadores

La validación sigue un orden fijo (timeframe, limit, símbolo, lectura de los indicadores, regla del límite, lectura de las velas), y gana el primer fallo. Un cuerpo corregido puede revelar un segundo error.

CasoCódigo
Temporalidad no válida, limit fuera de 1 a 1000, símbolo desconocido422
Lista indicators vacía, o una entrada que no es un objeto422
Falta la clave id o type, id no válido, campo del cuerpo desconocido422
Tipo no admitido422
Parámetro desconocido para este tipo (obv con un periodo, por ejemplo)422
Parámetro del tipo equivocado, o inferior a su mínimo422
macd o adosc con fast mayor o igual que slow422
sar con acceleration mayor que maximum422
Identificador duplicado422
Cuerpo mal formado, más de 50 instancias, campo demasiado largo422
limit más calentamiento supera 1000422
Datos insuficientes para esta temporalidad, o historial de volumen demasiado corto para un indicador de volumen422

#Flujo WebSocket

Toda petición imposible recibe un mensaje op: error que dice lo que se esperaba: la lista de símbolos válidos, la de los canales, los límites de depth. Tres situaciones no producen un error sino un cierre:

CódigoMotivo
4003 · límiteSe alcanzó el límite de conexiones simultáneas del plan. El motivo indica el límite. Una desconexión libera su plaza al instante.
4005 · cliente demasiado lentoLa cola de envío se ha desbordado diez veces. Consume más rápido o reduce el número de canales. El motivo distingue ambos casos.
4001No se envía en la práctica: la clave se comprueba en el handshake (401), y una trama auth en una sesión abierta responde authenticated.
4002Sesión nunca autenticada en cinco segundos. Un caso residual, ya que la clave se comprueba en el handshake.

La clave se comprueba en el handshake, antes de abrir: una clave ausente recibe un 401 missing_api_key, una desconocida o revocada un 401 invalid_api_key (con WWW-Authenticate), un plan sin el flujo (Free) un 403 plan_websocket — cada vez el valor de X-Deny-Reason. Un 429 indica un ritmo de apertura demasiado alto, y un 503 un servicio de flujo que ha alcanzado su capacidad, para todas las cuentas: reintenta un poco más tarde. Una vez conectado, un canal cuya familia no está en el plan recibe op: error con error.code: plan_family y se rechaza toda la suscripción. Ver Límites de uso.

#Servidor MCP

Los errores siguen un único patrón, Error (HTTP NNN): message [code: …], y una llamada de herramienta fallida se marca como error MCP (isError), con el mismo error en structuredContent.error: status, code, message, y param, hint, retry_after_s cuando aplican. Cada llamada de herramienta se evalúa como una petición REST con la clave del cliente: un 403 por una familia fuera del plan o un 429 por cuota se devuelven tal cual, con su mensaje y su error.code. Dos familias le son propias:

  • Petición no válida: un campo desconocido, un objetivo ausente (symbol, asset, series) o que ningún campo pedido espera, un parámetro que la ruta elegida no acepta, un timeframe que ningún campo usa, una ventana fechada sobre un campo que no la admite. Todo se enumera en un solo mensaje, antes de cualquier llamada de red.
  • Respuesta parcial: en las cinco herramientas de datos MCP (get_pair_data, get_options_data…), un campo con error no bloquea los demás. Se indica en errors y la respuesta lleva partial: true; la llamada solo falla si ningún campo tiene éxito.

Solo hay reintento si el origen no respondió (502, 503, 504 o fallo de red): un 500 no se repite, porque cada petición cuenta en la cuota del cliente. Un tiempo de espera agotado devuelve Error (HTTP 504) tras treinta segundos.

#Estrategia de reintentos

No todo se puede repetir. Repetir una petición errónea produce exactamente la misma respuesta y consume cuota para nada.

Python
import httpx  # pip install httpx


def should_retry(response: httpx.Response) -> bool:
    """What is transient, and what never will be (the official SDKs do the same)."""
    # A spent monthly quota only resets on the 1st of the month: fail fast.
    if response.status_code == 429 and response.headers.get("X-Deny-Reason") == "quota_exceeded":
        return False
    # 429 (rate) and 503: the server says when, in Retry-After. 502 and 504:
    # a gateway hiccup or a slow query. Retry with an increasing delay.
    if response.status_code in (429, 502, 503, 504):
        return True
    # 500: a fault on the service side, retry once.
    if response.status_code == 500:
        return True
    # 401, 403, 404, 405, 422: the request is at fault. Replaying it as is
    # produces exactly the same response.
    return False
  • Respeta Retry-After en lugar de inventar un plazo: el servidor sabe cuándo estará listo.
  • Limita el número de intentos. Tres o cuatro bastan: más allá, el problema no es pasajero.
  • No repitas un 4xx, salvo el 429. Corrige la petición.
  • En el flujo, reconéctate con un retraso creciente, y lee el código de cierre: 4003, se alcanzó el límite del plan, otra conexión de la cuenta debe cerrarse primero; 4005 (client too slow), el cliente está sobrecargado, y reconectar sin cambiar el consumo reproducirá el corte. Un 503 en el handshake solo pide esperar.