TokenPrices: onchain token prices, read directly from DEX liquidity.

Prices for a specific chain and contract, with the pool, block and route shown, and clear limits on what we cover.

TokenPrices computes USD prices from raw DEX pool state read over JSON-RPC at a pinned, finality-tagged block (a finalized slot on Solana, a closed ledger on Stellar). It does not resell or proxy any other price feed. Each response names the pool(s) and route (at most 2 hops to a USD anchor), the exact block, a quality status, the basis and the USD anchor assumption. When we cannot qualify a price we return price_usd: null and a reason. We never make up a price.

Machine-readable contract: /openapi.json (OpenAPI 3.1).

Quick start

Examples use BASE=http://127.0.0.1:8788. Every endpoint is a plain GET.

BASE=http://127.0.0.1:8788

# Current spot price of WETH on Base
curl -s "$BASE/v1/prices/base/0x4200000000000000000000000000000000000006"

# Effective sell price for $1,000 of WETH (fees + price impact included)
curl -s "$BASE/v1/prices/base/0x4200000000000000000000000000000000000006?mode=indicative&notional_usd=1000"

# Also serve thin-market (indicative) prices instead of null
curl -s "$BASE/v1/prices/base/0x4200000000000000000000000000000000000006?min_quality=indicative"

# Solana: WSOL by its mint (base58, case-sensitive)
curl -s "$BASE/v1/prices/solana/So11111111111111111111111111111111111111112"

# Several tokens in one request (up to 50, answered in request order; costs 5 units)
curl -s "$BASE/v1/prices/base?contracts=0x4200000000000000000000000000000000000006,0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"

# Every priced token on a chain (paginated, up to 200 per page; costs 5 units)
curl -s "$BASE/v1/prices/solana?limit=200"

# Last 24 h of 5-minute samples, hourly buckets
curl -s "$BASE/v1/prices/base/0x4200000000000000000000000000000000000006/history?interval=1h"

# Find a token, list pools, check health
curl -s "$BASE/v1/tokens/search?q=WETH"
curl -s "$BASE/v1/pools?chain=base&eligible_only=true"
curl -s "$BASE/v1/health"

# With an API key (higher rate limit)
curl -s -H "x-api-key: tp_..." "$BASE/v1/chains"

Conventions

Endpoints

GET/v1/prices/{chain}/{contract}

Current spot or indicative USD price, with route, block, depth and quality.

ParamValuesDefaultNotes
modespot | indicativespotIndicative = effective price when selling notional_usd of the token, including fees and price impact. Not a firm quote.
notional_usd100 | 1000 | 10000100Used by mode=indicative; with spot it picks the cost shown in depth.
min_qualityqualified | indicativequalifiedSee the quality gate above.
allow_staletrue | falsefalseServe the last sampled price when stale.

Response fields: price_usd, basis (onchain_spot|onchain_indicative|usd_peg_assumption), quality, reason, reasons, estimated_usd_anchor (e.g. USDC≈USD), as_of (block, hash, times, finality), source (pool, protocol, route, hops; hop fee_ppm = total fee, v4 protocol fee included), depth, shares_per_token (issuer multiplier; pending change in next), price_usd_per_ui_unit (see Solana), warnings.

GET/v1/prices/{chain}

Many prices in one request; costs 5 rate-limit units. With contracts (1–50 comma-separated addresses) you get one item per address in request order, unknown addresses included as UNSUPPORTED_TOKEN; limit/offset are null. Without contracts you get every sampled token on the chain, ordered by address, paginated by limit (default 100, max 200) and offset, with total. Query params apply to every item. An invalid item: 400 INVALID_ADDRESS (details.index).

GET/v1/prices/{chain}/{contract}/history

Stored end-of-block samples. Points below the quality gate are omitted. Costs 5 rate-limit units.

ParamValuesDefaultNotes
from, toISO 8601 or unix secondslast 24 hRange at most 7 days (2016 points at 5m).
interval5m | 1h | 1d5m1h/1d: OHLC bars, price_usd = close; max 90 / 2,190 days.
min_qualityqualified | indicativequalified

GET/v1/prices/{chain}/{contract}/chart

Fixed-range series: range = 24h (5m), 7d|30d|90d (1h), 1y|max (1d); default 7d. Same response as /history plus range. 5 units.

GET/v1/prices/{chain}/{contract}/ticks

Recent per-swap (Solana: per-state) prices from the real-time engine, in memory only. since (default now − 10 min, at most 60 min back), limit 1–500. Empty with reason when gated or the engine is unavailable. 5 units.

GET/v1/prices/{chain}/{contract}/depth

Depth chart of DEX liquidity (AMM, not resting orders): cumulative bids/asks per level over ±range% (0.1–20, default 10) in buckets (1–200, default 50), per-pool split, impact ladder (0.5/1/2/5%). 5 units.

GET/v1/pools

Registered pools with their latest state (prices both ways, liquidity, cost, tier, reasons). Active pools only by default (total = counts.pools). Pool fee_ppm is the LP fee tier (it excludes any v4 protocol fee).

