Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Exchange Filtering

Two filter layers narrow the stream you receive. The effective scope is their intersection.

1. Key restriction

Set by the administrator at key creation. Your effective allow-list appears in the welcome message:

{ "type": "welcome", "allowedCex": "binance,upbit", ... }

"*" means all exchanges.

2. ?cex= query parameter

Pass on the WebSocket URL:

URLReceives
wss://jp.cryptolisting.wsBinance + Upbit + Bithumb (everything the Tokyo endpoint carries)
wss://jp.cryptolisting.ws?cex=binanceBinance only
wss://jp.cryptolisting.ws?cex=binance,upbitBinance + Upbit
wss://jp.cryptolisting.ws?cex=upbitUpbit only
wss://kr.cryptolisting.wsUpbit + Bithumb, from the Seoul endpoint
wss://kr.cryptolisting.ws?cex=upbitUpbit only, from the Seoul endpoint
wss://us.cryptolisting.wsRobinhood, from the US endpoint (the only exchange it carries)
wss://us.cryptolisting.ws?cex=robinhoodSame — the filter is accepted but redundant there

robinhood is served by the US endpoint only: ?cex=robinhood on Tokyo or Seoul yields an empty intersection (the connection stays alive, nothing is delivered). Its two event types are wallet_listing (Robinhood Listing: asset added to Robinhood, not tradable yet on Robinhood) and cex_listing (Robinhood Live: tradable on Robinhood) — see Listing types.

Upbit only

Two ways to receive nothing but Upbit:

wss://jp.cryptolisting.ws?cex=upbit   # Tokyo endpoint, Upbit-filtered
wss://kr.cryptolisting.ws?cex=upbit   # Seoul endpoint, Upbit-filtered

Since 2026-09-04 the Seoul endpoint carries Bithumb as well, so ?cex=upbit there is a real filter — it used to be a no-op. Prefer Seoul if your bot runs in Korea, Tokyo if it also needs Binance — see WebSocket API.

Upbit titles are Korean. The schema is identical to every other publisher — only title changes language:

{
  "type": "announcement",
  "title": "플루언트(BLEND) 신규 거래지원 안내 (KRW, BTC, USDT 마켓)",
  "ticker": "BLEND",
  "publisher": "upbit",
  "listingType": "spot_listing",
  "detectedTimestampUs": 1710345000005000,
  "dispatchTimestampUs": 1710345000006000,
  "abnormalDetectionLatency": false,
  "markets": "KRW,BTC,USDT"
}

ticker is pre-extracted, so you never have to parse Korean yourself. Branch on listingType and publisher, not on title:

if msg["publisher"] == "upbit" and msg["listingType"] == "spot_listing":
    for ticker in msg["ticker"].split(","):   # already extracted from the Korean title
        snipe(ticker)

The split(",") is not defensive padding. Upbit routinely opens several assets in a single notice, and that notice arrives as one event listing every symbol — see ticker. Passing msg["ticker"] straight to an order would send "CYS,ICNT,XAN,EDEN,AIOZ,ALLO" as a symbol.

Effective filter

Key allowsYou requestYou receive
*binanceBinance
*noneAll
binance,upbitbinanceBinance
binance,upbitupbitUpbit
binance,upbitnoneBinance + Upbit
binanceupbitnothing (no overlap)

If the intersection is empty, the connection still succeeds and stays alive (PING and heartbeat keep flowing) but you receive no announcement messages. Verify your allowedCex against your ?cex= value.

Filtering by event type

?cex= narrows by exchange. To narrow by event class, branch on listingType in your handler:

def on_message(ws, raw):
    msg = json.loads(raw)
    if msg["type"] != "announcement":
        return

    lt = msg["listingType"]

    if lt == "spot_listing":
        snipe(msg["publisher"], msg["ticker"])

    elif lt in ("spot_delisting", "futures_delisting"):
        unwind(msg["publisher"], msg["ticker"])

    elif lt in ("monitoring_tag_extend", "monitoring_tag_remove", "caution_released"):
        log_risk_signal(msg["publisher"], msg["ticker"], lt)

The schema is identical across exchanges, so a single match / switch / if-elif chain covers every type. See Listing types for the full list.