WebSocket Real-Time Streaming
Channels & Metrics
Two channels are available. Each metric has a minimum plan tier — you can subscribe to any metric at or below your plan level. Set metrics: null to receive all metrics your plan allows.
Company Channel
Live BTC company metrics (updated on each BTC/USD or stock tick)
| Metric | Min Plan | Description |
|---|---|---|
| btc_reserve_usd | Explorer | Market value (USD) of the company's BTC holdings |
| mnav | Explorer | Multiple on net asset value |
| common_stock_price | Explorer | Common stock price |
| market_cap | Professional·Higher tier | Market capitalization |
| amplification | Professional·Higher tier | Amplification factor |
| net_leverage | Professional·Higher tier | Net leverage ratio |
| net_leverage_pure | Professional·Higher tier | Pure net leverage |
| pref_market_cap | Professional·Higher tier | Preferred market cap |
| btc_years_dividend_coverage | Professional·Higher tier | BTC years dividend coverage |
| enterprise_value_usd | Enterprise·Higher tier | Enterprise value of the company |
| pref_amplification | Enterprise·Higher tier | Preferred amplification |
Security Channel
Live preferred security metrics (updated on each tick)
| Metric | Min Plan | Description |
|---|---|---|
| close_price | Explorer | Closing price |
| effective_yield | Explorer | Effective yield |
| market_cap | Explorer | Market capitalization |
| btc_rating_usd_reserve_adj | Professional·Higher tier | USD reserve-adjusted BTC rating |
| btc_rating | Professional·Higher tier | BTC credit rating |
| cs_aaa | Professional·Higher tier | Credit spread vs AAA |
| cs_sofr | Professional·Higher tier | Credit spread vs SOFR |
| cs_us10y | Professional·Higher tier | Credit spread vs US 10Y |
| duration | Professional·Higher tier | Duration |
Getting Started
Get real-time data flowing in three steps:
Install the SDK
Add the package to your project — zero runtime dependencies.
npm install @hodlbase/realtimeSubscribe to a channel
Create a client and subscribe to the company or security you want to track.
import { HodlbaseRealtime } from "@hodlbase/realtime"
const client = new HodlbaseRealtime({ apiKey: "hb_YOUR_API_KEY" })
client.subscribe("company", "MSTR", {
metrics: ["common_stock_price", "mnav", "market_cap"],
interval: 5,
onUpdate: (msg) => console.log(msg.data),
})Receive live updates
The server immediately sends a snapshot, then pushes updates as data changes.
{"type": "update", "channel": "company", "ticker": "MSTR", "data": {"common_stock_price": "398.50", "mnav": "2.34", "market_cap": "115000000000"}, "updated_at": "2026-03-17T14:30:00Z"}Raw WebSocket Examples
If you prefer to work with the raw WebSocket protocol directly, here are examples in popular languages.
const ws = new WebSocket("wss://api.hodlbase.io/v1/ws/realtime?api_key=hb_YOUR_KEY");
ws.onopen = () => {
console.log("Connected!");
ws.send(JSON.stringify({
action: "subscribe",
channel: "company",
ticker: "MSTR",
metrics: ["mnav", "market_cap", "common_stock_price"],
interval: 0
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === "update") {
console.log(`${msg.ticker} mNAV: ${msg.data.mnav}`);
}
};
ws.onclose = (e) => console.log("Closed:", e.code, e.reason);React Integration
Wrap your app in HodlbaseProvider to share a single WebSocket connection across components. Use useHodlbaseRealtime to subscribe declaratively — it handles connect, reconnect, and cleanup automatically.
import { HodlbaseProvider, useHodlbaseRealtime } from "@hodlbase/realtime/react"
function App() {
return (
<HodlbaseProvider apiKey="hb_YOUR_API_KEY">
<Dashboard />
</HodlbaseProvider>
)
}
function Dashboard() {
// Stream Strategy's stock price and mNAV
const mstr = useHodlbaseRealtime("company", "MSTR", {
metrics: ["common_stock_price", "mnav"],
interval: 5,
})
// Stream STRC's close price and effective yield
const strc = useHodlbaseRealtime("security", "STRC", {
metrics: ["close_price", "effective_yield"],
interval: 5,
})
return (
<div>
<h2>Strategy (MSTR)</h2>
<p>Stock Price: ${mstr.data?.common_stock_price ?? "—"}</p>
<p>mNAV: {mstr.data?.mnav ?? "—"}</p>
<h2>STRC Preferred</h2>
<p>Close Price: ${strc.data?.close_price ?? "—"}</p>
<p>Effective Yield: {strc.data?.effective_yield ?? "—"}%</p>
<p>Updated: {mstr.updatedAt ?? "Connecting..."}</p>
</div>
)
}React Hook Options
| Option | Type | Default | Description |
|---|---|---|---|
| metrics | string[] | undefined (all) | Specific metrics to subscribe to |
| interval | number | undefined | Update interval in seconds |
| enabled | boolean | true | Set false to conditionally skip the subscription |
enabled option to conditionally subscribe based on user interaction — for example, only stream data when a ticker is selected in a dropdown.SDK Reference
The official @hodlbase/realtime package handles auto-reconnect with exponential backoff, heartbeat pings, and automatic subscription re-establishment on reconnect.
Installation
npm install @hodlbase/realtimeBasic Usage
import { HodlbaseRealtime } from "@hodlbase/realtime"
const client = new HodlbaseRealtime({
apiKey: "hb_YOUR_API_KEY",
// baseUrl: "wss://api.hodlbase.io", // default
// reconnect: true, // auto-reconnect (default)
// debug: false, // console logging
})
// Subscribe with per-subscription callback
const sub = client.subscribe("company", "MSTR", {
metrics: ["mnav", "market_cap"],
interval: 5,
onUpdate: (msg) => {
console.log("mNAV:", msg.data.mnav)
console.log("Market Cap:", msg.data.market_cap)
},
})
// Global event listeners
client.on("welcome", (msg) => console.log("Plan:", msg.plan))
client.on("statusChange", (status) => console.log("Status:", status))
client.on("error", (msg) => console.error("Error:", msg.message))
// Later: unsubscribe
sub.unsubscribe()
// Or disconnect entirely
client.disconnect()SDK Options
| Option | Type | Default | Description |
|---|---|---|---|
| apiKey | string | required | Your HodlBase API key |
| baseUrl | string | "wss://api.hodlbase.io" | WebSocket server URL |
| autoConnect | boolean | true | Connect immediately on construction |
| reconnect | boolean | true | Auto-reconnect with exponential backoff |
| maxReconnectAttempts | number | 0 (unlimited) | Stop reconnecting after N failures |
| debug | boolean | false | Log debug messages to console |
Plan Limits
All limits are per-user — shared across all your API keys, not per-key. This means 5 connections on your Professional plan can be spread across multiple keys.
| Plan | Connections | Entity Subs | Min Interval | Metrics |
|---|---|---|---|---|
| Explorer | 5 | 10 | 1s | Explorer-tier |
| Professional | 10 | 5 | 5s | All Explorer + Professional |
| Enterprise | 20 | 10 | 0s (real-time) | All Explorer + Professional + Enterprise |
| Partner | 50 | Unlimited | 0s (real-time) | All metrics |
company:MSTR from two connections counts as 1 entity subscription, not 2.Raw WebSocket Protocol
This section documents the raw WebSocket protocol for users who prefer to connect without the SDK — for example, from Python, Go, or other non-JavaScript environments. If you're using TypeScript or React, the @hodlbase/realtime SDK handles all of this for you.
Connection & Authentication
Connect to the WebSocket endpoint with your API key as a query parameter. The connection is authenticated before being accepted — invalid keys receive close code 4001.
wss://api.hodlbase.io/v1/ws/realtime?api_key=hb_YOUR_API_KEYOn successful connection, the server immediately sends a welcome message with your plan limits and current usage:
{
"type": "welcome",
"plan": "professional",
"limits": {
"max_connections": 5,
"max_entity_subscriptions": 3,
"min_update_interval": 10
},
"usage": {
"connections": 1,
"connections_limit": 5,
"entity_subscriptions": 0,
"entity_subscriptions_limit": 3,
"monthly_ws_updates": 0
}
}Client Messages
All messages are JSON. Client messages use an action field, server messages use a type field.
Subscribe
Subscribe to a channel + ticker to start receiving updates. You can subscribe to multiple tickers on the same connection.
{
"action": "subscribe",
"channel": "company",
"ticker": "MSTR",
"metrics": ["mnav", "market_cap"],
"interval": 5
}| Field | Type | Required | Description |
|---|---|---|---|
| action | "subscribe" | Yes | Action type |
| channel | "company" | "security" | Yes | Data channel |
| ticker | string | Yes | Entity ticker (e.g. "MSTR", "STRK") |
| metrics | string[] | null | No | Specific metrics, or null for all plan-allowed |
| interval | number | No | Seconds between updates (0 = real-time, subject to plan minimum) |
Unsubscribe
{
"action": "unsubscribe",
"channel": "company",
"ticker": "MSTR"
}Ping / Pong
{ "action": "ping" }Server responds with {"type": "pong"}. Send pings every 25 seconds to keep the connection alive.
Usage Query
{ "action": "usage" }Returns your current connection count, subscription count, and monthly update totals for both user-level and per-key breakdowns.
Server Messages
Subscribed
Confirms a subscription. Shows which metrics were accepted, the effective interval, and any metrics rejected due to plan restrictions.
{
"type": "subscribed",
"channel": "company",
"ticker": "MSTR",
"metrics": ["mnav", "market_cap", "common_stock_price"],
"interval": 15,
"usage": { ... },
"rejected": [
{ "metric": "enterprise_value_usd", "reason": "requires enterprise plan" }
],
"interval_adjusted": true,
"requested_interval": 0,
"min_interval": 15
}interval_adjusted: true with the enforced minimum in the response.Update
Metric data updates. All values are strings — parse to numbers as needed. An immediate snapshot is sent right after subscribing; subsequent updates are pushed on change (or on your interval).
{
"type": "update",
"channel": "company",
"ticker": "MSTR",
"data": {
"mnav": "2.34",
"market_cap": "115000000000",
"common_stock_price": "398.50"
},
"updated_at": "2026-03-17T14:30:00Z"
}Error
{
"type": "error",
"message": "Entity subscription limit (1) reached for your plan"
}Usage Response
{
"type": "usage",
"user_limits": {
"max_connections": 5,
"max_entity_subscriptions": 3,
"min_update_interval": 10
},
"user_usage": {
"user_id": "usr-abc",
"connections": 2,
"entity_subscriptions": 1,
"monthly_ws_updates": 42
},
"per_api_key_usage": {
"api_key_id": "key-abc",
"connections": 1,
"entity_subscriptions": 1,
"monthly_ws_updates": 12
}
}Error Handling
Close Codes
| Code | Meaning | Action |
|---|---|---|
| 1000 | Normal closure / idle timeout | Reconnect if needed |
| 4001 | Invalid API key | Check your API key |
| 4008 | Connection limit reached | Close another connection first |
| 4013 | Server recovery (force reconnect) | Reconnect immediately |
Reconnection Strategy
The @hodlbase/realtime SDK handles reconnection automatically with exponential backoff (1s to 30s). On reconnect, all active subscriptions are re-sent automatically. The server does not remember subscriptions across connections.
If building your own client:
- On unexpected disconnect: wait 1-2s, then reconnect with exponential backoff
- On close code
4013: reconnect immediately (server-side recovery) - On close code
4001: do not reconnect (invalid credentials) - After reconnecting: re-send all subscribe messages
Keepalive & Idle Timeout
The server sends heartbeat pings every 30 seconds. For long-running connections, send {"action": "ping"} every 25 seconds from the client side.