Skip to main content
binXbase

Developers

This exchange's read API is twelve HTTP routes: a market catalogue, candles, a health check, three server-sent event streams, four ledger reads and two composite endpoints. They are documented from the handlers that serve them, and they are the same ones the site itself calls.

Three requests, no setup

Nothing to register for and nothing to configure. In the first two, https://your-deployment stands for whichever origin is serving you this page — swap it in and they work right now. The third is plain browser JavaScript against a relative URL, so it runs from any page on this site exactly as written.

  1. Ask whether the data path is live

    Not “is the web server up” — that stays green while every price is an hour stale. This reports whether the ingest worker is writing, and answers 503 when it is not.

    Shell
    curl -i 'https://your-deployment/api/health'
  2. Pull real candles

    Eight intervals, up to 1,000 bars, straight off the venue. Note that time comes back in seconds — the charting convention, and the one unit surprise in this API.

    Shell
    curl 'https://your-deployment/api/klines?symbol=BTCUSDT&interval=1h&limit=3'
  3. Open a live stream

    The order book, the trade tape and the forming 1-minute candle for one symbol. Plain server-sent events, so a browser needs no library at all.

    JavaScript
    const stream = new EventSource('/api/stream/symbol/BTCUSDT')
    
    stream.addEventListener('snapshot', (event) => {
      const { ticker, book, trades } = JSON.parse(event.data)
      render(ticker, book, trades)
    })
    
    stream.addEventListener('trade', (event) => {
      appendToTape(JSON.parse(event.data))
    })

The whole API, on one screen

Twelve routes is a small surface, and seeing all of it at once is a more useful answer than a search box — you will know in ten seconds whether the thing you came for exists. Every row links into the reference.

Every endpoint in the binXbase read API, with its method, path, authentication mode and purpose
EndpointAuthWhat it serves
/api/healthGETPublicIs the ingest worker writing? 503 when it is not.
/api/markets/indexGETPublicThe searchable catalogue — six fields per pair, top 800 by volume.
/api/klinesGETPublicCandles for one pair at one of eight intervals.
/api/stream/tickersGETSSEPublicEvery tradable ticker, batched, as the worker publishes them.
/api/stream/symbolGETSSEPublicOne symbol's book, tape and forming 1m candle.
/api/stream/accountGETSSESessionA poke when your ledger moves. Never a balance.
/api/account/balancesGETSessionSpot, futures and funding wallet balances from the ledger.
/api/account/ordersGETOptionalThe last 40 orders, as the smallest projection that shows a status change.
/api/account/positionsGETSessionOpen futures positions with marks, or closed position history.
/api/account/activityGETSessionThe futures wallet ledger feed: margin, PnL, funding, liquidations.
/api/terminal/contextGETOptionalEverything an in-place market switch needs, in one round trip.
/api/ai/analyzePOSTPublicStreaming market commentary grounded in this venue’s own candles.

Every route has a client you can watch

This is the spot terminal at /trade/BTCUSDT, rendering live venue data. Five panels, five things documented in the reference — open it in another tab and you are watching these endpoints deliver.

The binXbase BTC/USDT spot terminal: a candle chart, a live order book, a trade tape and an order ticket, all rendering live venue data.

Every panel above is a client of an endpoint below

  1. Market headerThe snapshot event’s ticker, then every ticker event after it. The same figures the markets table gets in batches from /api/stream/tickers.

  2. Candle chartHistory from GET /api/klines at the selected interval, then the candle event folds the live 1-minute bar on top of it as trades print.

  3. Order bookThe book event — top 20 levels a side, roughly every 100ms. Bids descend from the best bid, asks ascend from the best ask.

  4. Trade tapeOne trade event per print. The row colour is side, which names the aggressor — buy means a taker lifted the ask.

  5. Order ticketGET /api/terminal/context for the market’s real precisions and minimum notional. It validates against them and then submits through a server action — there is no HTTP endpoint that places an order.

What is behind the routes

A websocket has to outlive a request, and a request handler is gone the moment it responds. So the venue connections live in a separate process — one worker holds every socket open and writes what arrives into Redis, and the web app only ever reads. That is the entire reason there are two processes, and it is what lets one upstream connection fan out to every connected browser.

The dashed branch is the part worth knowing before you integrate. Redis is a cache here, never a dependency. Every read falls back to the venue’s own REST API, so the endpoints keep answering with the worker stopped and the cache unreachable — polled instead of streamed, and slower. Never blank, and never a frozen number wearing a fresh timestamp.

You can tell which path you are on. The status page renders /api/health as it arrives, and that endpoint names the fallback whenever the fallback is the thing carrying the site.

There are no API keys

