REST API reference
Every MCP tool is a thin wrapper around a REST endpoint on api.blockchainacademics.com. If you would rather call HTTP directly (a non-MCP integration, a server-side pipeline, an internal job runner), the same API key and the same canonical envelope apply.
Base URL
https://api.blockchainacademics.comThe MCP servers and the CLI read BCA_API_BASE to point at another host (an allowlist: production, https://staging-api.blockchainacademics.com, and localhost); a custom HTTP client sets its own base URL. See Environment variables.
Authentication
Every request carries the API key in a header:
X-API-Key: bca_live_xxxxxxxxxxxxxxxxOr, equivalently, using the Authorization convention:
Authorization: Bearer bca_live_xxxxxxxxxxxxxxxxThe server accepts either. Get a key at blockchainacademics.com/crypto-api/signup.
Response envelope
Every 2xx response returns the canonical three-key envelope:
{
"data": { "...": "endpoint-specific payload" },
"attribution": {
"citations": [
{
"cite_url": "https://blockchainacademics.com/...",
"as_of": "2026-04-22T10:12:00Z",
"source_hash": "sha256:abc123..."
}
]
},
"meta": {
"status": "complete",
"request_id": "req_01HABCDEF0123456789ABCDEFG",
"pageInfo": {
"hasNextPage": false,
"hasPreviousPage": false,
"startCursor": null,
"endCursor": null
}
}
}Full field-by-field breakdown: Response envelope. Citation semantics: Citations.
Response headers
Every response carries:
X-Request-ID: req_01HABCDEF0123456789ABCDEFG
X-RateLimit-Limit: 1200
X-RateLimit-Remaining: 1187
X-RateLimit-Reset: 1713787200X-Request-ID mirrors meta.request_id. Log it with every call. X-RateLimit-Reset is Unix epoch seconds.
Core endpoint map
| Endpoint group | Path | Who uses it |
|---|---|---|
| Articles / corpus | /v1/articles/... | search_news, get_article, get_as_of_snapshot |
| Entities | /v1/entities/... | get_entity, list_entity_mentions |
| Topics | /v1/topics | list_topics |
| Academy | /v1/academy/... | get_explainer |
| Market | /v1/market/... | get_price, get_ohlc, get_market_overview, get_pair_data |
| On-chain | /v1/onchain/... | get_wallet_profile, get_tx, get_token_holders |
| Sentiment | /v1/sentiment/... | get_sentiment, get_social_pulse, get_fear_greed |
| Proprietary indicators | /v1/indicators/... | get_coverage_index, get_narrative_strength, … |
| Agent jobs | /v1/agents/... | generate_due_diligence, translate_contract, get_agent_job |
| Directories | /v1/directories/... | list_stablecoins, list_yields, list_vcs, list_jobs, get_job, … |
| Fundamentals | /v1/fundamentals/... | get_tokenomics, get_audit_reports, compare_protocols |
| Chain-specific | /v1/chains/... | get_solana_ecosystem, get_l2_comparison |
| Regulatory | /v1/regulatory/... | get_regulatory_status, track_sec_filings, get_mica_status |
| Security | /v1/security/... | scan_contract, check_phishing_domain, get_bug_bounty_programs |
| History time-series | /v1/history/... | get_history_prices, get_history_sentiment, … |
| Services (revenue, POST) | /v1/services/... | book_kol_campaign, request_custom_research, submit_listing |
| Grants | /v1/grants/... | search_grants, get_grant |
| Studio (leads) | /v1/studio/... | Internal: powers the studio's contact form |
For exact parameters, request bodies, and response shapes of every endpoint, see the live Swagger. This page intentionally does not duplicate it: the OpenAPI JSON is the source of truth, and a mirror drifts.
Example: search_news over HTTP
curl -s "https://api.blockchainacademics.com/v1/articles/search?q=circle+mica&limit=5" \
-H "X-API-Key: $BCA_API_KEY"Response:
{
"data": {
"items": [
{
"slug": "circle-mica-license",
"title": "Circle secures full MiCA license",
"published_at": "2026-04-18T14:02:17Z",
"summary": "Circle became the first USD stablecoin issuer…",
"entities": ["circle", "mica", "european-union"]
}
],
"total": 1
},
"attribution": {
"citations": [
{
"cite_url": "https://blockchainacademics.com/article/circle-mica-license?src=custom&utm_medium=http&utm_campaign=search_news",
"as_of": "2026-04-21T09:31:22Z",
"source_hash": "sha256:a9f1b7de2c4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1"
}
]
},
"meta": {
"status": "complete",
"request_id": "req_01HABCDEF0123456789ABCDEFG",
"pageInfo": {
"hasNextPage": false,
"hasPreviousPage": false,
"startCursor": null,
"endCursor": null
}
}
}Example: list with pagination
Collection endpoints paginate via meta.pageInfo, in the Linear / GraphQL cursor style.
curl -s "https://api.blockchainacademics.com/v1/articles/search?q=ethereum&limit=2" \
-H "X-API-Key: $BCA_API_KEY"{
"data": {
"items": [
{ "slug": "eth-pectra-mainnet", "title": "Pectra goes live on Ethereum mainnet", "published_at": "2026-04-20T12:00:00Z" },
{ "slug": "eth-staking-rate", "title": "ETH staking yield falls below 3%", "published_at": "2026-04-19T08:31:00Z" }
],
"total": 147
},
"attribution": {
"citations": [
{
"cite_url": "https://blockchainacademics.com/search?q=ethereum&src=custom&utm_medium=http&utm_campaign=search_news",
"as_of": "2026-04-22T10:12:00Z",
"source_hash": "sha256:b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3"
}
]
},
"meta": {
"status": "complete",
"request_id": "req_01HABCDEF0123456789ABCDEFK",
"pageInfo": {
"hasNextPage": true,
"hasPreviousPage": false,
"startCursor": "c_01HABCDEF000000000000AAAA",
"endCursor": "c_01HABCDEF000000000000BBBB"
}
}
}To fetch the next page, pass the endCursor as after:
curl -s "https://api.blockchainacademics.com/v1/articles/search?q=ethereum&limit=2&after=c_01HABCDEF000000000000BBBB" \
-H "X-API-Key: $BCA_API_KEY"Cursor format is server-internal: never parse or construct it client-side.
Example: degraded response (partial)
{
"data": {
"candles": [
{ "t": "2026-04-22T09:00:00Z", "o": 67200.5, "h": 67450.0, "l": 67100.0, "c": 67380.2 },
{ "t": "2026-04-22T10:00:00Z", "o": 67380.2, "h": null, "l": null, "c": null }
]
},
"attribution": {
"citations": [
{
"cite_url": "https://blockchainacademics.com/market/btc?src=custom&utm_medium=http&utm_campaign=get_ohlc",
"as_of": "2026-04-22T10:12:00Z",
"source_hash": "sha256:b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3"
}
]
},
"meta": {
"status": "partial",
"request_id": "req_01HABCDEF0123456789ABCDEFI",
"pageInfo": {
"hasNextPage": false,
"hasPreviousPage": false,
"startCursor": null,
"endCursor": null
},
"diagnostic": {
"reason": "Upstream returned 502 on 1 of 2 candles.",
"vendor": "coingecko",
"upstream_status": 502
}
}
}Errors
Errors return HTTP 4xx/5xx and an error body: they do not reuse the envelope shape.
{
"error": {
"code": "BCA_BAD_REQUEST",
"message": "`limit` must be between 1 and 200.",
"request_id": "req_01HABCDEF0123456789ABCDEFG"
}
}| HTTP | error.code | Meaning |
|---|---|---|
| 400 | BCA_BAD_REQUEST | Validation failure. |
| 401 | BCA_AUTH | Missing / invalid key. |
| 403 | BCA_TIER_LOCKED | Caller below required tier for the tool. |
| 404 | BCA_NOT_FOUND | Entity / article / slug not in corpus. |
| 429 | BCA_RATE_LIMIT | Rate-limited. Honor Retry-After header. |
| 5xx | BCA_UPSTREAM | Backend failure. Retry with backoff. |
Every error body carries request_id. It matches the X-Request-ID response header. Include it in any support ticket.
Integration-state responses are not errors. If a tool's upstream is not wired in yet, the response is HTTP 200 with meta.status: "unseeded" and a meta.diagnostic block: see Envelope → Status decision tree.
Pagination
List endpoints accept:
| Param | Default | Max | Notes |
|---|---|---|---|
limit | 10 to 50 | 200 | Per-endpoint max documented in Swagger. |
after | none | none | Opaque cursor from the previous page's endCursor. |
before | none | none | Opaque cursor for backward pagination (where enabled). |
Inspect meta.pageInfo on every response to decide whether to keep walking. Do not assume data.items.length < limit means you are done: some endpoints return up to limit per page even when more exist.
Rate limits
Rate limits are per key, not per IP. Live state lives in headers, not the body:
X-RateLimit-Limit: 1200
X-RateLimit-Remaining: 1187
X-RateLimit-Reset: 1713787200Each plan has a per-second rate limit and a monthly call quota, both listed on Pricing; over the limit you get HTTP 429 with Retry-After (see Rate-limit response). Agent loops with retries should cache. See Tiers & quotas for what changes between tiers.
Webhooks (v0.4)
Webhook delivery exists for monitor_keyword (its required webhook_url argument: HTTPS only, public hosts only, validated before the request leaves) and is a Pro feature alongside agent-job completion; see Pricing. Payloads reuse the canonical envelope shape, including meta.request_id for idempotency, and are signed with HMAC-SHA256 for verification through the X-BCA-Signature header. The completion payload looks like this; the final field list is documented here when the spec is locked:
{
"event": "job.completed",
"job_id": "job_a3f2c891",
"tool": "generate_due_diligence",
"result_url": "https://api.blockchainacademics.com/v1/agents/jobs/job_a3f2c891",
"completed_at": "2026-04-20T17:36:42Z"
}Failed deliveries are retried with exponential backoff over 24 hours.
Next
- Live Swagger: interactive, click to try.
- OpenAPI JSON: for codegen.
- Response envelope: field-by-field breakdown.
- Citations: why
citations[]is an array. - Tool reference: the MCP view of the same surface.