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

Message Reference

All messages are binary WebSocket frames containing UTF-8 JSON. Every message has a type field.

Forward compatibility. New fields may be added at any time without notice. Your parser must ignore unknown fields. Don’t use strict schema validation.

TypeSentDirection
welcomeOnce after handshakeServer → client
heartbeatEvery 30 sServer → client
announcementWhen an event is detectedServer → client
test_announcementAfter {"type":"test"}Server → client
errorOn test rate-limitServer → client
testRequest a test announcementClient → server

Welcome

{
  "type": "welcome",
  "tier": "premium",
  "maxDistinctIps": 2,
  "maxConnectionsPerIp": 5,
  "absoluteMaxConnections": 20,
  "allowedCex": "*",
  "expiresInSecs": 2592000
}
FieldTypeDescription
typestringAlways "welcome"
tierstringfree, basic, premium, or enterprise
maxDistinctIpsintegerMax distinct IPs that can hold connections with this key
maxConnectionsPerIpintegerPer-IP cap — fixed at 5
absoluteMaxConnectionsintegerHard cap across all IPs — fixed at 20
allowedCexstringEffective filter after key restriction × ?cex=. "*" = all
expiresInSecsinteger or nullSeconds until the key expires; null = no expiration

Announcement

Sent when the detection engine captures a new event.

{
  "type": "announcement",
  "title": "Binance Will List TOKEN (TOKEN)",
  "ticker": "TOKEN",
  "publisher": "binance",
  "listingType": "spot_listing",
  "detectedTimestampUs": 1710345000005000,
  "dispatchTimestampUs": 1710345000006000,
  "abnormalDetectionLatency": false
}
FieldTypeDescription
typestringAlways "announcement"
titlestringOriginal exchange title. Replaced by an upgrade notice on free for every type except not_listing.
tickerstringAsset symbol(s). Comma-separated on multi-ticker events. "" on free for every type except not_listing.
publisherstringLowercase exchange name (binance, upbit, bithumb)
listingTypestringOne of Listing types
detectedTimestampUsintegerDetection time, µs since Unix epoch
dispatchTimestampUsintegerDispatch time, µs since Unix epoch
abnormalDetectionLatencybooleantrue if publish→detect was abnormally high; payload still valid

ticker may contain several symbols. Always split on ,:

tickers = data["ticker"].split(",")  # ["ABC", "DEF", "GHI"]

Run your own parser as a fallback. ticker is pre-extracted from title for speed. Cross-check it against title in production code so a single parser bug doesn’t take you down. title is always the original, unmodified exchange title.

Publishers

ValueExchange
binanceBinance
upbitUpbit
bithumbBithumb

Listing types

ValueMeaningExchanges
spot_listingNew spot market listingBinance, Upbit, Bithumb
futures_listingNew futures/perpetual listingBinance
spot_delistingSpot market delistingBinance, Upbit, Bithumb
futures_delistingFutures/perpetual delistingBinance
hodler_airdropBinance HODLer AirdropBinance
monitoring_tag_extendToken added to Binance’s Monitoring TagBinance
monitoring_tag_removeToken removed from Binance’s Monitoring TagBinance
caution_releasedCaution designation lifted (유의 종목 지정 해제)Bithumb, Upbit
not_listingOther announcement (maintenance, token swap, etc.)Binance, Upbit

Monitoring Tag — edge cases

  • Mixed Seed Tag bundles. A monitoring_tag_* event lists Monitoring Tag tickers only. Tickers in the same announcement that belong to the Seed Tag program are excluded. Seed-Tag-only announcements arrive as not_listing.
  • Mixed extend + remove. When a single Binance announcement both adds and removes tickers, the dispatch emits two separate events with the same title and disjoint ticker lists.

Caution lifecycle (Bithumb / Upbit)

Korean exchanges run a multi-stage risk track on tokens already trading (pre-warning, formal designation, extension, release, delisting). We narrow the broadcast to the two stages that drive trading decisions:

  • caution_released — the caution designation is lifted (유의 종목 지정 해제). Often a positive technical signal.
  • spot_delisting — final removal (거래지원 종료).

The intermediate stages (유의촉구 / 거래유의·투자유의종목 지정 / 지정 연장) and risk-context deposit halts are detected but intentionally not forwarded — they generate noise without clear actionable signal on the trading side.

