TokensAPI

Multichain assets

Assets and variants, chain ids and CAIP-2, <chain>:<address> keys, capability flags, primary variant selection, and how per-chain stats roll up.

The v2 asset surface is chain-native: the same curated assets as v1, with their tokens on Solana, Ethereum, Base, Arbitrum, Polygon and BNB. This page is the model; the routes are on Chains, Assets and Variants, and the TypeScript types are in Shared shapes.

Two nouns

  • An asset is the curated, chain-agnostic thing: usd, bitcoin, tesla. It lives under GET /v2/assets and GET /v2/assets/{assetId}.
  • A variant is one token on one chain that represents an asset. It has exactly one identifier, the key <chain>:<address> (solana:EPjF…, base:0x8335…), and lives under GET /v2/variants/{chain}/{address} and the batch GET /v2/variants?keys=.

Every v2 asset is curated. There are no synthetic per-mint assets: an address-shaped {assetId} is 400 invalid_asset_id, and a token the registry does not know is 404 variant_not_found (or missing[] in a batch). For unregistered Solana tokens use token lists or search.

Chains and CAIP-2 ids

The chain list is compiled into the API, so adding a chain is a release, never a data change. Cache GET /v2/chains freely.

idcaip2FamilyExplorer
solanasolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpsvmSolana Explorer
ethereumeip155:1evmEtherscan
baseeip155:8453evmBasescan
arbitrumeip155:42161evmArbiscan
polygoneip155:137evmPolygonscan
bnbeip155:56evmBscScan

The id column is the chain value on every variant and the value every chain= param and <chain>:<address> key accepts. Provider-side slugs (polygon-pos, arbitrum-one, bsc) are not accepted and return 400 with details.code: "invalid_chain". Each chain also publishes a caip2 id (CAIP-2), an evmChainId (null for Solana) and an explorer URL template with an {address} placeholder.

Variant keys

A 0x address is the same string on every EVM chain, so no v2 input is a bare address: the chain is a path segment (/v2/variants/{chain}/{address}) or the key prefix (keys=base:0x…).

  • EVM addresses are 0x plus 40 hex characters. They are accepted in any casing and always returned lowercase. Compare keys, not checksummed strings.
  • Solana addresses are case-sensitive base58, as in v1.
  • A malformed address is 400 invalid_address with details.expected set to base58 or 0x40hex.

Capabilities per chain

GET /v2/chains publishes a capability flag set per chain. The flags are the contract for what the rest of the surface can return for a variant on that chain; gate your UI on them, not on chain === 'solana'.

FlagMeaning
marketsPer-variant market snapshot (price, liquidity, volume)
dexMarketsPer-pool DEX market lists
ohlcvCandles
riskRisk summary and details; also required for pegHealth
executionQualityFill-quality snapshots
swapSwap and trade links, execution routing; defines the "executable" pool below
firstPartyLogostokens.xyz-hosted logo mirror

Solana has every capability. The EVM chains have markets only: no OHLCV, risk, DEX market lists, execution quality, swap routing or first-party logos. When an EVM capability is switched on it flips in GET /v2/chains first, so a client that gates on the flags needs no code change.

What this means for variants: market is populated on every chain where a snapshot exists. Execution quality and peg health are opt-in on the asset detail (GET /v2/assets/{assetId}?include=executionQuality,pegHealth, plus preStocks for Solana PreStocks tokens) and are null on chains without the executionQuality or risk capability. OHLCV, risk, DEX market lists and swap routing are not exposed on v2 variants; use the v1 asset routes for Solana mints.

All chains by default

An asset's chains, per-chain chainStats, cross-chain aggregate, stats, primaryVariant and primaryVariantByChain are always computed from its full variant set.

chain= narrows what is listed, never what is summed. On GET /v2/assets it keeps only assets with at least one variant on that chain and, with include=variants, lists only that chain's variants. On GET /v2/assets/{assetId}/variants it filters the rows. The numbers above do not change, and each variant's rank stays its liquidity rank within the whole asset across chains.

