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 current” is one query:
count(venues where funding_8h > 0 and status(funding) = present) >= 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 wherever the registry holds the listing’s interval, or a continuous venue’s rate unit (elsewhere it
is null, and the listing is not compared); both, and their sources, are stated per listing in GET /v2/instruments
so you can check them rather than trust them.
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 for snapshot layers, or GET /v2/trades/stream for raw trades |
| a venue’s trade-history endpoint | POST /v2/trades, bounded stored events with explicit gap coverage |
| 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.
- DQL and Agent are perpetual-only. The market-data endpoints can read spot with
kind: "spot"and both kinds withkind: "all". - No trade-tape functions in DQL. Stored trades are available through
POST /v2/tradesandGET /v2/trades/stream; the stream has bounded subscribers and no replay. - 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. Venues and instruments are paged (limitdefaults to 500); follownext_cursoruntil null and keep each page’sregistry_revisionthe same before treating the catalogue as complete.- 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.