WebSocket Real-Time Streaming

Stream live Bitcoin treasury metrics directly to your application via WebSocket. Subscribe to company and security channels and receive instant updates as market data changes.

Real-time updatesPlan-based accessAuto-reconnect SDK

Sub-second latency

Updates pushed as soon as market data changes

Plan-gated metrics

Automatic access control per your plan tier

Throttle control

Set your own update interval per subscription

Build real-time apps with @hodlbase/realtime

Auto-reconnect, heartbeat management, subscription re-establishment, and full TypeScript types — with zero runtime dependencies. Includes React hooks for declarative subscriptions.

npm install @hodlbase/realtime
Company data
import { HodlbaseRealtime } from "@hodlbase/realtime"
const client = new HodlbaseRealtime({
  apiKey: "hb_YOUR_API_KEY"
})

// Stream Strategy's stock price + mNAV
client.subscribe("company", "MSTR", {
  metrics: ["common_stock_price", "mnav"],
  onUpdate: (msg) => {
    console.log("Price:", msg.data.common_stock_price)
    console.log("mNAV:", msg.data.mnav)
  }
})
Security data
// Stream STRC's close price + yield
client.subscribe("security", "STRC", {
  metrics: ["close_price", "effective_yield"],
  onUpdate: (msg) => {
    console.log("Price:", msg.data.close_price)
    console.log("Yield:", msg.data.effective_yield)
  }
})

// Global events
client.on("welcome", (msg) =>
  console.log("Plan:", msg.plan)
)
View on npm →

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)

Available tickers:MSTRASST3350.T
MetricMin PlanDescription
btc_reserve_usdExplorerMarket value (USD) of the company's BTC holdings
mnavExplorerMultiple on net asset value
common_stock_priceExplorerCommon stock price
market_capProfessional·Higher tierMarket capitalization
amplificationProfessional·Higher tierAmplification factor
net_leverageProfessional·Higher tierNet leverage ratio
net_leverage_pureProfessional·Higher tierPure net leverage
pref_market_capProfessional·Higher tierPreferred market cap
btc_years_dividend_coverageProfessional·Higher tierBTC years dividend coverage
enterprise_value_usdEnterprise·Higher tierEnterprise value of the company
pref_amplificationEnterprise·Higher tierPreferred amplification
Tip: Metrics marked Higher tier require a higher plan. to upgrade and unlock the full metric suite.

Security Channel

Live preferred security metrics (updated on each tick)

Available tickers:STRKSTRFSTRDSTRCSTRE
MetricMin PlanDescription
close_priceExplorerClosing price
effective_yieldExplorerEffective yield
market_capExplorerMarket capitalization
btc_rating_usd_reserve_adjProfessional·Higher tierUSD reserve-adjusted BTC rating
btc_ratingProfessional·Higher tierBTC credit rating
cs_aaaProfessional·Higher tierCredit spread vs AAA
cs_sofrProfessional·Higher tierCredit spread vs SOFR
cs_us10yProfessional·Higher tierCredit spread vs US 10Y
durationProfessional·Higher tierDuration
Tip: Metrics marked Higher tier require a higher plan. to upgrade and unlock the full metric suite.

Getting Started

Get real-time data flowing in three steps:

1

Install the SDK

Add the package to your project — zero runtime dependencies.

npm install @hodlbase/realtime
2

Subscribe to a channel

Create a client and subscribe to the company or security you want to track.

TypeScriptts
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),
})
3

Receive live updates

The server immediately sends a snapshot, then pushes updates as data changes.

Update message
{"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.

Browser JavaScriptjs
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.

React Dashboard — MSTR + STRC livetsx
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

OptionTypeDefaultDescription
metricsstring[]undefined (all)Specific metrics to subscribe to
intervalnumberundefinedUpdate interval in seconds
enabledbooleantrueSet false to conditionally skip the subscription
Tip: Use the 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/realtime

Basic Usage

TypeScriptts
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

OptionTypeDefaultDescription
apiKeystringrequiredYour HodlBase API key
baseUrlstring"wss://api.hodlbase.io"WebSocket server URL
autoConnectbooleantrueConnect immediately on construction
reconnectbooleantrueAuto-reconnect with exponential backoff
maxReconnectAttemptsnumber0 (unlimited)Stop reconnecting after N failures
debugbooleanfalseLog 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.

PlanConnectionsEntity SubsMin IntervalMetrics
Explorer5101sExplorer-tier
Professional1055sAll Explorer + Professional
Enterprise20100s (real-time)All Explorer + Professional + Enterprise
Partner50Unlimited0s (real-time)All metrics
Note: Entity subscriptions count unique channel:ticker pairs across all your connections. Subscribing to 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.

WebSocket endpoint
wss://api.hodlbase.io/v1/ws/realtime?api_key=hb_YOUR_API_KEY

On successful connection, the server immediately sends a welcome message with your plan limits and current usage:

Welcome message (received on connect)
{
  "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
  }
}
Note: The welcome message tells you your plan limits and current usage. Use this to configure your subscription behavior before sending subscribe messages.

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.

Client → Server
{
  "action": "subscribe",
  "channel": "company",
  "ticker": "MSTR",
  "metrics": ["mnav", "market_cap"],
  "interval": 5
}
FieldTypeRequiredDescription
action"subscribe"YesAction type
channel"company" | "security"YesData channel
tickerstringYesEntity ticker (e.g. "MSTR", "STRK")
metricsstring[] | nullNoSpecific metrics, or null for all plan-allowed
intervalnumberNoSeconds between updates (0 = real-time, subject to plan minimum)

Unsubscribe

Client → Server
{
  "action": "unsubscribe",
  "channel": "company",
  "ticker": "MSTR"
}

Ping / Pong

Client → Server
{ "action": "ping" }

Server responds with {"type": "pong"}. Send pings every 25 seconds to keep the connection alive.

Usage Query

Client → Server
{ "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.

Server → Client
{
  "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
}
Note: If your requested interval is below your plan minimum, the server adjusts it upward and includes 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).

Server → Client
{
  "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

Server → Client
{
  "type": "error",
  "message": "Entity subscription limit (1) reached for your plan"
}

Usage Response

Server → Client
{
  "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

CodeMeaningAction
1000Normal closure / idle timeoutReconnect if needed
4001Invalid API keyCheck your API key
4008Connection limit reachedClose another connection first
4013Server 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.

Important: Connections with no client messages for 5 minutes are automatically closed with code 1000. The SDK handles keepalive pings automatically — this only applies if you're using the raw protocol.