Stated early because it decides whether this API is any use to you. Nothing in this codebase issues, stores or verifies a key, and no handler reads an Authorization header. The account routes authenticate with the same session cookie the website uses, and there is no endpoint that trades credentials for one.

So this is a browsable read API. Eight of the twelve routes answer anyone, four are practical only from a signed-in browser, and none of them writes anything. Orders are placed by server actions from the trading terminal, not over HTTP with a body you can construct — so no combination of these routes adds up to a trading client.

What authenticates today

  • A NextAuth session cookie, set by signing in at /signin.
  • Same origin only. No Access-Control-Allow-Origin header is emitted anywhere, so another site’s JavaScript cannot read these responses.
  • Four routes answer 401 without it. Two more read it when it is there and answer without it.

What would have to exist for keys to be real

  • Issuance and revocation — a table and a surface, with the secret shown once and only its hash stored.
  • A signing scheme — a key in a query string is a key in every access log. HMAC over method, path, body, nonce and timestamp, with a replay window on the server.
  • Scopes — read and trade must be different grants, or a leaked read key spends money.
  • Rate limiting — session traffic is bounded by how fast a person can click. Key traffic is not, and no counter or quota exists in this repo.
  • Write endpoints — place, amend and cancel, with idempotency keys so a retry cannot double-fill.

The streams are the interesting part

Server-sent events rather than websockets, deliberately: the data travels one way, the browser reconnects by itself, and there is no protocol on top of HTTP to implement. Three rules cover almost every integration mistake made against them.

  • Every frame is named, so onmessage never fires

    These streams emit snapshot, book, trade, candle, ticker, tickers, ready, poke, fail and error. None of them is the default message event, so a client written against onmessage connects successfully and then sits in silence.

  • A snapshot arrives before anything live

    Every stream sends a full snapshot on connect, which is why reconnection needs no replay and there are no sequence gaps to reconcile. A client that connects between two publishes renders complete instead of staring at an empty table.

  • Nothing in front of these routes may compress or buffer

    A compressor assumes a body that ends, and these do not — its buffer grows without bound until the process is killed. The routes send cache-control: no-transform and x-accel-buffering: no; if you put a proxy in front, exclude /api/stream/* from compression.

Run the whole thing locally

A pnpm workspace with two processes. The web app is the only one you strictly need — without Redis or the worker, every read falls back to the venue, so the site polls instead of streams and is slower. Nothing is blank.

Get it running
pnpm install
cp .env.example .env   # defaults work as-is

pnpm dev               # the site, and with it the API
Add the live pipeline and the ledger
pnpm infra:up          # Postgres and Redis, in Docker
pnpm db:migrate        # the ledger — needed by /api/account/*

# in a second terminal
pnpm dev:marketd       # the venue websockets

Why the ports are unusual

The compose file maps both services to non-standard host ports rather than their defaults, so a local checkout cannot silently connect to another project’s database and start writing into it. The values live in the repository — the compose file and .env.example agree, so the copy step above needs no editing.

Development commands and what each one does
CommandWhat it does
pnpm devThe Next.js dev server — the API and the site.
pnpm dev:marketdThe ingest worker. Without it, reads fall back to the venue REST API.
pnpm infra:upPostgres and Redis in Docker. infra:down stops them again.
pnpm db:migrateApplies migrations and runs the engine cutover — the ledger.
pnpm testVitest across core, marketd and web.
pnpm typecheckTypechecks every package in the workspace.

Where every number comes from

Attribution matters more here than on most APIs, because the whole premise of this project is that the market data is real while the money is not. These are the upstream sources, and the refresh column is the cache window in the code rather than an aspiration.

Upstream data sources, their refresh behaviour, and which routes serve them
DataSourceRefreshServed by
Spot pairs, tickers, candles, depth, tradesBinance public REST and websocketStreaming/api/markets/index · /api/klines · /api/stream/*
Perpetual futures, funding, open interestBinance USDT-M futures REST and websocketStreaming · funding on a slow sweep/api/terminal/context · /api/stream/*
Market cap, circulating supply, rankCoinGecko free tier5 minutesNo route — server-rendered tools only
Fiat cross ratesFrankfurter (ECB reference rates)1 hourNo route — server-rendered tools only
Market commentaryAnthropic Claude, prompted with the candles aboveOn request/api/ai/analyze

Only the last one needs a credential, and it belongs to the operator rather than the caller: without ANTHROPIC_API_KEY in the server environment, /api/ai/analyze answers 503 and names the missing configuration rather than failing obscurely.

Balances, orders, positions and activity have no upstream at all. They are this project’s own double-entry ledger in Postgres, moved by real venue prices. How this works.

Read the reference

Every parameter each handler reads, every status code it can return, the exact shape of every response, the event names on all three streams, and the types they are built from. Written with the route files open — if a route behaves differently from what the page says, the page is the thing that is wrong.