For the complete documentation index, see llms.txt. This page is also available as Markdown.

The Analysis API

The same analysis the bot and the Web Analyzer run, as JSON.

If you're building something — an agent, a dashboard, a bot of your own — this is the surface to build on. It answers with typed fields instead of a rendered card, so nothing has to be scraped or parsed out of Markdown.

The one guarantee

POST /v1/analyze runs the same code path as /don in Telegram and as analyzer.digitaldon.net. Not a reimplementation, not a copy kept in step — literally the same pipeline, in the same process, sharing the same cache. The same token at the same moment scores the same on all three.

It's for agents, and it's paid

The Telegram bot and the Web Analyzer are free, and stay free. A human reads one card at a time.

A machine doesn't. An agent can pull the analysis in a loop, and the market data underneath it — prices, candles, holder graphs — is metered and billed to us. So the API is priced per call, in USDC, over x402: the HTTP-native payment protocol agents already speak.

Roughly a cent for a standard scan. No account, no API key, no invoice — your agent pays per request and gets an answer.

Calling it

curl -X POST https://<your-bot-host>/v1/analyze \
  -H 'Content-Type: application/json' \
  -d '{"token": "BONK"}'

First call comes back 402 with the price and the chains we accept:

HTTP/1.1 402 Payment Required
PAYMENT-REQUIRED: <base64 payment requirements>

Your agent signs a payment and retries with a PAYMENT-SIGNATURE header. Any x402 client library does this for you. The answer comes back 200 with the receipt in PAYMENT-RESPONSE.

We accept USDC on Base and on Solana — both are offered in the same 402, so your agent pays on whichever chain it holds funds on.

A GET form takes the same parameters, for quick checks and browser testing:

Request

Field
Type
Default
Meaning

token

string, required

—

Ticker, name, or contract address

chain

string

auto

sol, eth, base, bsc, rh, arb, mnt, or auto

depth

string

basic

Whichever of basic, holders, social, full this deployment sells — see below

options.include_chart_raw

bool

false

Return the candles the analysis ran on

options.include_raw_metrics

bool

false

Return every engine field, unshaped

Depth costs money and time. basic is the chart and the on-chain read. holders adds a wallet-cluster scan, social adds an X credibility check on the token's linked account, full adds both. Ask for what you'll actually read.

The menu is whatever the deployment can actually deliver. A depth whose feature is switched off is dropped from the depth enum, priced nowhere, and refused with depth_unavailable before any payment is taken — we would rather not sell you a block that comes back empty. At the time of writing social and full are not live (the X credibility check is off), so the live menu is basic and holders. Don't take that from this page: read the depth enum in /v1/openapi.json, or call the free get_pricing MCP tool. Both are generated from the same table the server charges from.

chain: auto searches every active chain and analyses the deepest pool. The others come back in data.request.alternatives — there's no interactive picker over HTTP, so if you wanted a different one, re-request with an explicit chain.

Response

Two version fields, and they move independently. schema_version is the shape of the document — parse against it. engine_version is the analysis contract that produced the numbers — cache against it. A scoring change bumps the second and leaves the first alone.

Know what you're looking at

data.token.asset_class is crypto or stock, and it's always there.

stock means the token is a tokenized claim on a listed security, matched against a verified issuer registry rather than guessed from the chain. When it's set, data.token.underlying names what's behind it:

The engine agrees with the field. A tokenized equity is scored on the STOCK profile — chosen by what the token is, not by its age — so analysis.profile.key reads STOCK and the short horizon reads days rather than 1d. The two move together; you can branch on either.

Branch on it before you apply any priors. Robinhood Chain is an Arbitrum Orbit L2 built for tokenized equities — a token there is usually a share, not a meme. Scoring a tokenized NVIDIA the way you'd score a week-old memecoin produces a confident, wrong answer, and the chain id alone won't stop you: the same NVIDIA is also listed on Solana as NVDAx by xStocks. That's what dual_listed is telling you, and the other route is in data.request.alternatives.

market_status is the underlying equity market, not the token's pool. The token trades around the clock; NVIDIA does not. A read taken at 03:00 ET is looking at a stale reference price, and you should know that before you act on it.

Holder depths aren't sold for tokenized stocks. Their supply sits with the issuer and the exchanges custodying it, so clustering would report custody wallets as insiders. Ask for holders on one and you get a warning saying so, a null block — and no charge.

What it costs

Depth
Price
What you get

basic

$0.01

Chart analysis + on-chain read

live

social

$0.03

...plus X credibility

not live

holders

$0.05

...plus wallet clustering

live

full

$0.08

Everything

not live

Plus $0.01 for include_chart_raw, $0.005 for include_raw_metrics.

Priced per depth because the cost per depth is genuinely different — a basic scan is two candle fetches, a holders scan walks a wallet funding graph. Flat pricing would just mean shallow calls subsidising deep ones.

The live price list is always in the x-402 block of /v1/openapi.json, generated from the same table the server charges from. Only depths this deployment can actually deliver are listed — and only those can be bought.