ParamValuesDefaultNotes
chainchain keyrequired
tokenaddressPools containing this token.
protocoluniswap-v2, uniswap-v3, aerodrome-volatile, aerodrome-stable, aerodrome-slipstream, uniswap-v4, pancakeswap-v3, pancakeswap-v2; Solana: raydium-amm-v4, raydium-cpmm, raydium-clmm, orca-whirlpool, meteora-dlmm; Stellar: stellar-classic-amm, aquarius-cp|stable, sushi-v3-stellarMeteora DLMM pools carry bin_step.
eligible_onlytrue | falsefalseOnly tier A or B pools.
include_inactivetrue | falsefalseAlso list seeded pools that are not sampled (active: false).
limit, offsetintegers50, 0

GET/v1/tokens/search

Search curated tokens by full address, symbol, name prefix or asset ticker. Results include verified, quality and the default-gate price_usd.

ParamValuesDefaultNotes
q1 to 64 charactersrequired
chain, classchain key, asset class
limit1 to 5020

GET/v1/tokens/{chain}/{contract}

Token metadata, a price summary and up to 20 pools. Unlike the price endpoint, an unknown token is 404 NOT_FOUND (catalogue lookup).

GET/v1/assets

Curated assets with asset_class, tokenized, form and stablecoin peg (asset_type deprecated). Only USD stablecoins are $1 anchors; catalogue_only tokens (e.g. BUIDL, BENJI) are never priced.

ParamValuesNotes
class, tokenized, formenum, true|false, enumTaxonomy filters (type: deprecated).
issuerstringExact, case-insensitive.
q1 to 64 charactersTicker or name prefix.
limit, offsetintegers

GET/v1/assets/{id}

One asset (e.g. crypto:ETH, URL-encoded) with its tokens. Unknown id: 404 NOT_FOUND.

GET/v1/icons/chain/{chain}, /v1/icons/protocol/{protocol}, /v1/icons/asset/{id}, /v1/icons/token/{chain}/{contract}

One icon as {kind, id, icon}; redirect=1&size=64: 302 to the image. Catalogue items carry icon (pools: protocol_icon).

GET/v1/chains

Chains: status, finality, protocols, anchors, counts, last sample. finality_tag is what is pinned (Robinhood: latest, confirmations: 30). Each chain has a family (evm, svm or stellar); chain_id is null off EVM.

GET/v1/health

Service and sampler health: ok when every active chain has a good run within 15 minutes, degraded otherwise, and 503 down if the database is unreachable.

GET/openapi.json

The OpenAPI 3.1 document for this API.

Quality statuses

Every price carries one quality. Only QUALIFIED is served by default.

StatusServed by defaultMeaning
QUALIFIEDyesThe best route is tier A at every hop, with no divergence, in bounds and fresh.
with min_quality=indicativeThe best route is tier B: thin liquidity, or high cost at $100.
with min_quality=indicativeAnother tier-A route disagrees by more than 500 bps.
with min_quality=indicativeThe token's units are ambiguous (unknown rebase type, or a non-1:1 wrapper whose conversion we do not read); the price is per pool accounting unit. Tokenized stocks with a share multiplier are not in this bucket: their price is USD per whole token and the multiplier is shown as shares_per_token.
with allow_stale=trueThe last good sample is older than 15 minutes, its block is too old, or it was invalidated by a reorg.
NO_RELIABLE_MARKETneverPools exist but none pass the eligibility rules, or the price is out of bounds.
ANCHOR_UNAVAILABLEneverEvery candidate route needs an intermediate anchor (e.g. WETH) that could not be priced at tier A.
NOT_YET_SAMPLEDneverThe token is in the registry but has not been sampled yet.
UNSUPPORTED_TOKENneverThe chain and contract are not in the curated registry, or the token is inactive.

Pool tiers: A = liquidity at least $10,000 and cost at most 200 bps for $100; B = indicative only; X = ineligible (liquidity under $1,000, cost over 1,000 bps, or read failure). Liquidity is 2 × the smaller side in USD.

Reason codes

reason is the most specific code; reasons lists all of them, gate reason first.

