Message Reference
All messages are delivered as binary WebSocket frames containing UTF-8 JSON. Every message has a type field.
Welcome
Sent once, immediately after a successful connection.
{
"type": "welcome",
"tier": "premium",
"maxConnections": 5,
"allowedCex": "*",
"expiresInSecs": 2592000
}
| Field | Type | Description |
|---|---|---|
type | string | Always "welcome" |
tier | string | Your subscription tier: basic, premium, or enterprise |
maxConnections | integer | Maximum simultaneous connections allowed with your key |
allowedCex | string | Exchanges you will receive announcements from (effective filter after applying key restrictions and your cex preference). "*" means all exchanges |
expiresInSecs | integer|null | Seconds until your key expires, or null if no expiration |
Announcement
Sent whenever a listing or other exchange announcement is detected on a monitored exchange.
Single ticker:
{
"type": "announcement",
"title": "Binance Will List TOKEN (TOKEN)",
"ticker": "TOKEN",
"publisher": "binance",
"listingType": "spot_listing",
"publishTimestampUs": 1710345000000000,
"detectedTimestampUs": 1710345000005000,
"dispatchTimestampUs": 1710345000006000
}
Multiple tickers (one announcement listing several assets at once):
{
"type": "announcement",
"title": "Binance Will List ABC, DEF and GHI",
"ticker": "ABC,DEF,GHI",
"publisher": "binance",
"listingType": "spot_listing",
"publishTimestampUs": 1710345000000000,
"detectedTimestampUs": 1710345000005000,
"dispatchTimestampUs": 1710345000006000
}
| Field | Type | Description |
|---|---|---|
type | string | Always "announcement" |
title | string | Original announcement title from the exchange |
ticker | string | Asset symbol(s). Comma-separated when multiple tickers are listed in the same announcement (e.g. "BTC" or "ABC,DEF,GHI") |
publisher | string | Exchange name in lowercase (e.g. "binance", "upbit", "bithumb") |
listingType | string | One of the listing types below |
publishTimestampUs | integer | When the exchange published the announcement (microseconds since UNIX epoch) |
detectedTimestampUs | integer | When our detection engine captured it (microseconds since UNIX epoch) |
dispatchTimestampUs | integer | When the dispatch server broadcasted it (microseconds since UNIX epoch) |
Always handle ticker as a potentially comma-separated list. Some exchange announcements list multiple assets at once. Split on , to get individual tickers:
tickers = data["ticker"].split(",") # ["ABC", "DEF", "GHI"]
Supported exchanges (publisher)
More exchanges coming soon. This list will grow over time. Use the cex query parameter when connecting to filter which exchanges you receive.
Listing types
| Value | Meaning |
|---|---|
spot_listing | New spot market listing |
futures_listing | New futures/perpetual listing |
spot_delisting | Spot market delisting (coming soon) |
futures_delisting | Futures/perpetual delisting (coming soon) |
not_listing | Other exchange announcement (maintenance, airdrop, token swap, etc.) |
Delistings coming soon. The spot_delisting and futures_delisting types are reserved for future use. Currently, announcements that are not listings are sent with listingType: "not_listing".
Measuring your latency
All timestamps are in microseconds since UNIX epoch (not milliseconds, not nanoseconds).
Detection delay = detectedTimestampUs - publishTimestampUs
Dispatch delay = dispatchTimestampUs - detectedTimestampUs
Your network delay = your_receive_time_us - dispatchTimestampUs
Total end-to-end = your_receive_time_us - publishTimestampUs
Python example:
import time
def on_announcement(msg):
now_us = int(time.time() * 1_000_000)
detection_ms = (msg["detectedTimestampUs"] - msg["publishTimestampUs"]) / 1000
dispatch_ms = (msg["dispatchTimestampUs"] - msg["detectedTimestampUs"]) / 1000
network_ms = (now_us - msg["dispatchTimestampUs"]) / 1000
total_ms = (now_us - msg["publishTimestampUs"]) / 1000
print(f"Detection: {detection_ms:.2f}ms")
print(f"Dispatch: {dispatch_ms:.2f}ms")
print(f"Network: {network_ms:.2f}ms")
print(f"Total: {total_ms:.2f}ms")
Test Announcement
You can request a fake announcement to verify your integration. Send a JSON message to the server:
{"type":"test"}
The server replies with a test_announcement — identical to a real announcement except for the type field:
{
"type": "test_announcement",
"title": "Binance Will List MOCKX (MOCKX)",
"ticker": "MOCKX",
"publisher": "binance",
"listingType": "spot_listing",
"publishTimestampUs": 1743850000000000,
"detectedTimestampUs": 1743850001999800,
"dispatchTimestampUs": 1743850002000000
}
The fields are the same as a regular Announcement. The timestamps are simulated: publishTimestampUs is 1 second before dispatch, and detectedTimestampUs is 200µs before dispatch.
This message is only sent to you, not broadcast to other subscribers. The exchange, listing type, ticker, and title are randomized from realistic templates.
Test rate limit
Test requests are rate-limited to 1 per minute per API key (shared across all connections using the same key). If you exceed the limit, the server responds with an error instead of a test announcement:
{
"type": "error",
"code": "test_rate_limited",
"retryAfterSecs": 42
}
| Field | Type | Description |
|---|---|---|
type | string | Always "error" |
code | string | Error code: "test_rate_limited" |
retryAfterSecs | integer | Seconds to wait before retrying |
Heartbeat
Sent every 30 seconds to indicate the server is alive.
{
"type": "heartbeat",
"timestampNs": 1710345030000000000,
"timeUtc": "2024-03-13T10:30:30.000000Z"
}
| Field | Type | Description |
|---|---|---|
type | string | Always "heartbeat" |
timestampNs | integer | Current server time in nanoseconds since UNIX epoch |
timeUtc | string | Current server time in ISO 8601 UTC format |
If you do not receive a heartbeat within 35 seconds, your connection is likely dead. Initiate a reconnection.