APIv1

REST API for corporate BTC, ETH & SOL 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
API Pro ($499/mo)2,000500,000
Team (Contact sales)3,0001,000,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 count, and unrealized PnL across the tracked universe (companies, ETFs, and governments combined).

Parameters
tokenquerystringToken to roll up. Defaults to bitcoin. Non-bitcoin values require Pro+.
Example response
{
  "token": "bitcoin",
  "total_amount": 3830257,
  "total_usd": 306420560000,
  "entity_count": 248,
  "unrealized_pnl_usd": 78900000000,
  "purchase_count": 7086
}
GET/api/v1/treasuriesPro
All tracked treasuries

Full list of tracked entities (public companies, private companies, ETFs, and governments) with holdings and cost basis. Sorted by holdings descending. Use entity_type to pull a single segment (e.g. just ETFs or just governments).

Parameters
tokenquerystringFilter to entities whose primary treasury asset is this token. Defaults to bitcoin. Non-bitcoin values require Pro+.
entity_typequerystringLimit to one segment. Omit (or pass all) to return every segment.
limitqueryintegerMax rows to return. Default 100, max 200.
offsetqueryintegerRow offset for pagination. Default 0.
Example response
[
  {
    "ticker": "MSTR",
    "name": "Strategy",
    "slug": "microstrategy",
    "country": "US",
    "flag_emoji": "🇺🇸",
    "logo_domain": "strategy.com",
    "logo_initial": "S",
    "btc_holdings": 818869,
    "avg_price_usd": 75577,
    "total_cost_usd": 59013000000,
    "last_purchase": "2026-04-13"
  }
]
GET/api/v1/treasuries/{slug}Pro
Single treasury detail + activity history

Entity metadata + its last 20 treasury events (buys + sells). Replace {slug} in the path with the slug from /treasuries.

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

Global feed of recent treasury events across every tracked entity. Includes both buys (positive amount) and sells (negative amount, e.g. ETF daily outflows). Each row is a discrete, citable disclosure (an SEC filing, a fund-administrator daily flow, an on-chain transfer, etc.). Note: governments report their holdings as official headline figures, not as individual dated transactions, so filtering this feed to a government returns an empty list — that is by design, not a gap. The current government position is on /treasuries/{slug}.

Parameters
tokenquerystringFilter to a specific token. Defaults to bitcoin. Non-bitcoin values require Pro+.
companyquerystringFilter to a single entity by its treasury slug.
entity_typequerystringLimit to one segment. Governments have no per-event activity (see note above).
sincequerystringISO date — only include events on or after this date.
orderquerystringSort direction by event date. Default desc (newest first).
limitqueryintegerPage size. Default 50, max 100.
offsetqueryintegerRow offset for pagination. Default 0.
Example response
{
  "data": [
    {
      "id": "abc-123",
      "token_id": "bitcoin",
      "amount": 13927,
      "btc_amount": 13927,
      "usd_amount": 1001000000,
      "price_per_btc": 71870,
      "purchase_date": "2026-04-13",
      "source": "sec_edgar",
      "sec_accession": "0000950170-26-000123",
      "filing_url": "https://sec.gov/...",
      "company": {
        "ticker": "MSTR",
        "name": "Strategy",
        "slug": "microstrategy",
        "country": "US",
        "flag_emoji": "🇺🇸",
        "logo_domain": "strategy.com",
        "logo_initial": "S"
      }
    }
  ],
  "meta": { "total": 3450, "limit": 50, "offset": 0, "token": "bitcoin", "entity_type": "all" }
}
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": 248 },
  { "id": "ethereum", "symbol": "ETH", "name": "Ethereum", "entity_count": 41 },
  { "id": "solana", "symbol": "SOL", "name": "Solana", "entity_count": 32 }
]
GET/api/v1/exportPro
Bulk data export

CSV or JSON bulk export of discrete treasury events (one row per disclosed buy/sell, with its per-(entity, token) data-confidence tier). Pro+ includes non-BTC tokens. Governments have no per-event rows here (their holdings are headline figures — use /treasuries).

Parameters
formatquerystringrequiredExport format.
tokenquerystringFilter to a specific token. Defaults to bitcoin. Non-bitcoin values require Pro+.
entity_typequerystringLimit to one segment.
companyquerystringFilter to a single entity by its treasury slug.
sincequerystringISO date — only include events on or after this date.
limitqueryintegerMax rows. Default 10,000, max 50,000.
Example response
company,ticker,country,token_id,amount,usd_amount,price_per_unit,purchase_date,total_holdings_after,source,data_confidence_tier,data_confidence_sources
Strategy,MSTR,US,bitcoin,13927,1001000000,71870,2026-04-13,818869,sec_edgar,verified,sec_edgar
BlackRock iShares Bitcoin Trust,IBIT,US,bitcoin,5859,...
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.

MCP server (AI agents)

For Claude Desktop, Claude Code, Cursor, ChatGPT custom GPTs, and any other client that speaks the Model Context Protocol, we host an MCP server at https://www.corpstacking.com/api/mcp/mcp. Same API key, same plan, same monthly quota — eight read-only tools wrapped around this same /v1 surface. Full reference and copy-paste configs for the popular clients at /mcp.

Embed widgets (free, no key)

Drop a live treasury widget on any page with a single <iframe>. No API key, no sign-up — the widget reads the same verified holdings shown on the site and refreshes on its own. Point it at any company by its page name:

<iframe
  src="https://www.corpstacking.com/api/embed/widget?company=strategy"
  width="420" height="180" frameborder="0"
  title="Strategy bitcoin treasury — CorpStacking"></iframe>

Options: company(the company's page name, e.g. strategy), theme (light or dark), variant, and layout for size. Signed-in users get a point-and-click builder with a live preview at /widgets.

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