Premiers pas

Authentification

Une seule clé ouvre les trois canaux. Elle voyage différemment sur chacun (un en-tête en REST et en MCP, un paramètre d’URL à la poignée de main en WebSocket), mais sa valeur est la même partout, et ce sont les droits de votre offre qui s’appliquent sur chacun.

#API REST

La clé va dans l’en-tête X-API-KEY. Pas de paramètre d’URL, pas de Authorization: Bearer : une clé dans une chaîne de requête finirait dans les journaux d’accès de chaque intermédiaire sur le chemin.

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

La comparaison s’exécute en temps constant : une clé fausse met exactement autant de temps à être rejetée qu’une clé presque juste.

#La route sans clé

Une seule route répond sans authentification : /v1/health, qui dit si le service est en marche. Les deux routes que l’on appelle juste après (la fraîcheur des données et le catalogue des actifs) exigent la clé, comme tout le reste. /v1/status est une sonde : jamais décomptée du quota mensuel, mais soumise à la limite de débit de l’offre. /v1/symbols appartient à la famille prix et volume, ouverte sur toutes les offres, et compte comme n’importe quel autre appel.

GET/v1/healthsans clé
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

#Flux WebSocket

La clé est présentée dans l’URL, comme paramètre ?api_key=, et elle est vérifiée à la poignée de main, avant que la connexion ne s’ouvre. Un navigateur ne peut pas poser d’en-tête sur un WebSocket : c’est la seule forme qui marche partout, et c’est celle qu’utilisent les exemples.

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 session s’ouvre déjà authentifiée : le premier message peut être l’abonnement. Une clé absente, invalide ou révoquée, ou une offre qui n’inclut pas le flux, reçoit un 403 à la poignée de main et rien n’est établi. Le plafond de connexions simultanées de l’offre s’applique à tout le compte : la connexion de trop est fermée avec le code 4003. L’ancienne trame {"op": "auth", "api_key": "…"} est toujours acceptée et répond ok, mais elle est facultative.

#Serveur MCP

Le serveur MCP attend la même clé, dans le même en-tête X-API-KEY, posé sur la connexion HTTP. Sans elle, aucun outil n’est exposé : le serveur refuse par défaut plutôt que d’ouvrir par défaut.

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 forme exacte du fichier de configuration dépend de l’agent ; le principe ne change pas : une URL et un en-tête. Voir Serveur MCP.

#Ce que dit un refus

CanalRéponseCause
REST403, X-Deny-Reason: keyEn-tête absent, clé inconnue ou révoquée. Le corps dit « Cle API absente, invalide ou revoquee ».
REST403, X-Deny-Reason: familyLa clé est valide, mais la famille de cet endpoint n’est pas dans votre offre.
MCP401, header X-API-KEY requisEn-tête absent sur la connexion, ou clé refusée. La vérification a lieu avant qu’un seul outil ne soit annoncé ; ensuite, chaque appel d’outil est jugé selon les droits de la clé.
WebSocket403 à la poignée de mainClé absente ou invalide dans l’URL (key), ou offre sans le flux (websocket). La connexion ne s’ouvre pas.
WebSocketfermeture 4003Plafond de connexions simultanées de l’offre atteint, pour tout le compte. La raison donne le plafond.

#Protéger et renouveler la clé

Où la garder

  • Dans une variable d’environnement ou un gestionnaire de secrets, jamais dans le dépôt de code.
  • Côté serveur uniquement. Une clé placée dans le JavaScript d’une page est publique, quel que soit l’obscurcissement.
  • Une clé par environnement : développement, préproduction, production. Une fuite peut alors être révoquée sans arrêter le reste.

La renouveler

  • Créez la nouvelle clé dans la console. Les deux restent valides pendant le recouvrement.
  • Déployez-la partout, puis vérifiez que le trafic sur l’ancienne est retombé à zéro.
  • Révoquez l’ancienne. La révocation est immédiate.