Endpoint
- URL:
wss://stream-perps-prod-eu.mobula.io/ - Event type:
stream(in the subscribe envelope)
Subscribe
Parameters
type(required) — must be"stream".authorization(required) — your Mobula server-side API key.payload.name(required) — client-provided subscription name. Echoed back on the ack frame and useful for log correlation.payload.chainIds(required) — array of chain ids to subscribe to. See Chain coverage below.payload.events(required) — array of event categories."order"covers all perp trade-lifecycle events listed in Event types below.payload.subscriptionTracking(optional) — string"true"(not a boolean). When set, the ack frame includes asubscriptionIdyou can use to demux multiple subscriptions on a single socket.
Subscribe acknowledgement
Server push
Each event is one frame. The top-level envelope wraps the event-specificdata:
data.traderAddressis lowercased on the wire — match accordingly when filtering by wallet.duplicateCountis informational; identical frames may still be delivered on reconnect or for Gains’TRADE_STORED+MARKET_BUYpair on the same transaction. Deduplicate on the client with a key like(transactionHash, type, traderAddress, tradeId).
Event types
For the field reference of each event category (
PerpOrderExecuted, PerpTpSlUpdate, PerpUncategorizedEvent) including extra-field shapes, see the Perpetuals data model.
Gains “two frames per trade” caveat
For every Gains market action, you will receive two events back-to-back with overlapping but different field sets — these are not duplicates:TRADE_STORED—{ market, exchange, chainId, traderAddress, type, tradeId, transactionHash, extra: { tradeType, leverage, long, collateralAmount, index, openPrice, tp, sl }, date, ... }MARKET_BUY(orMARKET_SELL) —{ market, exchange, chainId, type, priceQuote, priceUSD, blockNumber, baseAmountRaw, tradeId, collateralAmountRaw, collateralAsset, leverage, traderAddress, takeProfitUSD, stopLossUSD, extra: { open, amountSentToTrader, trade: { long, index, tradeType, openPrice, collateralIndex } }, transactionHash, date, ... }
(transactionHash, type, traderAddress, tradeId) — distinguishes the two without dropping one of them.
Chain coverage
Lighter on this stream is
lighter:304. The sibling Perp Positions WSS uses lighter:301. Subscribing to events with lighter:301 returns no Lighter frames at all — it is silently dropped.Envelope shape — events vs positions
The perp events stream and the perp positions WSS use different envelope shapes:Heartbeat & idle timeout
The server expectsping frames to keep the socket alive:
{ "event": "pong" }. The idle timeout is short (~10 s) before a subscription is sent — make sure your first frame after open is a subscribe, not silence. Once subscribed, 30 s+ between pings has been observed to work.
Lighter “hawk” subscribers
LighterMARKET_* / liquidation / TP / SL frames may include an activePosition / historicalPosition block embedded on the event with the full position state at the time of the event. Raw (non-hawk) subscribers receive a smaller payload without this block.
Implementation example
Related
Perp Positions Stream
Live snapshot of open positions + unfilled orders.
Perpetuals Data Model
Per-event field reference (executed / TP-SL / uncategorized).
Execution Cookbook
Pair with this stream to learn that your order filled.
Funding Stream
Real-time funding rates across CeFi + DeFi exchanges.