Composite announcements. A single title that pairs a release with a delisting (e.g. 신세틱스(SNX) 거래유의종목 지정 해제 및 (BCD, WTC) 거래지원 종료) is split into one event per (listingType, ticker-group). Multi-ticker, single-stage titles (e.g. (BCD, WTC) 거래지원 종료) are joined comma-separated like listings.

Example: caution_released

{
  "type": "announcement",
  "title": "신세틱스(SNX) 거래유의종목 지정 해제",
  "ticker": "SNX",
  "publisher": "bithumb",
  "listingType": "caution_released",
  "detectedTimestampUs": 1745971200834000,
  "dispatchTimestampUs": 1745971200842117,
  "abnormalDetectionLatency": false
}

Example: spot_delisting

{
  "type": "announcement",
  "title": "고트세우스 막시무스(GOAT) 거래지원 종료",
  "ticker": "GOAT",
  "publisher": "bithumb",
  "listingType": "spot_delisting",
  "detectedTimestampUs": 1745971200834000,
  "dispatchTimestampUs": 1745971200842117,
  "abnormalDetectionLatency": false
}

Tier behavior

Tier is returned in welcome.tier. Pricing details on the pricing page.

TierAll types except not_listingnot_listingDelivery delay
freeticker = "", title = upgrade notice. Other fields unchanged.Full contentNone
basicFull contentFull content+20 ms
premiumFull contentFull contentNone
enterpriseFull contentFull contentNone

The free tier is default-deny: any new event type is automatically redacted on free without a schema migration. Free clients must accept ticker = "" and the upgrade-notice title gracefully.

Test announcements always carry the full payload regardless of tier.

Measuring latency

All timestamps are microseconds since Unix epoch.

Dispatch delay  = dispatchTimestampUs − detectedTimestampUs
Network delay   = your_receive_us − dispatchTimestampUs
Total           = your_receive_us − detectedTimestampUs
import time

def on_announcement(msg):
    now_us = int(time.time() * 1_000_000)
    dispatch_ms = (msg["dispatchTimestampUs"] - msg["detectedTimestampUs"]) / 1000
    network_ms  = (now_us - msg["dispatchTimestampUs"]) / 1000
    total_ms    = (now_us - msg["detectedTimestampUs"]) / 1000
    print(f"dispatch={dispatch_ms:.2f}ms network={network_ms:.2f}ms total={total_ms:.2f}ms")

Test Announcement

Send {"type":"test"}. The server replies with a fake announcement, identical in shape to a real one but with type = "test_announcement" and ticker = "DUMMYTOKEN".

{
  "type": "test_announcement",
  "title": "Binance Will List DUMMYTOKEN (DUMMYTOKEN)",
  "ticker": "DUMMYTOKEN",
  "publisher": "binance",
  "listingType": "spot_listing",
  "detectedTimestampUs": 1743850001999800,
  "dispatchTimestampUs": 1743850002000000,
  "abnormalDetectionLatency": false
}
  • Sent only to the requester, not broadcast.
  • Always full payload, including on free.
  • Rate limit: 1 per minute per API key, shared across connections.

On excess, the server returns an error instead:

{"type": "error", "code": "test_rate_limited", "retryAfterSecs": 42}
FieldTypeDescription
typestringAlways "error"
codestringCurrently only test_rate_limited
retryAfterSecsintegerSeconds until next request allowed

Heartbeat

Sent every 30 s to every subscriber, every tier.

{
  "type": "heartbeat",
  "timestampNs": 1710345030123456789,
  "timeUtc": "2026-04-17T08:30:30.123456Z"
}
FieldTypeDescription
typestringAlways "heartbeat"
timestampNsintegerServer emission time, ns since Unix epoch
timeUtcstringISO 8601 UTC, microsecond precision

Keep-alive (PING/PONG)

Separate from the application-level heartbeat, the server sends a WebSocket PING control frame (opcode 0x9, empty payload) every 15 s (0–5 s jitter on the first). Your library responds with PONG automatically. If no PONG arrives within 30 s of the last ping, the server closes the TCP connection.

Don’t reconnect on “no announcement for N seconds”. Listings are sparse — you’ll loop. Watch the 30 s heartbeat instead, or rely on your WebSocket library’s close/error callbacks.