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://cryptolisting.wsAll exchanges
wss://cryptolisting.ws?cex=binanceBinance only
wss://cryptolisting.ws?cex=binance,upbitBinance + Upbit
wss://cryptolisting.ws?cex=upbitUpbit only
wss://kr.cryptolisting.ws?cex=upbitUpbit only, from the Seoul endpoint

Upbit only

Two ways to receive nothing but Upbit:

wss://cryptolisting.ws?cex=upbit      # Tokyo endpoint, Upbit-filtered
wss://kr.cryptolisting.ws?cex=upbit   # Seoul endpoint — Upbit is all it carries

The Seoul endpoint only ever carries Upbit, so ?cex=upbit there is a no-op you can keep for clarity. Prefer Seoul if your bot runs in Korea, Tokyo if it also needs Binance or Bithumb — 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.