A read-only API, because portability has to be true in an endpoint
Every tool in this market lets you in easily. Leaving is where they differ. This is a documented HTTP API over your own scans — the ledger, the disposals with their full lot lineage, the decisions, the holdings and the completeness report — and it exists so that "your data is yours" is something you can execute rather than something we say.
curl -H "Authorization: Bearer cb_ro_YOUR_TOKEN" \
https://clearbasis.tax/v1/scans
Mint a token in your dashboard.
X-API-Key works too. A browser cookie does not, deliberately.
Read your own scans: transactions, disposals and the lots behind them, decisions, open holdings, the reconciliation, and the free ledger CSV.
Write, delete, pay, resolve a decision, or act on your behalf in any way. Not because a scope says so — the router registers GET routes and nothing else, and the token table has no write concept to widen.
Endpoints
/v1/whoamiConfirms a token works and states what it can do — which is read, and nothing else. Use it as the first call when wiring something up.
/v1/scansYour scans, newest first: id, status, what each was built from, and whether it is saved. limit up to 200.
/v1/scans/{id}The computed answer — true gain, ordinary income, the blank-basis overstatement, coverage — with the completeness report attached. The reconciliation travels in the same payload on purpose: anything pulling a gain figure into another system should receive, at the same moment, how much of it rests on a cost record we could not find.
/v1/scans/{id}/transactionsThe normalised ledger, paged (offset, limit up to 500). Each row carries its evidence — which file and which row it came from.
/v1/scans/{id}/disposalsEvery disposal with its full lot lineage: which lots were consumed, their per-unit basis, when they were acquired, and whether any was a conservative default. A gain figure with no lineage cannot be checked by the person receiving it.
/v1/scans/{id}/decisionsWhat the engine refused to guess, each with its options and what each option would do to the numbers.
/v1/scans/{id}/holdingsOpen lots: what is still held, at what basis, acquired when, and which lots have no acquisition record behind them.
/v1/scans/{id}/reconciliationThe import-completeness report on its own — what a missing file is costing in dollars, by asset and by venue. Documented at /import-check.
/v1/scans/{id}/ledger.csvThe same free CSV the web app offers, for scripts. Universal template, so it re-imports here and anywhere else that reads the format.
What comes back that usually doesn't
The evidence. Every disposal carries the source file, the row, the transaction hash and the price source behind its figures, and every response is stamped with the engine version and the dated ruleset that produced it. An API that hands you numbers you cannot trace is a slower spreadsheet — and a figure whose rules you cannot name is one you cannot defend two years later, which is exactly when somebody asks.
{
"disposals": [{
"asset": "BTC", "qty": "0.5",
"proceeds_usd": "30000.00", "basis_usd": "10000.00", "gain_usd": "20000.00",
"lots": [{ "lot_id": "L00012", "qty": "0.5",
"unit_basis_usd": "20000", "acquired_ts": "2021-05-01T12:00:00+00:00",
"conservative_default": false }],
"evidence": [{ "source": "coinbase_csv", "ref": "row:184" }]
}],
"_meta": { "engine_version": "0.1.0", "ruleset_version": "2026.08-r1",
"api": "v1", "read_only": true }
}
Questions
Can a token write, delete or pay for anything?
No, and not because a flag says so. The API router registers GET routes and nothing else, and the token table has no concept of a write scope — there is no value to widen. Making a write token would mean building a different thing on purpose, which is a much harder mistake to make by accident than changing a string in a column.
Where do tokens live, and what happens if one leaks?
We store a sha256 and the first few characters, never the value — it is shown once, when you mint it. Every token starts with cb_ro_ so a secret scanner can recognise one on sight. Revoke it in your dashboard and it stops working immediately; revocation is permanent, never a soft state a later request could undo. And the worst a leaked one can do is read your own scans, because there is no organisation-wide or admin token in this product at all.
Why is a browser cookie not accepted?
Because a credential a browser attaches automatically is how a read API becomes a cross-site data leak. A caller has to attach the token deliberately, on every request.
Is the API behind the paywall?
The ledger and the computed answer are not — they are your data and the arithmetic on it. What stays paid is the produced paperwork, the same as everywhere else in this product.
Why does every response carry a ruleset version?
Because tax rules change and a number without the rules that produced it cannot be reproduced later. Every payload states the engine version and the dated ruleset, so a figure you pulled last year can be recomputed and compared rather than argued about.
What happens if I resolve a decision after pulling data?
The next call reflects it. Nothing is cached: every response is recomputed from your sources at request time, because a stored blob goes stale the moment a decision is answered, and an API that disagrees with the screen is worse than no API.
Can I use this to move to another provider?
Yes, and that is the point of publishing it. Between this, the free ledger CSV in the universal template and the <a class="cite" href="/basis-passport">Basis Passport</a> — whose spec is public and which anyone, including our competitors, may implement — there is no version of leaving that requires our permission.
Get a token
Run a free scan, save it, and mint a read-only token from your dashboard. No card, and nothing about the API is a paid tier.