Per-chain stats, aggregate and reference market

  • chainStats.<chain> is that chain's on-chain view. Liquidity, 24h volume and trade count are summed over every variant that passes the quality guard; market cap, FDV and supply only over variants whose issuance is native. A bridged or wrapped token is a claim on supply issued elsewhere, and a derivative (liquid staking token, ETF share, leveraged or basket product) is exposure to the asset rather than its supply. excludedFromSupplyCount says how many were left out and excludedByIssuance why. Issuance comes from an explicit tag, else a curated per-deployment override, else the variant kind. A derivative kind only means "derivative of this asset" when the asset has some other deployment: when every variant is a yield, ETF, leveraged or basket token (a standalone treasury fund, say) those variants are the asset and count as native. Commodities are the exception, since an ETF share is never the metal.
  • The quality guard. Before a market row enters any sum it must be fresh (a successful provider write within 2 h on Solana, 3 h on EVM), not dust (liquidity and 24h volume both under $1k, unless nothing else survives), and priced within 25% of the reference price, or without one, of the surviving rows' liquidity-weighted median. Derivatives skip the price check because they are not priced in the asset's units. A failing row leaves every sum and average, including market cap and supply, and says so in market.quality; excludedCount and excludedByQuality count them per chain and in aggregate.
  • aggregate is the sum of chainStats across chains, with averages weighted by chain liquidity, plus chainCount and excludedFromSupplyCount: the one guarded, issuance-aware on-chain figure.
  • referenceMarket is the off-chain reference (v1's canonicalMarket): CoinGecko for crypto, the stock feed for one-share-per-token equities and ETFs (source: "stock" replaces v1's clickhouse_stock), the PreStocks mark for pre-IPO equities. Keep a default branch when you switch on source.
  • stats is the reference-first headline. Price, market cap, FDV, 24h change and supply come from referenceMarket when there is one; liquidity, DEX volume, trades and the 1h change from aggregate; anything the reference lacks falls through to aggregate. For an equity the market cap is the company's and fdv is null (the tokenized float is aggregate.marketCap), and supply is the tokenized issued supply. stats.source says where each figure came from. A native coin's home-chain row (ETH on Ethereum) has no pool and carries the reference's supply, so chainStats.ethereum is not empty.
  • volume30dUSD sums each chain's 30-day DEX volume over the chains in the window (traded in the last 24h, or with accepted volume in the window). It is null until every such chain has at least 28 sampled days for each variant that matters to its total, refreshed within 48 h; rows the quality guard rejects are left out. volume30dScope says whether it covers every chain or Solana alone.

Primary variant and primaryScope

primaryVariant is the variant to trade. Selection has two steps: pick a candidate pool, then rank inside it.

With the default primaryScope=executable, candidates are drawn from chains whose swap capability is on (Solana), taking the first non-empty pool:

  1. executable_active — actively traded variants on executable chains
  2. active — actively traded variants on any chain
  3. executable — any variant on an executable chain
  4. all — everything

"Actively traded" means at least $100 of 24h volume and 5 trades. primaryScope=global skips the executable pools and gives the cross-chain liquidity winner.

Inside the pool the applied primaryVariantStrategy ranks: liquidity by default, stock_redeemability for equities (redeemable tiers first, then liquidity), or execution_quality. A variant with no market data never outranks one that trades, and compromised or blocked variants are skipped while a safe sibling exists; when every candidate is flagged, the least-bad flagged variant is returned rather than none. primaryVariantSelection reports the pool and the deciding rule so the choice is reproducible, and primaryVariantByChain runs the same ranking inside each chain.

Advisories

Advisories are keyed by Solana mint, so EVM variants always carry advisory: null. An asset's advisories[] lists every flagged variant, most severe first, including blocked siblings that list surfaces hide.

StatusMeaningWhat the API does
cautionTrade with care: a known concern that does not make the token unsafe.Returned everywhere; never changes ranking or inclusion.
compromisedThe token or its issuer was exploited or has otherwise lost integrity.Never the primary variant; excluded from trending; still readable with the advisory attached.
blockedDo not trade: operators have withdrawn the token from the surface.Never the primary variant; excluded from trending, search and curated lists; still readable by id.

Each advisory carries status, reason, an optional url with more detail, since (unix milliseconds the current status was set), and source (admin for a human operator, webacy_depeg for the Webacy depeg monitor, peg_guard for the tokens.xyz peg monitor). System sources only ever set caution; escalation is a human decision. Read endpoints annotate; they never refuse. Treat compromised and blocked as do-not-trade in your UI.

On the v2 asset surface: GET /v2/assets omits blocked variants from every row and drops an asset whose variants are all blocked; GET /v2/assets/{assetId}, the /variants sub-route and the variant routes serve them with the advisory attached. The long form is in Advisories.