Skip to content

Migrating from exchange APIs

Moving a bot off an exchange’s own API, or off several.

This page is about the differences that actually cost you time. It is not a sales pitch; where the platform is worse for your case, it says so.

One client instead of N. Every venue’s REST is its own dialect: its own symbols, its own rate limits, its own funding convention, its own idea of what a “24h change” is. Here there is one request shape and one answer shape, and the venue’s own spelling comes back in venue_symbol for when you need to send an order.

One question instead of N round trips. “Which instruments have positive funding on at least three venues, where the funding reading is under two minutes old” is one query:

count(venues where funding_8h > 0 and age(funding) < 2m) >= 3

Against exchange APIs that is a fan-out, a join, and a set of decisions about what to do when one venue is slow to answer — decisions you would be making in your own code, silently, differently each time.

Normalisation you did not write. Funding is per-interval as the venue publishes it, and funding_8h is the comparable figure; the transform is stated per listing in GET /v2/instruments so you can check it rather than trust it.

A missing value is not a zero. Exchange APIs are inconsistent about this — some omit a field, some send null, some send 0, some send the last value they had. Here a value is always an object with a status, and five things are told apart: present, stale, missing, not published by the venue, not collected by the platform. Code that treated absence as zero will need a branch. That branch is the bug you had.

Comparisons can answer unknown. If a reading is stale, funding > 0 is neither true nor false. all(...) over a set with one stale reading is unknown, not false. Ask for include_unknown and you get those instruments back labelled, instead of a screen that quietly narrowed itself.

Time is receipt time. A historical answer uses what was visible at that instant, not what is known now. A reading that arrived late was not available to you then, and a backtest that used it would be measuring information you did not have.

Venues are never averaged. There is no “the price of BTC”. If you want one, you compute it, and you will have to decide what to do about the venue that is stale — which is the decision the platform refuses to make for you.

what you did what you do here
GET /fapi/v1/premiumIndex per venue POST /v2/snapshots with where, or a DQL condition on funding
GET /fapi/v1/klines per symbol, per venue POST /v2/candles, many listings in one request
GET /api/v3/depth, then compute depth yourself POST /v2/books, or depth_bid_value(10bps) in a query
exchange info for tick size and lot size GET /v2/instruments
a websocket per venue, reconnect logic per venue GET /v2/stream, one connection, Last-Event-ID
your own staleness heuristics status(...) and age(...), stated per value

Said plainly, because finding out later is worse:

  • No orders. This is data. It will tell you the tick size; it will not place anything.
  • No spot. Perpetual futures only.
  • No raw trade tape in v2. The trade layer exists in the live surface; it is not part of the language yet.
  • No per-venue websocket parity. The stream carries the layers the platform collects, at the platform’s cadence, not every message the venue emits.
  • Only the venues in Venues in DQL. A venue the platform does not collect is not there, and the list is read live rather than promised.
  1. GET /v2/venues and GET /v2/dql/catalogue — no key. Learn what exists and what a query may name.
  2. Write the condition you care about and send it to POST /v2/dql/parse until it parses. Still no key.
  3. Get a key, run it on POST /v2/screen with include_unknown: true, and look at what came back unknown — that is your real coverage.
  4. Replace your staleness heuristics with status(...) and delete them.