API REST

API REST pour développeurs et agents IA

Une requête, une réponse déjà agrégée. Toutes les routes partagent la même forme, la même enveloppe et les mêmes paramètres transverses : ce qui est vrai d’une famille l’est des autres.

Les agents IA atteignent les mêmes routes sans construire d’URL : les outils du serveur MCP les appellent avec votre clé et renvoient les mêmes champs.

#La forme d’une requête

Forme
GET https://api.bytnode.com/v1/<family>/<metric>
  ?symbol=BTCUSDT       # required on per-asset routes
  &timeframe=1h         # on bucketed routes
  &limit=100            # on /history routes
  &fields=time,rate_pct

X-API-KEY: your_key

Le chemin dit la famille et la métrique. Les paramètres disent l’actif, l’unité de temps et la profondeur. L’en-tête porte la clé. Il n’y a rien d’autre à savoir pour appeler n’importe quelle route.

Un seul endpoint échappe à cette forme : POST /v1/indicators, qui prend un corps JSON parce qu’il décrit un calcul, pas une lecture.

#Dernière valeur ou historique

La plupart des métriques existent en deux versions. La route nue renvoie la dernière valeur : un objet, ou null. Le suffixe /history renvoie une liste de fenêtres closes uniquement, de la plus récente à la plus ancienne.

curl
# The latest value
GET /v1/basis?symbol=BTCUSDT

# The last 24, from newest to oldest
GET /v1/basis/history?symbol=BTCUSDT&timeframe=1h&limit=24

# A dated window, in epoch milliseconds (here the last 12 hours, computed by the shell)
NOW=$(( $(date +%s) * 1000 ))
curl -H "X-API-KEY: $BYTNODE_API_KEY" \
  "https://api.bytnode.com/v1/basis/history?symbol=BTCUSDT&timeframe=1h&since_ms=$(( NOW - 43200000 ))&until_ms=$NOW"

#Paramètres communs

ParamètreTypeDéfautDescription
symbolstringobligatoireL’actif, au format du catalogue (BTCUSDT), dans n’importe quelle casse. Absent : 422. Inconnu : 422 avec la liste. Les routes de tout le marché ne l’acceptent pas.
timeframestringobligatoireSur les routes par intervalles, obligatoire : 1m, 5m, 15m, 30m, 1h, 4h, 1d (et 1w sur les bougies). Absent ou hors liste : 422. Une ligne par minute, c’est timeframe=1m.
limitinteger—Nombre de lignes, des plus récentes aux plus anciennes, sur les routes /history et les séries : 100 par défaut et 1000 au plus sur toutes les routes ; 10 000 au plus sur les événements individuels (ticks spot et futures, gros trades, liquidations). Au-delà d’une page, suivez next.
since_ms / until_msinteger—Fenêtre de temps en millisecondes Unix, là où la route l’accepte. Le filtre s’applique avant limit.
livebooleanfalseSur les routes de dernière valeur par intervalles : expose la période en cours, marquée is_closed: false.
fieldsstring—Projection CSV de data, notation pointée pour les champs imbriqués. Champ inconnu : 422 avec une suggestion.

Ce qui n’existe pas

Le paramètre exchange n’existe sur aucune route : le passer renvoie 422, comme tout paramètre qu’une route ne déclare pas, ou un paramètre répété ; l’erreur suggère le nom le plus proche.

#Paginer un historique

Chaque route /history se pagine avec next : chaque réponse porte l’URL de la page suivante, ou null une fois la plage lue. Les lignes arrivent de la plus récente à la plus ancienne par défaut ; avec order=asc (et since_ms), de la plus ancienne. Chaque ligne porte time et is_closed.

  1. Demandez une première page : limit lignes, sans borne pour les plus récentes, ou since_ms et order=asc pour avancer à partir d’une date.
  2. Appelez next tel quel : c’est l’URL relative de la page suivante (/v1/klines/history?symbol=BTCUSDT&timeframe=1h&limit=500&before_ms=…), vos paramètres compris — préfixez-la de l’hôte de l’API. Vers le passé, elle utilise before_ms, une borne EXCLUSIVE : aucune ligne ne revient deux fois, rien à soustraire.
  3. Arrêtez-vous quand next vaut null : la plage est lue, ou la page suivante sortirait de la fenêtre d’historique de votre offre (la dernière page ne demande alors que les lignes qui y tiennent encore). Un 403 history_depth ne revient que si vous écrivez vous-même une borne au-delà de cette fenêtre ; GET /v1/limits donne les fenêtres de votre offre. Sur les trades individuels (/v1/trades/*), une page couvre au plus 15 minutes et peut être vide ou partielle alors que next n’est pas null : seul next: null termine la plage. Il en va de même sur toute route paginée : une lecture d’historique qui atteint une période dense rend, dans son budget de temps, les lignes déjà lues — une page courte avec un next, jamais un dépassement de délai. Les SDK officiels suivent next pour vous.
import os

import requests  # pip install requests


def history(path: str, params: dict, max_pages: int = 20) -> list[dict]:
    """Every row of a /history route, following `next` until the range is read."""
    rows: list[dict] = []
    url, query = f"https://api.bytnode.com{path}", params
    for _ in range(max_pages):
        body = requests.get(
            url,
            params=query,
            headers={"X-API-KEY": os.environ["BYTNODE_API_KEY"]},
            timeout=30,
        ).json()
        if body["status"] == "error":
            # history_depth: the range asked goes further back than your plan's window;
            # error.message gives the largest limit that fits. Never swallow it.
            raise RuntimeError(f'{body["error"]["code"]}: {body["error"]["message"]}')
        page = body["data"] or []
        rows += page
        if body.get("next") is None:
            break  # the range is read
        # `next` is the relative URL of the following page, your parameters
        # included: call it as is. It never repeats a row.
        url, query = f"https://api.bytnode.com{body['next']}", None
    return rows


# Without `limit`, each page stops at your plan's history window: no 403.
candles = history("/v1/klines/history", {"symbol": "BTCUSDT", "timeframe": "1h"})

#Les familles

Toutes les routes tiennent sur une page dans le catalogue des endpoints ; chaque champ de chaque route, avec son unité et une réponse réelle, est dans la référence interactive.