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.
What you stop writing
Section titled “What you stop writing”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) >= 3Against 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.
What changes in how you think
Section titled “What changes in how you think”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.
A rough map
Section titled “A rough map”| 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 |
What the platform does not give you
Section titled “What the platform does not give you”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.
The first hour
Section titled “The first hour”GET /v2/venuesandGET /v2/dql/catalogue— no key. Learn what exists and what a query may name.- Write the condition you care about and send it to
POST /v2/dql/parseuntil it parses. Still no key. - Get a key, run it on
POST /v2/screenwithinclude_unknown: true, and look at what came back unknown — that is your real coverage. - Replace your staleness heuristics with
status(...)and delete them.