Skip to main content

WebSocket API

Real-time order book, trades, candles, tickers and private account channels over Socket.IO.

The real-time API uses Socket.IO (v4), not a raw WebSocket, so connect with a Socket.IO client.

URLwss://arc-testnet-api.realtime.exchange
Path/socket.io
import { io } from 'socket.io-client';

const socket = io(WSS_URL, { path: '/socket.io', transports: ['websocket'] });

socket.on('connected', (info) => console.log('connected', info.id));
socket.on('orderbook', (msg) => console.log(msg.data.bids, msg.data.asks));
socket.emit('subscribe', 'orderbook:BTC-PERP');

Subscribing​

SendTo
socket.emit('subscribe', '<channel>')subscribe to one channel
socket.emit('subscribe', { channel, params })subscribe with parameters
socket.emit('message', { op: 'subscribe', data: ['<channel>', …] })subscribe to several channels at once
socket.emit('unsubscribe', '<channel>')leave a channel
socket.emit('message', { op: 'auth', data: { key, secret } })authenticate for private channels (Authentication)

The server first sends the channel's current state, then a subscribed acknowledgement. Updates follow on an event named after the channel kind (orderbook, trades, candles, ticker, index, markprice, positions, orders, …). The exception is ticker-frontend:*, which receives ticker events. unsubscribe is acknowledged with unsubscribed.

  • Initial state:
    • orderbook and candles send a snapshot (data.type: "snapshot"; candles send up to 500 bars).
    • trades replays up to 50 recent trades as individual trades messages.
  • Message shape:
    • Most messages are { channel, data, timestamp }.
    • orderbook, trades and candles messages add market (candles also add resolution); per-market ticker messages add market set to the instrument id; index and markprice messages carry only channel, data and timestamp. Private channels add account.
    • On private channels, channel is the bare kind (for example "positions"). The exception is notifications, whose channel is the full room notifications:{chainId}:{account}; its snapshot is data: {snapshot: [latest 20]}.
    • ticker:all messages are { data, timestamp }.
  • How updates arrive: public channels are pushed on a timer: orderbook every 2 s and ticker/index/markprice every 5 s as full state, and trades every 3 s with only the trades not sent before. Private channels receive two kinds of message on the same event:
    • Periodic full state: orders every 3 s; positions, account, portfolio and the history channels every 10 s.
    • Event messages as soon as the indexer reports a change: positions, account and portfolio receive data: {action, sourceAction, position} (action is position_opened, position_updated, position_closed or liquidated), and position-history receives the same message for position_closed and liquidated; orders and order-history receive data: {action, order}. Check for data.action before treating a message as full state.
    • candles: on each fill. A live update carries one bar in data; several can share the same time as the bar grows, so replace the bar with that time rather than appending.
  • Errors while subscribing: after SNAPSHOT_FAILED you stay subscribed (live updates follow). After any other error code (for example UNKNOWN_INSTRUMENT, INVALID_RESOLUTION, INVALID_CHANNEL, AUTH_REQUIRED, ACCOUNT_MISMATCH, SUBSCRIPTION_LIMIT, RATE_LIMITED) you are not subscribed. The subscribed / unsubscribed acks also carry kind and requested.
  • Browser origins: connections from a browser are accepted only from the deployment's allowed origins.
  • Reconnecting: authentication and subscriptions belong to one connection and are not restored by Socket.IO. On every (re)connect, send op: auth again, then resubscribe. Each subscribe re-sends the snapshot, so replace your local state with it.

Channel names are normalised: 0x addresses are lowercased, and symbols and assets are uppercased. So btc-perp and BTC-PERP are the same channel. A channel name longer than 128 characters is refused with INVALID_CHANNEL.

Public channels​

ChannelContent
orderbook:{MARKET}Order book snapshot {type: "snapshot", bids, asks}
trades:{MARKET} or trades:allEach new trade: {id, market, price, amount, side, is_buyer_taker, timestamp, tx_hash}
candles:{MARKET}:{RESOLUTION}Candle snapshot, then live updates. Resolutions: 1, 5, 15, 30, 60, 120, 240, 1D, 1W, 1M
ticker:{MARKET} or ticker:allTicker updates
ticker:{ASSET}:{TYPE}, ticker-frontend:{ASSET}:{TYPE}Ticker for an asset and instrument type, e.g. ticker:BTC:PERPETUAL
index:{ASSET}Index price
markprice:{ASSET}:PERPETUALMark price
Units

orderbook levels (price, amount/quantity) and the price in trades messages are raw on-chain integers: divide prices by 10^quote_decimals and sizes by 10^base_decimals from GET /markets (both are 18 for perpetuals). A trade's amount is already scaled. A trade's data.timestamp is Unix seconds, while the message's outer timestamp is milliseconds. data.market is the on-chain pool address; the message's outer market is the channel's market.

Private channels​

Authenticate first. You can only subscribe to your own account; any other account is refused with ACCOUNT_MISMATCH. You may omit the account (for example positions); the authenticated account is used.

account:{account}, portfolio:{account}, orders:{account}, positions:{account}, order-history:{account}, trade-history:{account}, position-history:{account}, and notifications:{account}.

Errors​

Errors arrive on the error event: { code, message }, plus, where relevant, channel, kind, requested (an echo of what you sent), market, resolution, retryAfterMs or limit.

CodeDefault message
INVALID_CHANNELInvalid channel
UNKNOWN_CHANNELUnknown channel
INVALID_PARAMSInvalid parameters
AUTH_REQUIREDAuthentication required for private channel
ACCOUNT_MISMATCHCannot subscribe to another account
UNKNOWN_INSTRUMENTUnknown instrument
INVALID_RESOLUTIONUnsupported resolution
SNAPSHOT_FAILEDFailed to fetch snapshot
RATE_LIMITEDToo many requests
SUBSCRIPTION_LIMITSubscription limit reached
REQUEST_CANCELLEDSubscribe request cancelled
INTERNALInternal error

Limits​

Default limits:

LimitDefault
Subscriptions per connection50
Subscribe requests per second per connection (sustained; each channel in a bulk message counts)10
Subscribe burst allowance per connection30
Channels per bulk subscribe message20
Connections per IP address50
Maximum message size (bytes)16000

Subscription and connection limits run in observe mode by default: breaches are counted, not refused, until the operator switches to enforce. The message size limit is always enforced: a larger message closes the connection. Build your client to stay within all the limits.