Kafka vs WebSocket
| Feature | Kafka | WebSocket |
|---|---|---|
| Data Recovery | Replay from any offset | Missed data is lost |
| Scaling | Consumer groups, horizontal scaling | Single connection per client |
| Persistence | 72h configurable retention | No persistence |
| Delivery | Exactly-once / at-least-once | Best-effort, drops on disconnect |
| Fault Tolerance | Auto rebalancing, no data loss | Manual reconnection required |
What is Kafka?
Kafka is a event streaming platform. Consumers (users, clients) can connect to specific topics and process the data that is being published on those topics by the producer (in this case UnusualWhales). The best part is that even if you have a downtime (server issues, migration work, etc.) no data will be lost as you can specify the offset where you left off and then continue to consume the messages from that specific offset resulting in no data loss.
Available Topics
Each topic is a single firehose that carries every symbol — open one connection per topic and filter on your end (e.g. by ticker) for the symbols you need. Topics are not per-ticker.
Options Flow
flow-alertsReal-time flow alerts (all, unfiltered)all-option-tradesAll option trade executions (6–10M/day; also available per-ticker)flow-expiryFlow by expiryflow-strikeFlow by strikeinterval-flowPer-interval option flow stats (sweeps, floors, multilegs, Greek flow, IV, net premium)greek-flowGreek-weighted flownet-flowLive net call/put premium & volume aggregates per ticker (net-prem view)Greeks, GEX & Volatility
gexFirehose of dealer Greek exposure for every ticker — gamma/delta/charm/vanna per 1% move (OI, volume & directional weighted). Filter by ticker on your end.gex-strikeFirehose of GEX broken out per strike (calls & puts separately) for every ticker. Filter by ticker on your end.gex-strike-expiryFirehose of GEX broken out per strike and expiry for every ticker. Filter by ticker on your end.option-greeksLive option greeksiv-term-structureIV term structureinterpolated-ivInterpolated implied volatilityrisk-reversal-skewRisk reversal skew dataMarket Data
priceLive last-trade price, time & cumulative session volume per tickerticker-quotesReal-time ticker quotesall-trade-reportAll stock trades (lit and off-lit)lit-tradesLive lit (exchange-based) equity tradesoff-lit-tradesLive off-lit (dark pool) equity tradesticker-stock-statesDaily OHLCV states per tickermarket-tideLive market tide and OTM market tidetrading-haltsTrading state changes (halts, resumes, LULD pauses)newsLive headline news (Truth Social posts + aggregator news)stock-directoryDaily stock directory of the exchangesChain Data
option-statesLive updates of contracts with volumes, side volume, OI, greeks and morechain-vol-price-lvlContract volume by price levelchain-fragInterval flow of contracts. Can be used to scan for contracts that have high volume in the last 5 minutes.chain-aggContract volume & premium aggregation. Can be used to build 1min charts of volume profilescontract-screenerLive option contract snapshots (Greeks, side volumes, OI growth) — build a screener on top of contractsInstitutional & Filings
insider-tradesInsider trading activitysec-filingsSEC filingFutures
futures-tradesLive CME futures trade prints across all contracts — tape, price, size, side & NBBO. Filter by contract on your end.ETF
etf-tideETF market tide dataAlerts
custom-alertsNotifications matching the alert configurations on your own Unusual Whales accountExample message structures
A sample of the message values published on the topics above. Values are protobuf-encoded on the wire; shown here decoded. Each record carries its symbol (e.g. ticker) so you can filter the firehose on your end. Full field references are in the API documentation.
gex{
"ticker": "SPY",
"timestamp": 1726670396000,
"price": "562.86",
"gamma_per_one_percent_move_oi": "-262444980.31",
"delta_per_one_percent_move_oi": "",
"charm_per_one_percent_move_oi": "-1677926539943.05",
"vanna_per_one_percent_move_oi": "2842602508.57",
"gamma_per_one_percent_move_vol": "-934307209.58",
"gamma_per_one_percent_move_dir": "-9372185.61"
}gex-strike{
"ticker": "SPY",
"strike": "290",
"price": "562.96",
"timestamp": 1726670426000,
"call_gamma_oi": "174792.59",
"put_gamma_oi": "-1172037.66",
"call_delta_oi": "0.4521",
"put_delta_oi": "-0.3812",
"call_charm_oi": "85658181.72",
"put_charm_oi": "-315259003.37",
"call_vanna_oi": "-6103.51",
"put_vanna_oi": "1337727.64"
}price{ "ticker": "SPY", "close": "562.82", "time": 1726670327692, "vol": 6015555 }news{
"headline": "US Energy Secretary foresees many more LNG export deals signed",
"timestamp": "2025-06-11T21:40:56Z",
"source": "social-media",
"tickers": [],
"is_trump_ts": false
}Connection
stream.unusualwhales.com:9083SASL/PLAIN over TLSProtocol BuffersQuick Start
Full example on GitHubfrom confluent_kafka import Consumer
from uw_protos.flow_alert_pb2 import FlowAlert
conf = {
'bootstrap.servers': 'stream.unusualwhales.com:9083',
'group.id': 'my-consumer-group',
'auto.offset.reset': 'earliest',
'security.protocol': 'SASL_SSL',
'sasl.mechanism': 'PLAIN',
'sasl.username': '<your-api-key>',
'sasl.password': '<your-api-secret>',
}
consumer = Consumer(conf)
consumer.subscribe(['flow-alerts'])
while True:
msg = consumer.poll(1.0)
if msg is None:
continue
if msg.error():
print(f"Error: {msg.error()}")
continue
alert = FlowAlert()
alert.ParseFromString(msg.value())
print(f"Offset {msg.offset()}: {alert.ticker} {alert.strike} {alert.expiry}")Get Started
Kafka streaming starts at $3,000/mo. Includes 80K daily REST requests, websocket access, and real-time Kafka cluster access.