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

PlanAPI accessReq/minReq/month
Free
Pro ($9/mo)601,000
Pro+ ($19/mo)30010,000
Team ($49/mo)1,000100,000

Rate-limit headers returned on every response: x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset.

Endpoints

GET/api/v1/statsPro
Aggregate treasury stats

Total BTC held, total USD value at current price, entity counts by type (public_company / government / etf), and unrealized PnL across the tracked universe.

Parameters
tokenquerystringToken to roll up. Defaults to bitcoin. Non-bitcoin values require Pro+.
Example response
{
  "token": "bitcoin",
  "total_amount": 2890400,
  "total_usd": 241600000000,
  "entity_count": 220,
  "unrealized_pnl_usd": 58900000000,
  "purchase_count": 1247
}
GET/api/v1/companiesPro
All tracked companies

Full list of tracked entities with holdings + cost basis. Sorted by BTC held descending.

Parameters
tokenquerystringFilter to entities whose primary treasury asset is this token.
limitqueryintegerMax rows to return. Default 200, max 500.
Example response
[
  {
    "ticker": "MSTR",
    "name": "Strategy",
    "slug": "microstrategy",
    "country": "US",
    "btc_holdings": 780897,
    "avg_price_usd": 75577,
    "total_cost_usd": 59013000000,
    "last_purchase": "2026-04-13"
  }
]
GET/api/v1/companies/{slug}Pro
Single company detail + purchase history

Company metadata + its last 20 purchases. Replace {slug} in the path with the company slug from /companies.

Parameters
slugpathstringrequiredCompany slug (e.g. microstrategy).
Example response
{
  "ticker": "MSTR",
  "name": "Strategy",
  "btc_holdings": 780897,
  "purchases": [
    { "date": "2026-04-13", "btc_amount": 13927, "usd_amount": 1001000000, "filing_url": "https://sec.gov/..." }
  ]
}
GET/api/v1/purchasesPro
Paginated purchase feed

Global feed of recent treasury purchases across every tracked entity.

Parameters
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.
Example response
{
  "data": [
    {
      "company_ticker": "MSTR",
      "purchase_date": "2026-04-13",
      "btc_amount": 13927,
      "usd_amount": 1001000000,
      "filing_url": "https://sec.gov/..."
    }
  ],
  "next_cursor": "eyJpZCI6Ii4uIn0="
}
GET/api/v1/marketPro
Live token prices + global stats

Latest prices for 12 tracked tokens, plus global market cap, 24h volume, BTC/ETH dominance, and Fear & Greed index.

Example response
{
  "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 }
}
GET/api/v1/tokensPro
Supported tokens

List of tokens that have tracked treasury holdings. Pro+ unlocks non-bitcoin tokens.

Example response
[
  { "id": "bitcoin", "symbol": "BTC", "name": "Bitcoin", "entity_count": 220 },
  { "id": "ethereum", "symbol": "ETH", "name": "Ethereum", "entity_count": 14 }
]
GET/api/v1/exportPro
Bulk data export

CSV or JSON export of the full dataset. Pro+ includes non-BTC holdings.

Parameters
formatquerystringrequiredExport format.
tokenquerystringFilter to a specific token.
Example response
ticker,name,btc_holdings,avg_price_usd,last_purchase
MSTR,Strategy,780897,75577,2026-04-13
IBIT,BlackRock iShares Bitcoin Trust,799151,...
GET/api/v1/usagePro
Your current rate limit + quota

Live counts for the calling API key — requests this minute, requests this month, and the ceilings your plan enforces.

Example response
{
  "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.

Parameters
Request
GET /api/v1/stats
Response
Send a request to see the response here.

Webhooks (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.

Create a subscription
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.

Event envelope
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/..."
  }
}
Verify the signature (Node.js)
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.

Inspect delivery history (DLQ)
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 key
402 upgrade_required — endpoint requires a higher plan
403 token_gated — token requires Pro+
429 rate_limited — check x-ratelimit-reset
429 quota_exceeded — monthly cap hit
500 server_error — logged + auto-reported