Primeros pasos

Autenticación

Una sola clave abre los tres canales. Viaja de forma distinta en cada uno (una cabecera en REST y en MCP, un parámetro de URL en el handshake de WebSocket), pero su valor es el mismo en todas partes, y en cada uno se aplican los permisos de tu plan.

#API REST

La clave va en la cabecera X-API-KEY. Ni parámetro de URL ni Authorization: Bearer: una clave en una query string acabaría en los registros de acceso de cada intermediario del camino.

curl
curl -H "X-API-KEY: $BYTNODE_KEY" \
  "https://api.bytnode.com/v1/basis?symbol=ETHUSDT"

La comparación se ejecuta en tiempo constante, así que una clave errónea tarda exactamente lo mismo en rechazarse que una casi correcta.

#La ruta sin clave

Solo una ruta responde sin autenticación: /v1/health, que indica si el servicio está en marcha. Las dos rutas que se llaman justo después (la frescura de los datos y el catálogo de activos) exigen la clave, como todo lo demás. /v1/status es una sonda: nunca se descuenta de la cuota mensual, pero está sujeta al límite de velocidad del plan. /v1/symbols pertenece a la familia precio y volumen, abierta en todos los planes, y cuenta como cualquier otra llamada.

GET/v1/healthsin clave
GET/v1/status
GET/v1/symbols
curl
# The only route that answers without a key.
curl https://api.bytnode.com/v1/health

# The next two require the key. /v1/status is a probe: never counted
# against the monthly quota, but subject to the rate limit of the plan.
curl -H "X-API-KEY: $BYTNODE_KEY" https://api.bytnode.com/v1/status
curl -H "X-API-KEY: $BYTNODE_KEY" https://api.bytnode.com/v1/symbols

#Flujo WebSocket

La clave se presenta en la URL, como parámetro ?api_key=, y se comprueba en el handshake, antes de que se abra la conexión. Un navegador no puede poner cabeceras en un WebSocket: es la única forma que funciona en todas partes, y la que usan los ejemplos.

import asyncio
import json
import os

import websockets


async def main() -> None:
    # The key travels in the URL, at the handshake: the connection opens
    # already authenticated. A missing or invalid key gets 403 before it opens.
    url = f"wss://api.bytnode.com/ws?api_key={os.environ['BYTNODE_KEY']}"
    async with websockets.connect(url) as ws:
        await ws.send(json.dumps({
            "op": "subscribe",
            "channels": ["trades.futures.BTCUSDT"],
        }))
        print(await ws.recv())   # {"op": "subscribed", "channels": [...]}


asyncio.run(main())

La sesión se abre ya autenticada: el primer mensaje puede ser la suscripción. Una clave ausente, inválida o revocada, o un plan que no incluye el flujo, recibe un 403 en el handshake y no se establece nada. El límite de conexiones simultáneas del plan se aplica a toda la cuenta: la conexión que sobra se cierra con el código 4003. La antigua trama {"op": "auth", "api_key": "…"} se sigue aceptando y responde ok, pero es opcional.

#Servidor MCP

El servidor MCP espera la misma clave, en la misma cabecera X-API-KEY, puesta en la conexión HTTP. Sin ella no se expone ninguna herramienta: el servidor rechaza por defecto en lugar de abrir por defecto.

from fastmcp import Client

headers = {"X-API-KEY": "your_key"}

async with Client("https://mcp.bytnode.com", headers=headers) as client:
    info = await client.call_tool("get_system_info", {})

La forma exacta del archivo de configuración depende del agente; el principio no cambia: una URL y una cabecera. Ver Servidor MCP.

#Lo que dice un rechazo

CanalRespuestaCausa
REST403, X-Deny-Reason: keyCabecera ausente, clave desconocida o revocada. El cuerpo dice « Cle API absente, invalide ou revoquee ».
REST403, X-Deny-Reason: familyLa clave es válida, pero la familia de este endpoint no está en tu plan.
MCP401, header X-API-KEY requisCabecera ausente en la conexión, o clave rechazada. La comprobación ocurre antes de anunciar una sola herramienta; después, cada llamada de herramienta se evalúa según los permisos de la clave.
WebSocket403 en el handshakeClave ausente o inválida en la URL (key), o plan sin el flujo (websocket). La conexión no se abre.
WebSocketcierre 4003Se alcanzó el límite de conexiones simultáneas del plan, para toda la cuenta. El motivo indica el límite.

#Proteger y rotar la clave

Dónde guardarla

  • En una variable de entorno o un gestor de secretos, nunca en el repositorio de código.
  • Solo en el servidor. Una clave puesta en el JavaScript de una página es pública, sea cual sea la ofuscación.
  • Una clave por entorno: desarrollo, preproducción, producción. Así una filtración puede revocarse sin detener el resto.

Rotarla

  • Crea la nueva clave en la consola. Ambas siguen siendo válidas durante el solapamiento.
  • Despliégala en todas partes y comprueba que el tráfico de la antigua ha caído a cero.
  • Revoca la antigua. La revocación es inmediata.