REST API for corporate BTC + ETH treasuries
Same data that powers the dashboard. SEC filings parsed in minutes, holdings reconciled nightly against bitbo.io, JSON + CSV bulk export. Keep your dashboards, spreadsheets, and models fed.
Quick start
Authenticate with the x-api-key header:
curl https://www.corpstacking.com/api/v1/stats \
-H "x-api-key: cs_live_YOUR_KEY"All responses are JSON with a content-type: application/json header. CSV export available at /export?format=csv.
Rate limits by plan
| Plan | API access | Req/min | Req/month |
|---|---|---|---|
| Free | — | — | — |
| Pro ($9/mo) | ✓ | 60 | 1,000 |
| Pro+ ($19/mo) | ✓ | 300 | 10,000 |
| Team ($49/mo) | ✓ | 1,000 | 100,000 |
Rate-limit headers returned on every response: x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset.
Endpoints
/api/v1/statsProTotal BTC held, total USD value at current price, entity counts by type (public_company / government / etf), and unrealized PnL across the tracked universe.
tokenquerystringToken to roll up. Defaults to bitcoin. Non-bitcoin values require Pro+.{
"token": "bitcoin",
"total_amount": 2890400,
"total_usd": 241600000000,
"entity_count": 220,
"unrealized_pnl_usd": 58900000000,
"purchase_count": 1247
}/api/v1/companiesProFull list of tracked entities with holdings + cost basis. Sorted by BTC held descending.
tokenquerystringFilter to entities whose primary treasury asset is this token.limitqueryintegerMax rows to return. Default 200, max 500.[
{
"ticker": "MSTR",
"name": "Strategy",
"slug": "microstrategy",
"country": "US",
"btc_holdings": 780897,
"avg_price_usd": 75577,
"total_cost_usd": 59013000000,
"last_purchase": "2026-04-13"
}
]/api/v1/companies/{slug}ProCompany metadata + its last 20 purchases. Replace {slug} in the path with the company slug from /companies.
slugpathstringrequiredCompany slug (e.g. microstrategy).{
"ticker": "MSTR",
"name": "Strategy",
"btc_holdings": 780897,
"purchases": [
{ "date": "2026-04-13", "btc_amount": 13927, "usd_amount": 1001000000, "filing_url": "https://sec.gov/..." }
]
}/api/v1/purchasesProGlobal feed of recent treasury purchases across every tracked entity.
tokenquerystringFilter to a specific token.sincequerystringISO date — only include purchases on or after this date.min_btcqueryintegerFilter to purchases with at least this many tokens.limitqueryintegerPage size, max 500.cursorquerystringOpaque cursor from a previous response.next_cursor for pagination.{
"data": [
{
"company_ticker": "MSTR",
"purchase_date": "2026-04-13",
"btc_amount": 13927,
"usd_amount": 1001000000,
"filing_url": "https://sec.gov/..."
}
],
"next_cursor": "eyJpZCI6Ii4uIn0="
}/api/v1/marketProLatest prices for 12 tracked tokens, plus global market cap, 24h volume, BTC/ETH dominance, and Fear & Greed index.
{
"tokens": [ { "id": "bitcoin", "symbol": "BTC", "price_usd": 83512.44, "change_24h": -1.23 } ],
"stats": { "total_market_cap": 3200000000000, "btc_dominance": 58.2, "fear_greed_value": 62 }
}/api/v1/tokensProList of tokens that have tracked treasury holdings. Pro+ unlocks non-bitcoin tokens.
[
{ "id": "bitcoin", "symbol": "BTC", "name": "Bitcoin", "entity_count": 220 },
{ "id": "ethereum", "symbol": "ETH", "name": "Ethereum", "entity_count": 14 }
]/api/v1/exportProCSV or JSON export of the full dataset. Pro+ includes non-BTC holdings.
formatquerystringrequiredExport format.tokenquerystringFilter to a specific token.ticker,name,btc_holdings,avg_price_usd,last_purchase
MSTR,Strategy,780897,75577,2026-04-13
IBIT,BlackRock iShares Bitcoin Trust,799151,.../api/v1/usageProLive counts for the calling API key — requests this minute, requests this month, and the ceilings your plan enforces.
{
"plan": "pro_plus",
"minute": { "used": 3, "limit": 60 },
"monthly": { "used": 412, "limit": 10000 }
}Live playground
Stored in your browser session only. Never logged or transmitted beyond the request itself.
GET /api/v1/statsWebhooks (API Pro)
API Pro subscribers can receive signed HTTP callbacks whenever a qualifying event fires. Three event types today: purchase.detected, holding.updated, and price.threshold. Delivery is at-least-once; each envelope carries a stable id you can use to dedupe.
curl -X POST https://www.corpstacking.com/api/v1/webhooks \
-H "x-api-key: cs_live_YOUR_KEY" \
-H "content-type: application/json" \
-d '{
"url": "https://your-app.example/corpstacking-webhook",
"events": ["purchase.detected", "holding.updated"],
"description": "prod pipeline"
}'Response returns a secret field (prefix whsec_cs_). Store it now — subsequent GET /webhooks only shows the masked preview.
POST https://your-app.example/corpstacking-webhook
content-type: application/json
user-agent: CorpStacking-Webhooks/1.0
x-corpstacking-event: purchase.detected
x-corpstacking-event-id: 9f0c…e2a1
x-corpstacking-timestamp: 1745078400
x-corpstacking-signature: t=1745078400,v1=5a8b…c9d0
{
"id": "9f0c…e2a1",
"type": "purchase.detected",
"created_at": "2026-04-19T14:00:00Z",
"data": {
"company": { "id": "uuid", "ticker": "MSTR" },
"token_id": "bitcoin",
"amount": 1250,
"usd_amount": 82500000,
"total_holdings_after": 214046,
"purchase_date": "2026-04-19",
"sec_accession": "0001104659-26-000...",
"filing_url": "https://sec.gov/..."
}
}import { createHmac, timingSafeEqual } from 'crypto'
function verify(secret, rawBody, header) {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=', 2)))
const t = parts.t, v1 = parts.v1
if (!t || !v1) return false
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
const a = Buffer.from(v1, 'hex'), b = Buffer.from(expected, 'hex')
return a.length === b.length && timingSafeEqual(a, b)
}
// Express handler — rawBody must be the unparsed request body bytes.
app.post('/corpstacking-webhook', express.raw({ type: '*/*' }), (req, res) => {
const ok = verify(process.env.CORPSTACKING_SECRET, req.body.toString(), req.header('x-corpstacking-signature'))
if (!ok) return res.sendStatus(400)
const event = JSON.parse(req.body.toString())
// ... dedupe on event.id, then process
res.sendStatus(200)
})Reject requests where the timestamp is more than 5 minutes old to prevent replay attacks. We retry on 5xx/network with exponential backoff up to 24h; 4xx is treated as permanent failure.
curl "https://www.corpstacking.com/api/v1/webhooks/events?subscription_id=SUB_ID&status=failed" \
-H "x-api-key: cs_live_YOUR_KEY"After 20 consecutive failures (~24h under the backoff curve), the subscription is auto-paused with paused_reason: too_many_failures. DELETE and recreate the subscription, or contact support to resume.
Errors
Errors are JSON with a stable code field. Treat codes as the source of truth; messages are human-readable and may change.
401 unauthorized — missing or invalid API key402 upgrade_required — endpoint requires a higher plan403 token_gated — token requires Pro+429 rate_limited — check x-ratelimit-reset429 quota_exceeded — monthly cap hit500 server_error — logged + auto-reported