Feeds channel
Subscribe to the real-time WebSocket stream of scored feed items.
The feeds channel pushes scored news and filings the moment they're
scored — the same items you'd poll from GET /v1/feeds,
delivered in real time over WebSocket.
Connect
Authentication happens on the connection itself — pass your API key as an
api_key query parameter (browsers can't set WebSocket headers):
wss://ws.forecite.dev/?api_key=fc_live_your_key_hereNon-browser clients may instead send an Authorization: Bearer fc_live_...
header on the upgrade. On a successful connection the server sends a welcome
frame with your plan tier and limits:
{ "type": "welcome", "tier": "quant", "limits": { "realtime_feeds_per_day": null, "scored_feed": true } }Subscribe
Send a subscribe frame to start receiving feeds. snapshot (optional) replays
the latest N matching items before the live stream begins:
{
"type": "subscribe",
"filters": { "symbols": ["NVDA", "TSLA"], "actionability": true },
"snapshot": 10
}Filters accept the same axes as GET /v1/feeds:
symbols, exchanges, aggregators, providers, actionability,
sentiment_min / sentiment_max, and tags. Tag filtering uses a tags
object — one key per dimension mapping to allowed values (OR within a dimension,
AND across dimensions):
{
"type": "subscribe",
"filters": { "tags": { "market_country": ["US"], "corp_activity": ["earnings", "ma"] } }
}The server acknowledges with { "type": "subscribed", "filters": { ... } }.
Messages
Each scored item arrives as a feed frame. snapshot is true for replayed
history and false for live items; count is your running daily total. data
has the same shape as a REST feed item:
{
"type": "feed",
"snapshot": false,
"count": 42,
"data": {
"id": "0b3f...",
"title": "Q3 EPS $4.93 vs $4.59 est; FY guide raised",
"source": "globenewswire",
"published_at": "2026-06-30T13:59:00Z",
"scoring": { "actionability": true, "actionability_score": 82, "sentiment_score": 8 },
"symbols": [{ "symbol": "NVDA", "exchange": "NASDAQ", "short_symbol": "NVDA" }]
}
}On the Free plan the stream still delivers up to 50 items/day, but Verdict
scores are withheld — the scoring object is locked:
"scoring": {
"locked": true,
"message": "Upgrade to a paid plan to unlock Verdict Engine actionability & sentiment scores.",
"actionability": null,
"actionability_score": null,
"sentiment_score": null,
"sentiment_short": null,
"sentiment_conviction": null
}Paid plans return "locked": false with the real scores. Check scoring.locked
before reading scoring.actionability / sentiment_*.
If you hit your plan's daily feed cap, the server sends a quota_exceeded frame
and stops delivering until the UTC-day reset:
{ "type": "quota_exceeded", "limit": 5000, "message": "Daily realtime feed limit of 5000 reached for tier starter" }Heartbeats & keepalive
The server sends WebSocket pings and expects pong replies (handled
automatically by most clients). You can also send { "type": "ping" } and
receive { "type": "pong" }. Reconnect with backoff on disconnect.
Use the SDK
The official SDKs wrap all of this —
connection, auth, subscribe, and reconnect — behind a single stream() call.