API REST

API REST para desarrolladores y agentes de IA

Una petición, una respuesta ya agregada. Todas las rutas comparten la misma forma, el mismo sobre y los mismos parámetros transversales: lo que vale para una familia vale para las demás.

Los agentes de IA llegan a las mismas rutas sin construir una URL: las herramientas del servidor MCP las llaman con tu clave y devuelven los mismos campos.

#La forma de una petición

Forma
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

La ruta indica la familia y la métrica. Los parámetros indican el activo, la temporalidad y la profundidad. La cabecera lleva la clave. No hace falta saber nada más para llamar a cualquier ruta.

Un solo endpoint escapa a esta forma: POST /v1/indicators, que recibe un cuerpo JSON porque describe un cálculo, no una lectura.

#Último valor o historial

La mayoría de las métricas existen en dos versiones. La ruta sin sufijo devuelve el último valor: un objeto o null. El sufijo /history devuelve una lista de ventanas cerradas únicamente, de la más reciente a la más antigua.

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"

#Parámetros comunes

ParámetroTipoPor defectoDescripción
symbolstringobligatorioEl activo, en el formato del catálogo (BTCUSDT), en mayúsculas o minúsculas. Ausente: 422. Desconocido: 422 con la lista. Las rutas de todo el mercado no lo aceptan.
timeframestringobligatorioEn las rutas por intervalos, obligatorio: 1m, 5m, 15m, 30m, 1h, 4h, 1d (y 1w en las velas). Ausente o fuera de la lista: 422. Una fila por minuto es timeframe=1m.
limitinteger—Número de filas, de la más reciente a la más antigua, en las rutas /history y las series: 100 por defecto y 1000 como máximo en todas las rutas; 10 000 como máximo en los eventos individuales (ticks spot y futuros, operaciones grandes, liquidaciones). Más allá de una página, sigue next.
since_ms / until_msinteger—Ventana de tiempo en milisegundos Unix, donde la ruta lo acepta. El filtro se aplica antes que limit.
livebooleanfalseEn las rutas de último valor por intervalos: expone el periodo en curso, marcado is_closed: false.
fieldsstring—Proyección CSV de data, notación con puntos para los campos anidados. Campo desconocido: 422 con una sugerencia.

Lo que no existe

El parámetro exchange no existe en ninguna ruta: las respuestas están agregadas y no nombran su mercado, con la única excepción de los diferenciales entre mercados. Pasarlo devuelve 422, igual que cualquier parámetro que una ruta no declara o un parámetro repetido; el error sugiere el nombre más parecido.

#Paginar un historial

Cada ruta /history se pagina con next: cada respuesta lleva la URL de la página siguiente, o null una vez leído el rango. Las filas llegan de la más reciente a la más antigua por defecto; con order=asc (y since_ms), de la más antigua. Cada fila lleva time e is_closed.

  1. Pide una primera página: limit filas, sin límite para las más recientes, o since_ms y order=asc para avanzar desde una fecha.
  2. Llama a next tal cual: es la URL relativa de la página siguiente (/v1/klines/history?symbol=BTCUSDT&timeframe=1h&limit=500&before_ms=…), con tus parámetros incluidos — antepón el host de la API. Hacia atrás, usa before_ms, un límite EXCLUSIVO: ninguna fila vuelve dos veces, nada que restar.
  3. Detente cuando next sea null: el rango está leído, o la página siguiente saldría de la ventana de historial de tu plan (la última página solo pide entonces las filas que aún caben). Un 403 history_depth solo aparece si tú mismo escribes un límite más allá de esa ventana; GET /v1/limits da las ventanas de tu plan. En los trades individuales (/v1/trades/*), una página cubre como máximo 15 minutos y puede estar vacía o incompleta aunque next no sea null: solo next: null termina el rango. Lo mismo vale en toda ruta paginada: una lectura de histórico que llega a un periodo denso devuelve, dentro de su presupuesto de tiempo, las filas ya leídas — una página corta con un next, nunca un tiempo de espera agotado. Los SDK oficiales siguen next por usted.
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"})

#Las familias

Todas las rutas caben en una página en el catálogo de endpoints; cada campo de cada ruta, con su unidad y una respuesta real, está en la referencia interactiva.