Docs
Semantics
Most explorer bugs are not crashes — they are confident answers to questions the backend cannot actually answer yet. This page is the contract that prevents them. It is short; build against it and staleness, misses and races become states you can handle instead of surprises.
Canonical hashes
Every hash — txid, block hash, merkle root — is display byte order, the order Bitcoin Core and every explorer print. URLs using the legacy internal byte order still resolve: they answer 301 Moved Permanently to the canonical form, so old links never break and never fork the URL space.
curl -sI https://api.btcintel.io/v1/tx/<internal-order-hex> | grep -i location
location: /v1/tx/<canonical-hex>One error envelope
{ "error": { "code": "...", "message": "...", "request_id": "..." } }| HTTP | code | Meaning |
|---|---|---|
| 400 | invalid_input | Malformed hash, height, or an address for the wrong network |
| 404 | not_found | Not in the index and, for transactions, not in the node mempool either |
| 409 | not_indexed_yet | Confirmed on-chain, but above what the index has processed — retry later, it will appear |
| 500 / 503 | internal | Our fault or a backend down. The message says which; quote the request_id when reporting |
not_indexed_yet is the deliberate one: while the index trails the node, a confirmed transaction above the index tip is not reported as nonexistent. You get a 409 that says exactly what is known.
Index lag is disclosed, never hidden
X-Btcintel-Index-Height: 323207
X-Btcintel-Node-Height: 965950- Data derived from the index (address balances, block lists) is as of the index height. The headers say so on every response; the web UI banners it whenever lag exceeds 2 blocks.
- Confirmations are always computed against the node tip, never the index tip — a lagging index must not undercount how deep your transaction is buried.
/readyanswers 503 while lag exceeds 2 blocks, so load balancers and uptime checks share the same honesty.
Caches cannot lie either
Error responses carry Cache-Control: no-store: a 404 or 409 is a statement about now, and it must flip the moment the index catches up. Canonicalization redirects are cacheable for a day (permanent by definition); hash-addressed content is immutable for a week — a reorged-away block still serves its own bytes under its own hash, which remains true forever.
Three bands that never fuse
Every subject page — address, transaction, block — is three bands, and the API’s siblings map onto them one to one. Chain is what the index and the extractors say: facts, the coinbase observation, transaction observations, the template audit. Curated · btcintel is what btcintel claims with its sources: annotations, attributions, exposure and relations, each row with its method, source, fetch, confidence class and level, validity and citation. Community is what others said on Nostr, shown as they signed it and never merged with the bands above. Nothing in one band is computed from another, and a list never mixes them.
Absent is not empty
A sibling that was never computed is absent from the response (no key); one that was computed and found nothing is []. The web draws the first with a dashed frame (“not computed yet”) and the second with a solid one (“computed · none”). classification is absent while the extractor has no raw transaction to read; observation and audit are absent for blocks the engine never saw; attributions: [] means the matcher ran and no catalog matched, and the page then says “no attribution”, never “unknown miner”.
Claims are a set, not a verdict
Labels come from more than one source, and sources may contradict. The API returns each assertion with its source, attribution text, fetch, confidence class and level; it does not collapse them into a score, and the web draws no colour scale. If two sources disagree about an address you see both claims under a “sources disagree” marker. Attributions work the same way: the read policy orders them, disputed is true when the two best rows name different entities, and no winner is stored.
Citations you can verify
btcintel:annotations:9990@v1 sha256:f861bfd04e4f577a
curl -s "https://api.btcintel.io/v1/claims/ann_9990?version=1" | jq -r .content_sha256
# starts with f861bfd04e4f577a: the row is the one the citation namesEvery claim row carries btcintel:<table>:<id>@v<version> sha256:<first 16 hex of the content hash>, and the copy action copies exactly that. Versions are immutable: a claim that expires or is retracted keeps every version addressable, with the date and the reason. ?include=expired on a subject page adds the claims that are no longer current, marked as such.