@hodlbase/client
v0.4.0
Published
Official TypeScript/JavaScript client for the HodlBase Bitcoin treasury data API
Downloads
727
Maintainers
Readme
@hodlbase/client
Official TypeScript/JavaScript client for the HodlBase API — structured financial data for Bitcoin treasury companies and their preferred securities.
- Zero dependencies — uses native
fetch(Node.js 18+, all modern browsers) - Full TypeScript support with strict types
- Automatic pagination for list and timeseries endpoints
- ESM and CommonJS
Installation
npm install @hodlbase/clientQuick Start
import { HodlbaseClient } from "@hodlbase/client";
const client = new HodlbaseClient({ apiKey: "your-api-key" });
// List all Bitcoin treasury companies
const { data: companies } = await client.companies.list();
// Get live BTC holdings and mNAV for Strategy (MSTR)
const live = await client.companies.getLive("MSTR", "fundamental_metrics");Resources
The client exposes two resource namespaces: companies and securities.
Companies
Bitcoin treasury companies — publicly traded companies that hold Bitcoin on their balance sheet.
// List all companies (auto-paginated)
const { data, metadata } = await client.companies.list();
// Get company details
const company = await client.companies.get("MSTR");
// Get available metrics
const metrics = await client.companies.metrics();
// Get latest values for a metric subgroup
const live = await client.companies.getLive("MSTR", "fundamental_metrics");
// Get latest value for a single metric
const metric = await client.companies.getMetricLive(
"MSTR",
"fundamental_metrics",
"btc_holdings",
);
// Get historical timeseries (auto-paginated)
const ts = await client.companies.getTimeseries("MSTR", "fundamental_metrics", {
granularity: "1d",
days: 30,
});
// Get timeseries for a single metric
const mnav = await client.companies.getMetricTimeseries(
"MSTR",
"fundamental_metrics",
"mnav",
{ granularity: "1d", days: 90 },
);Company metric subgroups:
| Subgroup | Key metrics |
|---|---|
| fundamental_metrics | BTC holdings, BTC reserve (USD), mNAV, Bitcoin per share, enterprise value |
| common_equity | Common stock price, market cap, shares outstanding (basic & fully diluted), shares per BTC |
| capital_structure_leverage | Debt outstanding, amplification, net leverage, preferred amplification |
| digital_credit_aggregates | Preferred notional, preferred market cap, USD cash reserve, annual dividends |
| dividends_coverage | Annual preferred dividends, convertible bond coupons, BTC years & USD months of dividend coverage |
| volatility | BTC 7-day and 30-day historical volatility |
| other | Bitcoins bought YTD, capital raised YTD |
Securities
Preferred securities issued by Bitcoin treasury companies to raise capital (e.g. STRK, STRF).
// List all preferred securities (with optional filters)
const { data } = await client.securities.list({ companyTicker: "MSTR" });
// Get security details
const strk = await client.securities.get("STRK");
// Get live data
const live = await client.securities.getLive("STRK", "fundamental_metrics");
// Get historical timeseries
const ts = await client.securities.getTimeseries("STRK", "credit_quality", {
granularity: "1d",
days: 30,
});Security metric subgroups:
| Subgroup | Key metrics |
|---|---|
| fundamental_metrics | Close price, effective yield, market cap, notional value, dividend rate, shares outstanding |
| credit_quality | Bitcoin rating (pure & USD-adjusted), higher seniority notional |
| atm_capital_metrics | Cumulative capital raised, cumulative ATM capital, total dividends paid, total Bitcoin accumulated |
| volume_metrics | Daily & 30-day volume, currency volume, volume above par |
| credit_spreads | Yield spread vs AAA, SOFR, US 10Y |
| advanced_metrics | Macaulay duration, 90-day BTC price correlation, 30-day historical volatility |
Timeseries Options
All timeseries methods accept an options object:
interface TimeseriesOptions {
start?: string; // ISO date string, e.g. "2024-01-01"
end?: string; // ISO date string
days?: number; // Number of historical days
granularity?: Granularity; // "1min" | "1h" | "2h" | "4h" | "12h" | "1d" | "1w" | "1m"
maxItems?: number; // Max total data points to return (sequential methods only)
concurrency?: number; // Parallel chunks for Fast methods (default: 4)
}Streaming Pages
For large datasets, iterate one page at a time instead of loading everything into memory:
for await (const page of client.companies.getTimeseriesPages("MSTR", "fundamental_metrics")) {
console.log(`Got ${page.data.length} points`);
// process page.data incrementally
}Fast Fetching (Parallel)
For large datasets, use the Fast variants to fetch timeseries data in parallel. These methods use a probe-then-parallel strategy:
- A lightweight probe request (
limit=1) discovers the full date range and whether the data spans multiple pages - If multi-page: the range is split into concurrent chunks (default: 4), fetched in parallel, and merged
- If single-page: falls back to a normal sequential fetch (no overhead beyond the probe)
// Single metric — parallel
const mnav = await client.companies.getMetricTimeseriesFast(
"MSTR",
"fundamental_metrics",
"mnav",
{ granularity: "1h", days: 365 },
);
// Subgroup timeseries — parallel
const ts = await client.companies.getTimeseriesFast("MSTR", "fundamental_metrics", {
granularity: "1h",
days: 90,
});
// Custom concurrency (default is 4)
const ts6 = await client.securities.getMetricTimeseriesFast(
"STRK",
"credit_spreads",
"cs_aaa",
{ granularity: "1h", days: 90, concurrency: 6 },
);The Fast methods return the exact same types (TimeseriesData / SingleMetricTimeseriesData) as their sequential counterparts — they're drop-in replacements.
Performance
Benchmarked against the production API (7 runs, trimmed mean, 95% CI):
| Scenario | Points | Normal | Fast | Speedup | |---|---|---|---|---| | 1D · 30 days | 30 | 59ms ± 4ms | 120ms ± 6ms | 0.49x | | 1D · 365 days | 365 | 72ms ± 8ms | 135ms ± 13ms | 0.53x | | 1H · 90 days | 2,160 | 434ms ± 10ms | 294ms ± 50ms | 1.48x | | 1H · 365 days | 8,755 | 1,649ms ± 60ms | 668ms ± 38ms | 2.47x |
When to use which:
| Method | Best for |
|---|---|
| getTimeseries / getMetricTimeseries | Small-to-medium requests (< 1,000 data points). Daily granularity, short date ranges. |
| getTimeseriesFast / getMetricTimeseriesFast | Large requests (1,000+ data points). Hourly or finer granularity, long date ranges. Up to 2.5x faster. |
Run
HODLBASE_API_KEY=your-key node benchmarks/benchmark.mjsto reproduce these results.
Error Handling
The client throws typed errors you can catch individually:
import {
HodlbaseError,
AuthenticationError,
RateLimitError,
NotFoundError,
} from "@hodlbase/client";
try {
const company = await client.companies.get("INVALID");
} catch (error) {
if (error instanceof NotFoundError) {
console.log("Company not found");
} else if (error instanceof AuthenticationError) {
console.log("Invalid API key");
} else if (error instanceof RateLimitError) {
console.log(`Rate limited — retry after ${error.retryAfter}s`);
} else if (error instanceof HodlbaseError) {
console.log(`API error ${error.statusCode}: ${error.detail}`);
}
}Error classes: AuthenticationError (401), ValidationError (400), ForbiddenError (403), NotFoundError (404), RateLimitError (429), HodlbaseError (base)
TypeScript
All types are exported for use in your code:
import type {
Company,
CompanyDetail,
Security,
SecurityDetail,
LiveData,
MetricValue,
TimeseriesData,
TimeseriesPoint,
Granularity,
CompanySubgroup,
SecuritySubgroup,
} from "@hodlbase/client";Development
The dev API sits behind HTTP Basic Auth. Use baseUrl and extraHeaders to connect:
import { HodlbaseClient } from "@hodlbase/client";
const client = new HodlbaseClient({
apiKey: "your-api-key",
baseUrl: "https://dev-api.hodlbase.io/v1",
extraHeaders: {
Authorization: `Basic ${btoa("username:password")}`,
},
});Replace username and password with your dev credentials.
License
MIT
