Guide
WebSockets
On this page
Connecting#
Upgrade request
wss://api.ordermap.si/v1/ws/options/trades?symbols=SPXW&min_premium=100000
Authorization: Bearer om_...- Send the key in the
AuthorizationorX-API-Keyheader of the HTTP upgrade request. A key in the query string is rejected. The upgrade counts as one request against your rate limit. - The query string can carry an initial subscription:
symbols(comma-separated) and the stream's filters. You can also subscribe after connecting. - One connection per account per stream. A second upgrade on a stream that already has a connection is refused with
409 stream_connection_limitbefore the upgrade, and the first connection is not disturbed. A client that crashed is noticed within about 20 seconds, after which you can reconnect. - Errors before the upgrade are ordinary JSON errors:
400(bad symbols or filters),401,403(streaming_not_in_plan,dataset_release_restricted,account_suspended),409,426 upgrade_required(not a WebSocket request),429(rate limit or cooldown) and503 stream_unavailable(the stream is not running; retry afterRetry-After).
Node.js, with the ws package: the key stays on the server
import WebSocket from "ws";
const ws = new WebSocket("wss://api.ordermap.si/v1/ws/options/trades?symbols=SPXW&min_premium=100000", {
headers: { Authorization: `Bearer ${process.env.ORDERMAP_API_KEY}` },
});
ws.on("message", (raw) => {
const message = JSON.parse(raw.toString());
if (message.type === "event") console.log(message.seq, message.data.option_symbol, message.data.premium);
if (message.type === "gap") console.warn("continuity lost: refetch through REST", message);
if (message.type === "error") console.error(message.code, message.message);
});
ws.on("close", (code, reason) => console.log("closed", code, reason.toString()));
// Change the subscription while connected:
ws.on("open", () => {
ws.send(JSON.stringify({ action: "subscribe", symbols: ["SPXW", "SPY"], filters: { side: ["above_ask", "at_ask"] } }));
});From a browser#
A browser cannot set headers on a WebSocket object, so it sends the key as a subprotocol: new WebSocket(url, ["ordermap.v1", "ordermap.key.om_..."]). The server reads it only on an upgrade to a stream (a path under /v1/ws/) that has neither an Authorization nor an X-API-Key header and offers ordermap.v1. It answers with Sec-WebSocket-Protocol: ordermap.v1, never repeats or logs the key, and treats it exactly like a bearer key: the same rate limit, abuse checks and 30-second revalidation.
// A browser cannot set headers on a WebSocket: the key travels as a subprotocol.
const ws = new WebSocket("wss://api.ordermap.si/v1/ws/futures/trades?symbols=ES", ["ordermap.v1", "ordermap.key." + key]);Messages#
The protocol is ordermap.v1. Every message is one JSON text frame.
| Direction | Message | Meaning |
|---|---|---|
| server | {"type":"hello","protocol":"ordermap.v1","event_type":...,"heartbeat_ms":10000,"max_symbols":100,"wildcard":true,"filters":{...}} | First message. Describes the stream and its filters. |
| server | {"type":"subscribed","event_type":...,"symbols":[...],"filters":{...}} | The current subscription, sent after every change (* for a wildcard). |
| server | {"type":"status","event_type":...,"symbol":...,"state":"live","reason":...} | state is live, degraded or unavailable. |
| server | {"type":"gap","event_type":...,"symbol":...,"reason":...} | Continuity was lost. Refetch the missed range through REST. |
| server | {"type":"error","code":...,"message":...,"symbol":...} | A request message was refused. |
| server | {"type":"event","event_type":...,"seq":N,"data":{...}} | One event. seq counts the connection's events from 1. |
| client | {"action":"subscribe","symbols":["SPXW"],"filters":{"min_premium":100000}} | Adds symbols. filters, when present, replace the previous filters ({} clears them). |
| client | {"action":"unsubscribe","symbols":["SPXW"]} | Removes symbols. With no symbols, removes everything. |
The first message
{"type":"hello","protocol":"ordermap.v1","event_type":"options.trades","heartbeat_ms":10000,"max_symbols":100,"wildcard":true,"filters":{}}- One event per message, delivered when it happens. There is no timed batching and no resend buffer. Value-style streams (index values, basis, forming bars) are limited to 60 events per second per symbol. The futures quote, depth and bar streams, the index value stream and the basis stream send the latest state right after you subscribe. Trade streams start with the next trade.
- After a disconnect there is no replay. Reconnect, resubscribe and use the REST endpoints for anything you missed. A
gapmessage tells you when continuity was lost while connected. - Cancelled option prints are not retracted on the stream. Reconcile through the REST trades endpoint, which leaves cancelled prints out.
- Symbols are validated per stream. A family symbol (
SPX,NDX,RUT,VIX) also selects its weekly root (SPXW,NDXP,RUTW,VIXW), and a futures product (ES) selects its front contract, so on one socket you cannot subscribe to both a family and one of its members (overlapping_symbols). Option-stream symbols are option roots.
Error messages#
The code of an error message is one of: invalid_message, unknown_action, invalid_symbol, too_many_symbols, wildcard_not_allowed, overlapping_symbols, invalid_filter, symbol_unavailable (the symbol cannot be served on this stream), capacity and account_capacity (too many subscriptions in the service, or held by your account alone), and inbound_rate_limited.
Keepalive and limits#
- The server pings every 10 seconds and closes a connection that has not answered (or sent any frame) within 20 seconds. Standard WebSocket libraries answer pings automatically.
- Client messages are limited to 4 KiB and 10 per second. More closes the connection with
1008. - At most 100 symbols per connection, fewer on some streams (see the table below). A stream that accepts
*takes it only as the sole subscription. - Each connection has an outbound queue of 4,096 messages or 4 MiB. A client that cannot keep up is closed with
4008 slow_consumerinstead of silently losing events. - Your key, project, account and plan are re-checked every 30 seconds.
Close codes#
| Code | Reason | Meaning |
|---|---|---|
| 1000, 1001 | server_shutdown | Normal close, or the service is restarting. Reconnect. |
| 1008 | inbound_rate_limited | You sent more than 10 messages a second. |
| 1009 | message_too_big | A client message was larger than 4 KiB. |
| 1013 | cooldown retry_after_ms=N, state_unavailable | Try again later. For a cooldown the reason says when. |
| 4008 | slow_consumer | The connection fell too far behind. |
| 4401 | unauthorized | The key was revoked or the account became unavailable. |
| 4403 | forbidden, plan_changed, account_suspended | Your plan, a dataset release or your standing no longer allows the stream. |
Streams#
Each stream has a page in the reference with its filters, the event schema and an example event. The symbol limit is per stream.
| Stream | Event type | Delivers | Symbols | Filters |
|---|---|---|---|---|
| / | options.flow | Consolidated sweeps, splits and blocks. | up to 100 | contract_typesidemin_sizemin_premiummax_premiumtrade_typesentimentmin_dtemax_dte |
| / | options.trades | Every classified option print. | up to 100 or * | contract_typesidemin_sizemin_premiummax_premium |
| / | options.unusual | Unusual option activity. | up to 100 | contract_typesidemin_sizemin_premiummax_premiumtrade_typesentimentmin_dtemax_dte |
| / | futures.bars | Futures one-minute bars. | up to 25 | none |
| / | futures.depth | Futures market depth. | up to 10 | none |
| / | futures.quotes | Futures best bid and offer. | up to 25 | none |
| / | futures.trades | Futures trades. | up to 25 | min_sizeside |
| / | indices.basis | Index versus futures-implied value. | up to 10 | none |
| / | indices.values | Cash index values. | up to 100 | none |
| / | stocks.bars | Stock second and minute bars. | up to 100 | interval |
| / | stocks.blocks | Large stock trades. | up to 100 | min_sizemin_notional |
| / | stocks.dark_pool | Off-exchange stock trades. | up to 100 | min_sizemin_notional |
| / | stocks.imbalances | Exchange auction imbalances. | up to 100 or * | none |
| / | stocks.luld | Limit up-limit down bands and halts. | up to 100 | none |
| / | stocks.quotes | Stock national best bid and offer. | up to 50 | none |
| / | stocks.trades | Stock trades. | up to 50 | min_sizemin_notional |
| / | analytics | Snapshots of streamable endpoints. | 20 subscriptions | none |
Analytics snapshots#
/v1/ws/analytics pushes the complete response of any REST endpoint marked streamable whenever its data changes. Each subscription names an endpoint and its ordinary query parameters:
Subscribe
{"action":"subscribe","id":"levels","route":"/v1/options/levels","params":{"underlying_symbol":"SPX"},"interval_ms":5000}The hello message lists the streamable endpoints with their minimum refresh interval (routes[].min_interval_ms, from half a second to five seconds) and max_subscriptions (20). The server answers with subscribed (the subscriptions with the interval applied), then sends events:
Event
{"type":"event","event_type":"analytics","seq":1,"data":{"id":"levels","route":"/v1/options/levels","revision":"1","resync":true,"response":{"data":{},"meta":{}}}}responseis the endpoint's usual{data, meta}envelope, always the full snapshot, never a delta.revisionincreases with each change of the data, andresyncistrueon the first snapshot after subscribing. Send{"action":"unsubscribe","id":"levels"}to stop one subscription (noidstops all).idis yours: up to 64 letters, digits,_,-,.,:. Re-subscribing with the sameidreplaces the subscription.interval_mscan only slow a subscription down (up to one hour), never below the endpoint's minimum.- An account's subscriptions together may refresh at most 10 times a second (the sum of 1/interval). Beyond that the subscribe is refused with
poll_budget_exceeded: choose longer intervals. Subscriptions with the same endpoint and parameters share one refresh. - A paginated endpoint streams its first page:
has_moreis reported andnext_cursor_idandnext_urlarenull. Results larger than 4 MiB are not streamed (response_too_large). - Under heavy load a refresh can be skipped and retried about one interval later, so a snapshot may arrive late. After several skipped refreshes in a row the server sends a
statusmessage withstate: degradedandreason: refresh_delayed, andstate: livewhen refreshes resume. Load never produces anerrormessage. - Other analytics errors:
invalid_route(not streamable; see /v1/catalog),too_many_subscriptionsandinvalid_filter(badparams).
Streamable endpoints#
| Endpoint | Returns |
|---|---|
| / | Contracts of an underlying: those listed now, or those quoted on a past date. |
| / | The option chain: quotes, day statistics, open interest and Greeks of every contract. |
| / | Call and put premium, volume and net flow for each expiration. |
| / | Call and put premium and volume at every strike, by aggressor class. |
| / | Market share of option premium, volume and trades by exchange. |
| / | Underlyings or contracts leading option flow: top premium, bullish, bearish and movers. |
| / | Net call and put premium per bucket and cumulatively over the session. |
| / | Net premium or net volume per bucket for calls, puts and overall. |
| / | Overnight (global trading hours) option prints. |
| / | Cumulative signed premium of calls, puts and net sentiment over the session. |
| / | Put/call volume and premium ratios per bucket and cumulatively. |
| / | Premium, volume and trades of a filtered option print set, by contract type and aggressor side. |
| / | Executed premium and trade counts per strike (SPX and SPXW). |
| / | Modeled dealer gamma, delta, vanna or charm exposure by expiration. |
| / | Modeled dealer gamma, delta, vanna or charm exposure by strike. |
| / | Modeled levels repriced at intraday horizons with a statistical cone. |
| / | Strike by expiration grid of modeled exposure, open interest or flow. |
| / | Modeled exposure by strike at every interval of a session. |
| / | Modeled exposure repriced over a sweep of underlying prices, with zero crossings. |
| / | Dense strike by expiration surface of net modeled exposure for 3D views. |
| / | Key levels of the modeled dealer exposure: walls, gamma flip, largest cell, max pain, expected move. |
| / | Modeled exposure and executed flow by strike for SPX and SPXW. |
| / | Open interest by strike or by expiration. |
| / | Intraday implied volatility against adjusted realized volatility. |
| / | Implied volatility smile by strike and delta, with risk reversals and butterflies. |
| / | Implied volatility surface by strike and expiration. |
| / | One strike across expirations, or one expiration across strikes, of the volatility surface. |
| / | At-the-money implied volatility by expiration, constant-maturity points and forward volatilities. |
| / | The current displayed depth of a futures contract. |
| / | Spread and displayed liquidity of a futures contract. |
| / | Last trade, top of book and session OHLCV of a futures contract. |
| / | Cumulative volume delta of a futures session. |
| / | Buy, sell and net volume per bar. |
| / | Footprint bars: volume by price inside each bar, with imbalances. |
| / | Aggregated futures prints (order-flow bubbles) for a window. |
| / | Volume profile of the whole current trading session (POC, VAH, VAL). |
| / | Volume by price for a window, with point of control and value area. |
| / | Current value and session range of up to 50 indices. |
| / | Live basis, fair value and premium of up to 10 indices. |
| / | Top 20 gainers or losers of the day. |
| / | Current-day snapshot of up to 50 stocks. |
| / | One-call picture of the market: indexes, futures, volatility and breadth. |
| / | Trading state of the US equity market and health of the data areas. |