Skip to content

Private user data stream

Evidence: UserDataApiController, WS_API_SPOT.md private section, Postman ListenKey / OpenAPI-WS.

listenKey REST

Method Path Auth
POST /openapi/v1/userDataStream API key only (validKeyOnly)
PUT /openapi/v1/userDataStream?listenKey=... API key only
DELETE /openapi/v1/userDataStream?listenKey=... API key only

Keepalive: call PUT every 30 minutes (guidance from WS_API_SPOT.md).

Connect

wss://<host>/openapi/ws/<listenKey>/spot

Invalid / expired key → HTTP 401 at handshake. After connect, the gateway auto-subscribes the private channel — no SUBSCRIBE required.

Events (illustrative from WS_API_SPOT.md)

Order update — orderReport

{
  "e": "orderReport",
  "i": 4293153,
  "c": "my-order-id",
  "s": "BTC_USDT",
  "S": "BUY",
  "o": "LIMIT",
  "oO": "LIMIT",
  "f": "GTC",
  "p": "0.10264410",
  "aP": "0.00000000",
  "q": "1.00000000",
  "a": "0.10264410",
  "z": "0.00000000",
  "Z": "0.00000000",
  "X": "NEW",
  "O": 1672515782136,
  "P": "0.00000000",
  "tT": 0,
  "tC": "",
  "r": ""
}

Statuses: NEW · PARTIALLY_FILLED · FILLED · CANCELED · EXPIRED

[NEEDS-CONFIRMATION: confirm symbol separator BTC_USDT vs BTCUSDT in production payloads]

Trade update — tradeReport

{
  "e": "tradeReport",
  "t": 987654,
  "s": "BTC_USDT",
  "i": 4293153,
  "l": "0.50000000",
  "L": "0.10264410",
  "Y": "0.05132205",
  "N": "USDT"
}

Balance update — balanceReport

{
  "e": "balanceReport",
  "B": [{ "a": "BTC", "f": "0.50000000", "l": "0.00000000", "t": "0.50000000" }]
}

WS error frame

{ "code": 401, "msg": "invalid or expired listenKey", "id": null }

Codes documented in WS guide: 400, 401, 429 (not the same namespace as REST ApiErrorCode).