CodeMeaning
BELOW_MIN_QUALITYA price exists but is below the requested min_quality.
NO_POOLSNo registered pools for the token.
NO_ROUTE_TO_ANCHORNo 1 or 2 hop route reaches a USD anchor.
POOL_READ_FAILEDReading pool state failed at the pinned block.
ZERO_LIQUIDITYPool has zero liquidity or a zero price.
LIQUIDITY_BELOW_MINIMUMLiquidity under $1,000 (tier X).
LIQUIDITY_BELOW_QUALIFIEDLiquidity under $10,000 (tier B).
PRICE_IMPACT_TOO_HIGHSelling $100 costs more than 1,000 bps (tier X).
PRICE_IMPACT_ABOVE_QUALIFIEDSelling $100 costs more than 200 bps (tier B).
INSUFFICIENT_DEPTHNot enough depth for the requested notional.
QUOTE_UNAVAILABLEThe quote for that notional failed.
ANCHOR_UNAVAILABLEThe intermediate anchor could not be priced.
PRICE_OUT_OF_BOUNDSComputed price outside 1e-18 to 1e12 USD.
DIVERGENT_MARKETSQualified routes disagree by more than 500 bps.
STALE_SAMPLELast sample older than 15 minutes.
BLOCK_TOO_OLDThe sampled block is more than 30 minutes old.
REORGEDThe sampled block was reorged out.
NOT_YET_SAMPLEDNo sample yet.
TOKEN_NOT_IN_REGISTRYUnknown chain and contract pair.
TOKEN_INACTIVEToken is in the registry but inactive.
CHAIN_INACTIVEThe chain is not being sampled.
PROBE_TOO_LARGE, DECODE_ERROR, UNKNOWN_FACTORYPool-level failures: quote probe too large, undecodable return data, or a pool whose factory is not in the registry.
UNKNOWN_PROGRAM, POOL_ACCOUNT_MISMATCHSolana pool-level failures: the pool account is not owned by a registry program, or its mints/vaults differ from the registry.
ANCHOR_DEPEGGEDA USD stablecoin anchor is over 50 bps from $1 against the chain's other stablecoins (recovers under 30 bps): priced from markets, routes through it repriced and capped at indicative.
DEPTH_UNAVAILABLENo depth book (engine down, none stored); 404 detail.
MINT_READ_FAILED, TOKEN_PAUSED, TRANSFER_HOOK_UNSUPPORTED, TOKEN_EXTENSION_UNSUPPORTEDSolana Token-2022 gating: the mint could not be read, is paused, has an unapproved transfer hook, or is non-transferable. The token is not priced.
ISSUER_AUTH_REQUIRED, ISSUER_READ_FAILEDStellar: the issuer must authorize every holder (not an open market), or its account could not be read. Not priced.

Warnings

CodeMeaning
USD_PEG_ASSUMEDA USD stablecoin anchor is valued at exactly 1 USD.
ANCHOR_DEPEG_SUSPECTEDStablecoin anchors disagree with each other.
ANCHOR_PEG_UNVERIFIEDThe USD stablecoin this price rests on could not be checked against other markets; still valued at $1 (see source.anchor_peg).
QUOTE_UNAVAILABLEA depth quote failed; the pool was capped at tier B.
PROVISIONAL_FINALITYRead at latest (not yet safe or finalized), e.g. Robinhood Chain.
FINALITY_TAG_FALLBACKThe RPC rejected the finality tag; read at latest minus N blocks.
REBASING_OR_WRAPPED_TOKENThe token rebases or has an issuer share multiplier (shares_per_token); the price is USD per 1 whole token as traded. Unknown units: INDICATIVE_UNITS_UNVERIFIED.
STALE_SERVED, INDICATIVE_SERVEDA stale or indicative price was served because you asked for it.
DEPTH_ESTIMATEDA concentrated-liquidity pool on the route (Solana, Sushi on Stellar) was quoted with a single-range estimate.
TRANSFER_FEEThe Token-2022 mint charges (or has scheduled) a transfer fee; it is included in depth quotes.
TRANSFER_HOOK_PRESENT, PERMANENT_DELEGATE, PAUSABLEInformational Token-2022 facts: an approved transfer hook, an issuer permanent delegate, or a pausable (not paused) mint.
MULTIPLIER_CHANGE_PENDINGA scheduled multiplier change; see shares_per_token.next.
AUTH_REVOCABLE, CLAWBACK_ENABLEDStellar: the issuer can freeze holders, or claw balances back.

Solana

Stellar

Errors

{ "error": { "code": "INVALID_ADDRESS", "message": "contract must be a 20-byte hex address", "details": { "param": "contract" } },
  "request_id": "6f1c2b8e-3a1d-4b44-9a57-1f3c0e0b9d21" }
HTTPCodeWhen
400INVALID_PARAMETERBad enum, range or time, or a range that is too large.
400INVALID_ADDRESSMalformed address.
400UNSUPPORTED_NOTIONALnotional_usd not 100, 1000 or 10000.
401INVALID_API_KEYUnknown or revoked x-api-key.
404UNSUPPORTED_CHAINChain not supported.
404NOT_FOUNDUnknown route, asset or catalogue token.
405METHOD_NOT_ALLOWEDAnything but GET, HEAD or OPTIONS on /v1.
429RATE_LIMITEDRate limit exceeded; see Retry-After.
500INTERNALUnexpected error.
503DB_UNAVAILABLEDatabase unavailable.

Rate limits and API keys

Coverage and limits

We would rather return null with a reason than a number we cannot stand behind. Read these limits before relying on a price.

TokenPrices API v1 · openapi.json · health · Attributions & removal
Logos and icons are trademarks of their respective owners. TokenPrices shows them unaltered, only to identify the chain, protocol or token concerned. Their display does not imply any affiliation with or endorsement by the owner. To request removal, contact legal@tokenprices.xyz.