Order Book
Import your own exchange order flow (orders and their lifecycle events) so it can be browsed, tied to identities and scanned for order-book market abuse.
The books (venue + instrument pairs) the workspace holds, most active first, up to 200.
Same item shape as instruments in the summary.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Response Body
application/json
application/json
curl -X GET "https://example.com/v1/orderbook/books"{ "books": [ { "venue": "WHITEBIT", "instrument": "BTC/USDC", "events": 820, "orders": 410, "clients": 31, "cancels": 96, "last_event": "2026-04-20T09:44:17+00:00" } ]}The distinct client ids found in your events, busiest (most events) first, each with its linked
identity (or null) and its tags. The roster is read from the events: there is no separate client
list to maintain.
linked and total describe this page only, not the whole workspace (use
GET /v1/orderbook/summary for workspace coverage).
status=matched|unmatched cannot be applied when the workspace has more than 20,000 links. The
list is then returned unfiltered with filter_applied: false.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Query Parameters
Case-insensitive substring of the client id. Ignored when longer than 190 characters.
length <= 190matched keeps linked clients, unmatched keeps the ones still to link, anything else means all.
"all"Value in
- "all"
- "matched"
- "unmatched"
Page size. Clamped to 1..500. Defaults to 100.
1 <= value <= 500100Response Body
application/json
application/json
curl -X GET "https://example.com/v1/orderbook/clients"{ "clients": [ { "client_id": "POSTG0000114", "events": 212, "orders": 106, "venues": 1, "instruments": 3, "first_event": "2026-03-20T08:00:00+00:00", "last_event": "2026-04-20T09:44:17+00:00", "identity": { "id": "8a1d2c3b-4e5f-4a6b-9c7d-0e1f2a3b4c5d", "type": "PERSON", "display_name": "Jane Doe", "country": "FR" }, "tags": [ { "id": "1c2d3e4f-5a6b-4c7d-8e9f-a0b1c2d3e4f5", "name": "VIP", "color": "#f59e0b" } ] }, { "client_id": "POSTG0000127", "events": 40, "orders": 20, "venues": 1, "instruments": 1, "first_event": "2026-03-21T08:00:00+00:00", "last_event": "2026-03-21T09:00:00+00:00", "identity": null, "tags": [] } ], "linked": 1, "total": 2, "status": "all", "filter_applied": true}One page of the workspace's events, newest first (ts_event descending, ties broken by
order_id then event_id), with the total count behind the filters. Every filter is optional
and they are combined with AND.
Amounts are returned as strings so no precision is lost. identities maps each client id
on the page that is linked to an identity; unlinked client ids are absent from it.
A filter value that fails its format check (for example a venue with spaces, or an unknown
event_type) is ignored rather than rejected.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Query Parameters
Venue, case-insensitive. Letters, digits and / _ - ., up to 60 characters.
Instrument as BASE/QUOTE, case-insensitive.
Exact client id (up to 190 characters).
Exact order id (up to 190 characters).
Event type, case-insensitive.
Value in
- "NEW"
- "REPLACE"
- "PARTIAL_FILL"
- "FILL"
- "CANCEL"
- "EXPIRE"
- "REJECT"
Side, case-insensitive.
Value in
- "BUY"
- "SELL"
Inclusive lower bound on ts_event. RFC 3339, or YYYY-MM-DD HH:MM:SS read as UTC.
Exclusive upper bound on ts_event, same formats. Must be after from.
Page size. Clamped to 1..200. Defaults to 50.
1 <= value <= 20050Rows to skip. Capped at 100000. Defaults to 0.
0 <= value <= 10000001 or true adds raw, the row exactly as it was posted, to each event.
Value in
- "1"
- "true"
Response Body
application/json
application/json
application/json
curl -X GET "https://example.com/v1/orderbook/events"{ "events": [ { "event_id": "3f5a0c9b7e1d2f4a6c8e0b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a", "ts_event": "2026-04-20T09:44:17+00:00", "organization": "acme", "venue": "WHITEBIT", "instrument": "BTC/USDC", "base": "BTC", "quote": "USDC", "client_id": "POSTG0000114", "account_id": "", "order_id": "1776678242178", "event_type": "FILL", "order_type": "LIMIT", "side": "SELL", "status": "FILLED", "price": "75235.92", "quantity": "0.0001", "filled_qty": "0.0001", "notional": "7.523592", "notional_usd": null, "fee": null, "fee_currency": "", "cancel_origin": "", "time_in_force": "", "order_origin": "", "liquidity_flag": "", "best_bid": null, "best_ask": null } ], "identities": { "POSTG0000114": { "id": "8a1d2c3b-4e5f-4a6b-9c7d-0e1f2a3b4c5d", "type": "PERSON", "display_name": "Jane Doe" } }, "total": 1, "limit": 50, "offset": 0}Validates and stores one batch of up to 1000 rows into a RUNNING import. The whole request
must stay under 2 MB; a larger body arrives empty and is answered with a 400 telling you to
send fewer rows.
Rows that fail validation are skipped and reported in errors; the others are written. A partial
batch is a normal outcome and still returns 200. Only the first 50 rejection messages of a run are
kept on the import record; each batch response carries all of its own.
record_shape must match the shape the import was opened with, and the parsing options
(timezone, decimal_separator) must be sent on every batch, since the server does not
remember them between requests. Set first_row to the file line of the batch's first row so
error messages point at the original file.
Re-sending a batch (after a timeout, for example) is safe: event ids are derived from the event content, so duplicates collapse in storage.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Rows plus the parsing options that apply to all of them.
Response Body
application/json
application/json
application/json
curl -X POST "https://example.com/v1/orderbook/import/batch" \ -H "Content-Type: application/json" \ -d '{ "import_id": "0b8f7a52-6a0e-4a8f-9d5e-3c1f0e2b7a41", "record_shape": "ORDER_SNAPSHOT", "timezone": "UTC", "decimal_separator": ".", "first_row": 2, "rows": [ { "client_id": "POSTG0000113", "order_id": "1767011739000", "venue": "BYBIT", "base": "ETH", "quote": "USDC", "side": "SELL", "order_type": "LIMIT", "price": "3000", "quantity": "0.003", "status": "CANCELED", "cancel_origin": "EXCHANGE", "ts_submitted": "2025-12-29 12:35:39", "ts_finished": "2025-12-29 12:51:00" } ] }'{ "import_id": "0b8f7a52-6a0e-4a8f-9d5e-3c1f0e2b7a41", "rows_seen": 1, "rows_accepted": 1, "rows_rejected": 0, "events_written": 2, "errors": []}Closes a RUNNING import as DONE (default) or FAILED, and returns the import record.
events_written is then replaced by the number of distinct events the table actually holds
for this import, which is lower than the running tally when rows were re-sent or duplicated.
Finishing an import that is no longer RUNNING changes nothing and returns its current record.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/orderbook/import/finish" \ -H "Content-Type: application/json" \ -d '{ "import_id": "0b8f7a52-6a0e-4a8f-9d5e-3c1f0e2b7a41" }'{ "id": "0b8f7a52-6a0e-4a8f-9d5e-3c1f0e2b7a41", "venue": "WHITEBIT", "filename": "whitebit-2026-04.csv", "record_shape": "ORDER_SNAPSHOT", "status": "DONE", "rows_received": 127, "rows_accepted": 126, "rows_rejected": 1, "events_written": 252, "errors": [ "row 88: ts_finished: required because status is FILLED (a terminal state)" ], "created_at": "2026-04-21 10:02:11", "finished_at": "2026-04-21 10:02:19", "rolled_back_at": "", "events_removed": 0}Opens an import run and returns its import_id. Every batch refers to that id, and the run's
record_shape is fixed here: batches declaring another shape are refused.
All fields are optional, but the body must be a JSON object (send {} for the defaults).
venue is stored uppercased and is only a label on the run; each row still carries its own
venue. profile_id, when given, must be a mapping profile of your workspace and is recorded
as the provenance of the mapping.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
curl -X POST "https://example.com/v1/orderbook/import/start" \ -H "Content-Type: application/json" \ -d '{ "record_shape": "ORDER_SNAPSHOT", "filename": "whitebit-2026-04.csv", "venue": "WHITEBIT" }'{ "import_id": "0b8f7a52-6a0e-4a8f-9d5e-3c1f0e2b7a41", "record_shape": "ORDER_SNAPSHOT"}Lists the workspace's import runs, newest first, including rolled-back ones. A run still
RUNNING long after its created_at is an upload that was never finished.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Query Parameters
Maximum number of imports. Clamped to 1..200. Defaults to 25.
1 <= value <= 20025Response Body
application/json
application/json
curl -X GET "https://example.com/v1/orderbook/imports"{ "imports": [ { "id": "0b8f7a52-6a0e-4a8f-9d5e-3c1f0e2b7a41", "venue": "WHITEBIT", "filename": "whitebit-2026-04.csv", "record_shape": "ORDER_SNAPSHOT", "status": "ROLLED_BACK", "rows_received": 127, "rows_accepted": 127, "rows_rejected": 0, "events_written": 254, "errors": [], "created_at": "2026-04-21 10:02:11", "finished_at": "2026-04-21 10:02:19", "rolled_back_at": "2026-04-21 11:30:00", "events_removed": 254 } ]}Deletes the events written by one import and marks it ROLLED_BACK. The import record is kept
(with rolled_back_at and events_removed) as an audit trail. Works on RUNNING imports too,
which is how an abandoned upload is undone. Links between client ids and identities are not
touched.
Events carry no import in their id: if the same file was imported twice, the stored copy of an event belongs to the later import. Rolling back the earlier one may then remove nothing, and rolling back the later one removes events both imports carried. Re-import the file if that was not intended.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/orderbook/imports/rollback" \ -H "Content-Type: application/json" \ -d '{ "import_id": "0b8f7a52-6a0e-4a8f-9d5e-3c1f0e2b7a41" }'{ "rolled_back": true, "import_id": "0b8f7a52-6a0e-4a8f-9d5e-3c1f0e2b7a41", "events_removed": 254}Points one of your client ids at an identity, replacing any previous link for that client id (linking a client already held by another identity moves it). Several client ids may point at the same identity, which is how one person trading under several ids becomes visible.
The identity must be visible to your workspace (403 otherwise) and the client id must appear in your stored events (400 otherwise). The change is written to the identity's activity log.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/orderbook/link-identity" \ -H "Content-Type: application/json" \ -d '{ "client_id": "POSTG0000114", "identity_id": "8a1d2c3b-4e5f-4a6b-9c7d-0e1f2a3b4c5d" }'{ "linked": true, "client_id": "POSTG0000114", "identity_id": "8a1d2c3b-4e5f-4a6b-9c7d-0e1f2a3b4c5d"}Removes the link of one client id. Succeeds even when the client id was not linked.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Query Parameters
Client id to unlink (URL-encoded).
length <= 190Response Body
application/json
application/json
application/json
curl -X DELETE "https://example.com/v1/orderbook/link-identity?client_id=string"{ "unlinked": true, "client_id": "POSTG0000114"}Lists the workspace's saved mapping profiles, most recently updated first. A profile stores how a feed's columns map to canonical fields so the next import of the same feed does not need to be mapped again.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Response Body
application/json
application/json
curl -X GET "https://example.com/v1/orderbook/profiles"{ "profiles": [ { "id": "5d7c1a3e-2f4b-4c6d-8e9f-0a1b2c3d4e5f", "name": "Whitebit export", "venue": "WHITEBIT", "profile": { "record_shape": "ORDER_SNAPSHOT", "timezone": "Europe/Paris", "decimal_separator": ",", "columns": { "ord_id": "order_id", "ord_amount": "quantity" } }, "created_at": "2026-04-20 16:10:00", "updated_at": "2026-04-21 09:58:43" } ]}Creates a profile, or overwrites the one with the same name in your workspace (its venue and
profile are replaced and its id kept). Returns the id of the stored profile.
profile is a free-form JSON object that your client replays; the server never applies it.
Only two keys are checked when present: record_shape must be a valid shape and timezone a
valid IANA zone. The serialized profile is limited to 64 KB.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
curl -X POST "https://example.com/v1/orderbook/profiles" \ -H "Content-Type: application/json" \ -d '{ "name": "Whitebit export", "venue": "whitebit", "profile": { "record_shape": "ORDER_SNAPSHOT", "timezone": "Europe/Paris", "decimal_separator": ",", "columns": { "ord_id": "order_id", "ord_amount": "quantity" } } }'{ "id": "5d7c1a3e-2f4b-4c6d-8e9f-0a1b2c3d4e5f", "name": "Whitebit export"}Deletes one mapping profile of your workspace. Imports that recorded it keep their history.
Answers {"deleted": true} even when no profile with this id exists.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Query Parameters
Profile id.
uuidResponse Body
application/json
application/json
application/json
curl -X DELETE "https://example.com/v1/orderbook/profiles?id=497f6eca-6276-4993-bfeb-53cbbbba6f08"{ "deleted": true}Deletes every order-book event of the workspace and its whole import history. Mapping
profiles and client-identity links are kept. This cannot be undone; to undo a single file use
POST /v1/orderbook/imports/rollback instead.
To confirm, confirm must equal your workspace's exact tenancy key: the organization name
shown by GET /v1/hello (Hello, member of <key>!). In the development workspace that key ends
in __dev, so a dev key can only purge dev data.
Admin only: a member, or a key created by a member, gets 403.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/orderbook/purge" \ -H "Content-Type: application/json" \ -d '{ "confirm": "acme" }'{ "purged": true, "events_removed": 12400, "imports_removed": 7}Returns the canonical fields a row may carry for one record shape, with their type, whether
they are required, allowed enum values, header aliases (lowercase names commonly used for the
column in exchange exports) and enum synonyms (common spellings mapped to the canonical value).
Fields that only apply to the other shape are left out: ts_submitted appears only for
ORDER_SNAPSHOT, ts_event and event_type only for ORDER_EVENT.
Synonyms are a hint for building your column mapping. The server does not apply them:
a row must already carry the canonical value (FILLED, not COMPLETED).
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Query Parameters
Record shape to describe. Case-insensitive. Defaults to ORDER_SNAPSHOT.
"ORDER_SNAPSHOT"Value in
- "ORDER_SNAPSHOT"
- "ORDER_EVENT"
Response Body
application/json
application/json
application/json
curl -X GET "https://example.com/v1/orderbook/schema"{ "shape": "ORDER_SNAPSHOT", "shapes": [ "ORDER_SNAPSHOT", "ORDER_EVENT" ], "max_rows_per_batch": 1000, "max_rows_per_validate": 500, "fields": [ { "name": "side", "label": "Side", "type": "enum", "required": true, "values": [ "BUY", "SELL" ], "aliases": [ "side", "direction", "way", "buy_sell" ], "unlocks": "", "synonyms": { "B": "BUY", "S": "SELL", "BID": "BUY", "ASK": "SELL" } } ]}Totals for the stored order book, the 50 most active books (venue + instrument), and how many clients are linked to an identity.
coverage.matched counts linked clients that still have events; coverage.stale counts links
whose client has no event left (typically after a rollback). When the workspace has more than
20,000 links the intersection is not computed: exact is false, matched is 0, and stale
and pct are null.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Response Body
application/json
application/json
curl -X GET "https://example.com/v1/orderbook/summary"{ "totals": { "events": 12400, "orders": 6150, "clients": 214, "instruments": 9, "venues": 2, "first_event": "2025-12-29T12:35:39+00:00", "last_event": "2026-04-20T09:44:17+00:00" }, "instruments": [ { "venue": "BYBIT", "instrument": "ETH/USDC", "events": 5100, "orders": 2540, "clients": 88, "cancels": 1210, "last_event": "2026-04-20T09:40:02+00:00" } ], "coverage": { "exact": true, "clients": 214, "matched": 40, "mapped": 42, "stale": 2, "pct": 19 }}Parses and validates up to 500 rows exactly as an import would, and reports what would be stored. Nothing is written. Use it on a sample of the file before opening an import: it is what catches a wrong timezone or a mis-mapped column before thousands of rows are stored.
Each rejected row produces one message in errors, prefixed with its line number
(row N: ..., counted from first_row). profile is built from the rows that parsed, so it
shows the distribution that would actually be stored.
A snapshot row expands into one NEW event plus, when its status is terminal, the event that
ended it, which is why events_would_write can exceed rows_accepted.
Authorization
ApiKeyAuth Send your API key as a bearer token: Authorization: Bearer sq_YOUR_API_KEY.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Rows plus the parsing options that apply to all of them.
Response Body
application/json
application/json
application/json
curl -X POST "https://example.com/v1/orderbook/validate" \ -H "Content-Type: application/json" \ -d '{ "record_shape": "ORDER_SNAPSHOT", "timezone": "Europe/Paris", "decimal_separator": ".", "first_row": 2, "rows": [ { "client_id": "POSTG0000114", "order_id": "1776678242178", "venue": "WHITEBIT", "base": "BTC", "quote": "USDC", "side": "SELL", "order_type": "LIMIT", "price": "75235.92", "quantity": "0.0001", "status": "FILLED", "ts_submitted": "2026-04-20 09:44:02", "ts_finished": "2026-04-20 09:44:17" } ] }'{ "rows_seen": 2, "rows_accepted": 1, "rows_rejected": 1, "events_would_write": 2, "errors": [ "row 3: status: 'COMPLETED' is not one of NEW, OPEN, PARTIALLY_FILLED, FILLED, CANCELED, EXPIRED, REJECTED - map it to one of those in the import profile" ], "profile": { "events": 2, "venues": [ { "value": "WHITEBIT", "count": 2 } ], "instruments": [ { "value": "BTC/USDC", "count": 2 } ], "statuses": [ { "value": "FILLED", "count": 1 }, { "value": "NEW", "count": 1 } ], "clients": [ { "value": "POSTG0000114", "count": 2 } ], "first_event": "2026-04-20T07:44:02+00:00", "last_event": "2026-04-20T07:44:17+00:00" }}