Free trial: one call per day per IP at basic, no payment needed, so you can see the shape of a real response before wiring up a wallet.

You only pay for a complete answer

Two guarantees, and they're enforced in code rather than promised in a FAQ:

A failed scan is free. Payment is verified before the analysis runs and settled only after it succeeds. If our upstream data source falls over, the money never moved. There's no refund process because there's nothing to refund.

A partial answer is free too. If any block you paid for comes back empty — the chain has no holder backend, the token has no X account, something upstream failed — you get depth_undeliverable and you are not charged. Retry at a depth that fits the token; basic always works.

The one case where you pay and get less than you hoped: the analysis ran fine and simply says the token looks bad. That's a complete answer.

Three things that trip people up

A missing block is not a failure. If a depth can't be served — the feature is off, the chain has no holder backend, no X account is linked, an upstream call failed — you get the blocks that did work, null for the one that didn't, and an entry in data.warnings explaining which and why. Status stays 200. The chart analysis was already paid for; throwing it away because the holder scan failed would help nobody.

No analysis is still an answer. A token too new to have candles returns 200 with analysis.available: false. Check analysis.state:

  • fresh_launch — too young for a chart to exist yet

  • dormant — old enough, but it has barely traded since launch

Those are different findings and shouldn't be described the same way. A four-month-old token that stopped trading is not a new launch. Either way, data.onchain is populated — liquidity ratio and unlock overhang need no candles, and at that age "is this a rug" is the more useful question than "where's the entry".

Zones are null, never zero. No short-term setup and a setup at $0 are not the same thing. If levels.short_term.entry is null, check no_setup_reason — it'll tell you the band was too narrow to trade.

Prefer tools over HTTP?

If you're wiring this into an AI agent rather than writing HTTP calls yourself, use the MCP server instead — same analysis, same pricing, but the agent discovers and calls it on its own.

Finding your way around

GET / returns a small index — what the service is, where the endpoints are, and whether payment is switched on:

Handy if you've been handed a hostname and nothing else.

The contract

An OpenAPI 3.1 document, built from the running configuration — the chain enum is that deployment's active chains, the rate limit is the one actually enforced. Point your client generator (or your agent) at that URL rather than a copy you keep locally, and it can't drift from the server answering you.

It's free to read, and it carries three complete worked responses — a basic call, a holders call, and a tokenized stock — so you can see exactly what an answer looks like before you spend anything. Those are generated by the same code that builds a real response, so they can't drift either.

Errors

Failures use the same envelope with ok: false, a machine-readable error, and a detail string written to be read.

Code
Status

invalid_request

400

Malformed body, missing or oversized token

invalid_depth

400

depth wasn't one of the documented values

depth_unavailable

400

That depth isn't sellable here right now — detail names what is

payment_required

402

Pay and retry. Price is in the header and in accepts

payment_failed

402

Settlement failed, result withheld. No funds moved

depth_undeliverable

402

Couldn't serve this token at that depth — not charged

unknown_chain

400

Not a chain this API knows

inactive_chain

400

Known, but switched off on this deployment

token_not_found

404

No pool matched

feature_disabled

404

The endpoint is switched off

no_chart_data

422

Resolved, but the OHLCV source returned nothing — transient

rate_limited

429

Per-IP limit exceeded

internal

502

An upstream call failed — transient

payment_unavailable

503

Payment layer unreachable. Fails closed — transient

no_chart_data and internal are worth a retry. The 400s are not — fix the request.

Rate limits

Six requests per minute per IP by default, with its own budget: an API caller can't drain what the Web Analyzer needs. Deep scans are additionally bounded by a server-side concurrency cap, so a burst queues rather than failing.

Asking for a price is cheap and has its own, much higher limit — the probe-then-pay handshake never eats into your scan budget.

What's cached, and for how long

Published, because "is this a live read or a cached one" is a fair thing to want answered before you build on a paid source — and the only other way to find out is to buy two calls and compare. The live numbers are in the x-cache block of /v1/openapi.json, read from the modules that enforce them:

Default

Analysis document

60s

Repeat calls for the same token inside the window are served from one memoised scan — identical numbers

Candles behind it

90s

Maximum age of the OHLCV frame the analysis ran on

Holder block

900s

The wallet-cluster scan is expensive upstream

Social block

1800s

Per X handle

The cache is shared with Telegram and the Web Analyzer — that's the same-answer- everywhere guarantee at the top of this page, made of one memoised scan rather than three.

Two things worth being plain about. generated_at is when the document was built, not when the data was read — check engine_version, not the timestamp, when you cache your own copies. And a cache hit is still charged: the price is for the answer, not for our upstream request. The cache is in-memory and per-process, so treat every number above as a freshness bound rather than a guarantee.

Not reachable from a browser

There's no CORS grant on /v1/analyze. A web page can't spend the API on a visitor's behalf. Call it server-side, from your agent — which is where an agent lives anyway.

The spec at /v1/openapi.json is browser-readable. Reading a price list isn't spending it.


None of this is financial advice. It's an opinion with arithmetic behind it.

Last updated

Was this helpful?