KuCoin · AsyncAPI Specification
KuCoin Public WebSocket API
Version 1.0.0
AsyncAPI 2.6 description of KuCoin's public WebSocket streaming API for the Classic (Spot/Margin) account. ## Obtaining the connection endpoint The WebSocket endpoint and a short-lived bearer token are NOT statically documented. A client must first call the REST endpoint: `POST https://api.kucoin.com/api/v1/bullet-public` The response contains a `token` and an `instanceServers[]` array. Each `instanceServer` includes: | Field | Type | Notes | |-------|------|-------| | `endpoint` | string | The wss URL (e.g. `wss://ws-api-spot.kucoin.com/`) | | `protocol` | string | `websocket` | | `encrypt` | boolean | `true` | | `pingInterval` | integer | Milliseconds between client pings (e.g. 18000) | | `pingTimeout` | integer | Milliseconds to wait for pong before disconnecting (e.g. 10000) | Example response (truncated): ```json { "code": "200000", "data": { "token": "2neAi...", "instanceServers": [ { "pingInterval": 18000, "endpoint": "wss://ws-api-spot.kucoin.com/", "protocol": "websocket", "encrypt": true, "pingTimeout": 10000 } ] } } ``` The client then opens the WebSocket using the URL: `{endpoint}?token={token}&connectId={uniqueId}` The connection becomes active only after the server sends a `welcome` message. The client must send a `ping` envelope every `pingInterval` milliseconds and consider the connection dead if no `pong` arrives within `pingTimeout`. Tokens are valid for 24 hours; clients should refresh and reconnect before expiry. Sources: - https://www.kucoin.com/docs/websocket/basic-info/introduction - https://www.kucoin.com/docs-new/websocket-api/base-info/introduction - https://www.kucoin.com/docs/rest/websocket/get-public-token
/
publish clientToServer
Control and heartbeat messages sent by the client.
Single multiplexed WebSocket connection. All KuCoin streaming traffic flows over one connection; topics are subscribed/unsubscribed via JSON control messages and inbound messages are routed by their `topic` and `subject` fields.
asyncapi: '2.6.0'
info:
title: KuCoin Public WebSocket API
version: '1.0.0'
description: |
AsyncAPI 2.6 description of KuCoin's public WebSocket streaming API
for the Classic (Spot/Margin) account.
## Obtaining the connection endpoint
The WebSocket endpoint and a short-lived bearer token are NOT statically
documented. A client must first call the REST endpoint:
`POST https://api.kucoin.com/api/v1/bullet-public`
The response contains a `token` and an `instanceServers[]` array. Each
`instanceServer` includes:
| Field | Type | Notes |
|-------|------|-------|
| `endpoint` | string | The wss URL (e.g. `wss://ws-api-spot.kucoin.com/`) |
| `protocol` | string | `websocket` |
| `encrypt` | boolean | `true` |
| `pingInterval` | integer | Milliseconds between client pings (e.g. 18000) |
| `pingTimeout` | integer | Milliseconds to wait for pong before disconnecting (e.g. 10000) |
Example response (truncated):
```json
{
"code": "200000",
"data": {
"token": "2neAi...",
"instanceServers": [
{
"pingInterval": 18000,
"endpoint": "wss://ws-api-spot.kucoin.com/",
"protocol": "websocket",
"encrypt": true,
"pingTimeout": 10000
}
]
}
}
```
The client then opens the WebSocket using the URL:
`{endpoint}?token={token}&connectId={uniqueId}`
The connection becomes active only after the server sends a `welcome`
message. The client must send a `ping` envelope every `pingInterval`
milliseconds and consider the connection dead if no `pong` arrives within
`pingTimeout`.
Tokens are valid for 24 hours; clients should refresh and reconnect before
expiry.
Sources:
- https://www.kucoin.com/docs/websocket/basic-info/introduction
- https://www.kucoin.com/docs-new/websocket-api/base-info/introduction
- https://www.kucoin.com/docs/rest/websocket/get-public-token
contact:
name: KuCoin API Support
url: https://www.kucoin.com/docs-new
license:
name: KuCoin Terms of Service
url: https://www.kucoin.com/legal/terms-of-use
termsOfService: https://www.kucoin.com/legal/terms-of-use
defaultContentType: application/json
servers:
bulletPublic:
url: 'ws-api-spot.kucoin.com'
protocol: wss
description: |
Classic Spot/Margin public WebSocket endpoint.
The actual endpoint is returned by `POST /api/v1/bullet-public` and
must be appended with `?token={token}&connectId={uniqueId}`.
variables:
token:
description: Short-lived public token issued by /api/v1/bullet-public.
default: REPLACE_WITH_TOKEN
connectId:
description: Unique client-generated connection identifier.
default: REPLACE_WITH_CONNECT_ID
channels:
'/':
description: |
Single multiplexed WebSocket connection. All KuCoin streaming traffic
flows over one connection; topics are subscribed/unsubscribed via JSON
control messages and inbound messages are routed by their `topic` and
`subject` fields.
publish:
operationId: clientToServer
summary: Control and heartbeat messages sent by the client.
message:
oneOf:
- $ref: '#/components/messages/SubscribeMessage'
- $ref: '#/components/messages/UnsubscribeMessage'
- $ref: '#/components/messages/PingMessage'
subscribe:
operationId: serverToClient
summary: Server-pushed envelopes (welcome, ack, pong, market data, private data).
message:
oneOf:
- $ref: '#/components/messages/WelcomeMessage'
- $ref: '#/components/messages/AckMessage'
- $ref: '#/components/messages/PongMessage'
- $ref: '#/components/messages/ErrorMessage'
- $ref: '#/components/messages/SymbolTicker'
- $ref: '#/components/messages/AllSymbolsTicker'
- $ref: '#/components/messages/SymbolSnapshot'
- $ref: '#/components/messages/MarketSnapshot'
- $ref: '#/components/messages/Level1BBO'
- $ref: '#/components/messages/Level2Update'
- $ref: '#/components/messages/Level2Depth5'
- $ref: '#/components/messages/Level2Depth50'
- $ref: '#/components/messages/Klines'
- $ref: '#/components/messages/Match'
- $ref: '#/components/messages/IndexPrice'
- $ref: '#/components/messages/MarkPrice'
- $ref: '#/components/messages/TradeOrders'
- $ref: '#/components/messages/TradeOrdersV2'
- $ref: '#/components/messages/AccountBalance'
- $ref: '#/components/messages/CrossMarginPosition'
- $ref: '#/components/messages/IsolatedMarginPosition'
components:
messages:
SubscribeMessage:
name: subscribe
title: Subscribe
summary: Subscribe to a topic. Some topics support a comma-separated list of symbols (up to 100).
payload:
$ref: '#/components/schemas/SubscribeEnvelope'
UnsubscribeMessage:
name: unsubscribe
title: Unsubscribe
summary: Unsubscribe from a topic.
payload:
$ref: '#/components/schemas/UnsubscribeEnvelope'
PingMessage:
name: ping
title: Ping
summary: Heartbeat. Must be sent every `pingInterval` ms returned by /api/v1/bullet-public.
payload:
$ref: '#/components/schemas/PingEnvelope'
WelcomeMessage:
name: welcome
title: Welcome
summary: First message from server after the WebSocket handshake. The connection is only usable after this message is received.
payload:
$ref: '#/components/schemas/WelcomeEnvelope'
AckMessage:
name: ack
title: Subscribe/Unsubscribe Acknowledgement
summary: 'Sent when the client requested `"response": true` on subscribe/unsubscribe.'
payload:
$ref: '#/components/schemas/AckEnvelope'
PongMessage:
name: pong
title: Pong
summary: Reply to a client ping.
payload:
$ref: '#/components/schemas/PongEnvelope'
ErrorMessage:
name: error
title: Error
summary: Server-side error envelope.
payload:
$ref: '#/components/schemas/ErrorEnvelope'
SymbolTicker:
name: symbolTicker
title: 'Public: Symbol Ticker'
summary: 'Topic: `/market/ticker:{symbol}` — pushed at most once every 100ms.'
payload:
$ref: '#/components/schemas/SymbolTickerEnvelope'
AllSymbolsTicker:
name: allSymbolsTicker
title: 'Public: All Symbols Ticker'
summary: 'Topic: `/market/ticker:all` — ticker updates for every symbol, subject is the symbol code.'
payload:
$ref: '#/components/schemas/AllSymbolsTickerEnvelope'
SymbolSnapshot:
name: symbolSnapshot
title: 'Public: Symbol Snapshot'
summary: 'Topic: `/market/snapshot:{symbol}` — pushed every 2s.'
payload:
$ref: '#/components/schemas/SymbolSnapshotEnvelope'
MarketSnapshot:
name: marketSnapshot
title: 'Public: Market Snapshot'
summary: 'Topic: `/market/snapshot:{market}` — pushed every 2s for every symbol in a market (e.g. `BTC`, `USDS`).'
payload:
$ref: '#/components/schemas/MarketSnapshotEnvelope'
Level1BBO:
name: level1
title: 'Public: Orderbook - Best Bid/Ask (Level 1)'
summary: 'Topic: `/spotMarket/level1:{symbol}` — pushed at most once every 10ms when top-of-book changes.'
payload:
$ref: '#/components/schemas/Level1Envelope'
Level2Update:
name: level2
title: 'Public: Orderbook - Incremental Updates (Level 2)'
summary: 'Topic: `/market/level2:{symbol}` — real-time incremental updates. Subject `trade.l2update`.'
payload:
$ref: '#/components/schemas/Level2Envelope'
Level2Depth5:
name: level2Depth5
title: 'Public: Orderbook - Top 5 (Level 2 Depth 5)'
summary: 'Topic: `/spotMarket/level2Depth5:{symbol}` — pushed at most once every 100ms.'
payload:
$ref: '#/components/schemas/Level2DepthEnvelope'
Level2Depth50:
name: level2Depth50
title: 'Public: Orderbook - Top 50 (Level 2 Depth 50)'
summary: 'Topic: `/spotMarket/level2Depth50:{symbol}` — pushed at most once every 100ms.'
payload:
$ref: '#/components/schemas/Level2DepthEnvelope'
Klines:
name: klines
title: 'Public: Klines (Candles)'
summary: 'Topic: `/market/candles:{symbol}_{type}` — real-time OHLCV updates. Type is one of 1min,3min,15min,30min,1hour,2hour,4hour,6hour,8hour,12hour,1day,1week.'
payload:
$ref: '#/components/schemas/KlinesEnvelope'
Match:
name: match
title: 'Public: Match Execution Data'
summary: 'Topic: `/market/match:{symbol}` — real-time Level 3 trade matches. Subject `trade.l3match`.'
payload:
$ref: '#/components/schemas/MatchEnvelope'
IndexPrice:
name: indexPrice
title: 'Public: Index Price'
summary: 'Topic: `/indicator/index:{symbol}` — pushed once per second.'
payload:
$ref: '#/components/schemas/IndexPriceEnvelope'
MarkPrice:
name: markPrice
title: 'Public: Mark Price'
summary: 'Topic: `/indicator/markPrice:{symbol}` — pushed once per second.'
payload:
$ref: '#/components/schemas/MarkPriceEnvelope'
TradeOrders:
name: tradeOrders
title: 'Private: Trade Orders V1'
summary: 'Topic: `/spotMarket/tradeOrders` — real-time order lifecycle events. Requires private token.'
payload:
$ref: '#/components/schemas/TradeOrdersEnvelope'
TradeOrdersV2:
name: tradeOrdersV2
title: 'Private: Trade Orders V2'
summary: 'Topic: `/spotMarket/tradeOrdersV2` — real-time order lifecycle events with additional update events. Requires private token.'
payload:
$ref: '#/components/schemas/TradeOrdersV2Envelope'
AccountBalance:
name: accountBalance
title: 'Private: Account Balance'
summary: 'Topic: `/account/balance` — real-time balance changes. Requires private token.'
payload:
$ref: '#/components/schemas/AccountBalanceEnvelope'
CrossMarginPosition:
name: crossMarginPosition
title: 'Private: Cross Margin Position'
summary: 'Topic: `/margin/position` — debt ratio and position status updates. Requires private token.'
payload:
$ref: '#/components/schemas/CrossMarginPositionEnvelope'
IsolatedMarginPosition:
name: isolatedMarginPosition
title: 'Private: Isolated Margin Position'
summary: 'Topic: `/margin/isolatedPosition:{symbol}` — isolated margin position changes. Requires private token.'
payload:
$ref: '#/components/schemas/IsolatedMarginPositionEnvelope'
schemas:
# ---------- Control / heartbeat envelopes ----------
SubscribeEnvelope:
type: object
required: [id, type, topic]
properties:
id:
type: string
description: Client-generated message id (echoed in the ack).
type:
type: string
const: subscribe
topic:
type: string
description: Topic to subscribe to (e.g. `/market/ticker:BTC-USDT`).
example: /market/ticker:BTC-USDT
privateChannel:
type: boolean
default: false
description: Set to true for private topics that require a private token.
response:
type: boolean
default: false
description: 'If true, the server returns an `ack` envelope with the same id.'
UnsubscribeEnvelope:
type: object
required: [id, type, topic]
properties:
id:
type: string
type:
type: string
const: unsubscribe
topic:
type: string
privateChannel:
type: boolean
response:
type: boolean
PingEnvelope:
type: object
required: [id, type]
properties:
id:
type: string
description: Arbitrary client id used to correlate with the pong.
type:
type: string
const: ping
WelcomeEnvelope:
type: object
required: [id, type]
properties:
id:
type: string
type:
type: string
const: welcome
example:
id: hQvf8jkno
type: welcome
AckEnvelope:
type: object
required: [id, type]
properties:
id:
type: string
type:
type: string
const: ack
example:
id: '1545910660739'
type: ack
PongEnvelope:
type: object
required: [id, type]
properties:
id:
type: string
type:
type: string
const: pong
timestamp:
type: integer
description: Server timestamp (microseconds).
example:
id: '1545910590801'
type: pong
timestamp: 1764215232226553
ErrorEnvelope:
type: object
required: [id, type]
properties:
id:
type: string
type:
type: string
const: error
code:
type: integer
data:
type: string
# ---------- Generic data envelope ----------
DataEnvelopeBase:
type: object
required: [type, topic, subject, data]
properties:
id:
type: string
type:
type: string
const: message
topic:
type: string
subject:
type: string
sn:
type: integer
description: Sequence number, present on some channels.
# ---------- Public market data payloads ----------
SymbolTickerEnvelope:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /market/ticker:BTC-USDT
subject:
type: string
example: trade.ticker
data:
$ref: '#/components/schemas/SymbolTickerData'
SymbolTickerData:
type: object
properties:
sequence: { type: string }
price: { type: string, description: Most recent transaction price. }
size: { type: string, description: Most recent transaction size. }
bestAsk: { type: string }
bestAskSize: { type: string }
bestBid: { type: string }
bestBidSize: { type: string }
time: { type: integer, description: Match timestamp (milliseconds). }
AllSymbolsTickerEnvelope:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /market/ticker:all
subject:
type: string
description: Symbol code, e.g. `BTC-USDT`.
example: BTC-USDT
data:
$ref: '#/components/schemas/SymbolTickerData'
SymbolSnapshotEnvelope:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /market/snapshot:BTC-USDT
subject:
type: string
example: trade.snapshot
data:
type: object
properties:
sequence: { type: string }
data:
$ref: '#/components/schemas/SnapshotData'
MarketSnapshotEnvelope:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /market/snapshot:BTC
subject:
type: string
example: trade.snapshot
data:
type: object
properties:
sequence: { type: integer }
data:
$ref: '#/components/schemas/SnapshotData'
SnapshotData:
type: object
description: Snapshot detail object pushed every 2 seconds.
properties:
askSize: { type: number }
averagePrice: { type: number }
baseCurrency: { type: string }
bidSize: { type: number }
board: { type: integer }
buy: { type: number }
changePrice: { type: number }
changeRate: { type: number }
close: { type: number }
datetime: { type: integer, description: Timestamp (milliseconds). }
high: { type: number }
lastTradedPrice: { type: number }
low: { type: number }
makerCoefficient: { type: number }
makerFeeRate: { type: number }
marginTrade: { type: boolean }
mark: { type: integer }
market: { type: string }
marketChange1h:
$ref: '#/components/schemas/MarketChangeWindow'
marketChange4h:
$ref: '#/components/schemas/MarketChangeWindow'
marketChange24h:
$ref: '#/components/schemas/MarketChangeWindow'
markets:
type: array
items: { type: string }
open: { type: number }
quoteCurrency: { type: string }
sell: { type: number }
siteTypes:
type: array
items: { type: string }
sort: { type: integer }
symbol: { type: string }
symbolCode: { type: string }
takerCoefficient: { type: number }
takerFeeRate: { type: number }
trading: { type: boolean }
vol: { type: number }
volValue: { type: number }
MarketChangeWindow:
type: object
properties:
changePrice: { type: number }
changeRate: { type: number }
high: { type: number }
low: { type: number }
open: { type: number }
vol: { type: number }
volValue: { type: number }
Level1Envelope:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /spotMarket/level1:BTC-USDT
subject:
type: string
example: level1
data:
type: object
properties:
asks:
type: array
description: '[price, size] for the best ask.'
items: { type: string }
minItems: 2
maxItems: 2
bids:
type: array
description: '[price, size] for the best bid.'
items: { type: string }
minItems: 2
maxItems: 2
timestamp: { type: integer }
Level2Envelope:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /market/level2:BTC-USDT
subject:
type: string
example: trade.l2update
data:
type: object
properties:
symbol: { type: string }
sequenceStart: { type: integer }
sequenceEnd: { type: integer }
time: { type: integer }
changes:
type: object
properties:
asks:
type: array
description: '[price, size, sequence] tuples. A size of 0 means the price level was removed.'
items:
type: array
items: { type: string }
minItems: 3
maxItems: 3
bids:
type: array
items:
type: array
items: { type: string }
minItems: 3
maxItems: 3
Level2DepthEnvelope:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
subject:
type: string
example: level2
data:
type: object
properties:
asks:
type: array
items:
type: array
items: { type: string }
minItems: 2
maxItems: 2
bids:
type: array
items:
type: array
items: { type: string }
minItems: 2
maxItems: 2
timestamp: { type: integer }
KlinesEnvelope:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /market/candles:BTC-USDT_1hour
subject:
type: string
example: trade.candles.update
data:
type: object
properties:
symbol:
type: string
candles:
type: array
description: '[startTime, open, close, high, low, volume, amount] as strings.'
items: { type: string }
minItems: 7
maxItems: 7
time:
type: integer
description: Push timestamp (nanoseconds).
MatchEnvelope:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /market/match:BTC-USDT
subject:
type: string
example: trade.l3match
data:
type: object
properties:
symbol: { type: string }
sequence: { type: string }
side:
type: string
enum: [buy, sell]
size: { type: string }
price: { type: string }
takerOrderId: { type: string }
makerOrderId: { type: string }
tradeId: { type: string }
type:
type: string
description: Always `match` for this stream.
time:
type: string
description: Match time (nanoseconds, as a string).
IndexPriceEnvelope:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /indicator/index:USDT-BTC
subject:
type: string
example: tick
data:
type: object
properties:
symbol: { type: string }
granularity: { type: integer, description: Granularity in milliseconds. }
timestamp: { type: integer }
value: { type: number }
MarkPriceEnvelope:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /indicator/markPrice:USDT-BTC
subject:
type: string
example: tick
data:
type: object
properties:
symbol: { type: string }
granularity: { type: integer }
timestamp: { type: integer }
value: { type: number }
# ---------- Private user data payloads ----------
PrivateEnvelopeBase:
allOf:
- $ref: '#/components/schemas/DataEnvelopeBase'
- type: object
properties:
userId: { type: string }
channelType:
type: string
const: private
TradeOrdersEnvelope:
allOf:
- $ref: '#/components/schemas/PrivateEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /spotMarket/tradeOrders
subject:
type: string
example: orderChange
data:
$ref: '#/components/schemas/TradeOrderData'
TradeOrdersV2Envelope:
allOf:
- $ref: '#/components/schemas/PrivateEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /spotMarket/tradeOrdersV2
subject:
type: string
example: orderChange
data:
$ref: '#/components/schemas/TradeOrderData'
TradeOrderData:
type: object
properties:
orderId: { type: string }
clientOid: { type: string }
symbol: { type: string }
side:
type: string
enum: [buy, sell]
orderType:
type: string
enum: [limit, market]
price: { type: string }
size: { type: string }
originSize: { type: string }
filledSize: { type: string }
canceledSize: { type: string }
remainSize: { type: string }
remainFunds: { type: string }
status:
type: string
enum: [new, open, match, done]
type:
type: string
description: Event type.
enum: [received, open, match, update, filled, canceled]
orderTime:
type: integer
description: Order creation timestamp (milliseconds).
ts:
type: integer
description: Event timestamp (nanoseconds).
matchPrice: { type: string }
matchSize: { type: string }
tradeId: { type: string }
liquidity:
type: string
enum: [taker, maker]
feeType: { type: string }
oldSize: { type: string }
AccountBalanceEnvelope:
allOf:
- $ref: '#/components/schemas/PrivateEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /account/balance
subject:
type: string
example: account.balance
data:
type: object
properties:
accountId: { type: string }
currency: { type: string }
total: { type: string }
available: { type: string }
hold: { type: string }
availableChange: { type: string }
holdChange: { type: string }
relationContext:
type: object
properties:
symbol: { type: string }
orderId: { type: string }
tradeId: { type: string }
relationEvent:
type: string
description: Event type (deposit, transfer, hold, etc.).
relationEventId: { type: string }
time:
type: string
description: Event timestamp (milliseconds).
CrossMarginPositionEnvelope:
allOf:
- $ref: '#/components/schemas/PrivateEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /margin/position
subject:
type: string
description: Either `debt.ratio` or `position.status`.
enum: [debt.ratio, position.status]
data:
oneOf:
- $ref: '#/components/schemas/MarginDebtRatioData'
- $ref: '#/components/schemas/MarginPositionStatusData'
MarginDebtRatioData:
type: object
properties:
debtRatio: { type: number }
totalAsset: { type: string }
marginCoefficientTotalAsset: { type: string }
totalDebt: { type: string }
assetList:
type: object
additionalProperties:
type: object
properties:
total: { type: string }
available: { type: string }
hold: { type: string }
debtList:
type: object
additionalProperties:
type: string
timestamp: { type: integer }
MarginPositionStatusData:
type: object
properties:
type:
type: string
description: Position status (e.g. `FROZEN_FL`).
timestamp: { type: integer }
IsolatedMarginPositionEnvelope:
allOf:
- $ref: '#/components/schemas/PrivateEnvelopeBase'
- type: object
properties:
topic:
type: string
example: /margin/isolatedPosition:BTC-USDT
subject:
type: string
example: positionChange
data:
type: object
properties:
tag:
type: string
description: Isolated trading pair symbol.
status:
type: string
description: Position status (e.g. `DEBT`).
statusBizType:
type: string
accumulatedPrincipal: { type: string }
changeAssets:
type: object
additionalProperties:
type: object
properties:
total: { type: string }
hold: { type: string }
liabilityPrincipal: { type: string }
liabilityInterest: { type: string }
timestamp: { type: integer }