Discovery and markets API
Screen launchpad tokens by market cap, holders, top-10 %, curve and spam
Solana launchpad tokens matching ALL of: market cap, holders, top-10 %, curve %, age, no spam/manipulation. Read createdAtIso for the creation time rather than converting createdAtUnixTime.
/api/external/discovery/token-screen/{chain}What it returns
Answers "which tokens match ALL of these criteria right now?" for launchpad tokens, either already bonded (stage=bonded: left the bonding curve for a regular pool) or still on the curve (stage=bonding). Omit stage when the question does not care which: both stages are screened into one list sorted by sortBy, stage echoes "all", every row says which it is in bonded and curvePct, and counts.byStage splits the counts. With no stage the liquidity floor and minLiquidityUsd apply to bonded rows only, and a row not bonded publishes liquidityUsd null: before bonding the figure counts the curve, not a tradeable pool, so it is neither shown nor checked. minCurvePct and maxCurvePct need stage=bonding; without it they are refused with a 400. Every criterion passed is checked against a value on the row, and every row carries the evidence it was checked on. A row is NEVER returned on a criterion that could not be checked: it is listed under unverified with the criteria it is missing instead. counts says how many candidates were read, checked, matched, dropped and left unverified. Market cap is read twice, from the candidate list and from the deepest pool, and the two can differ by a few percent; a market-cap bound passes only when every reading known passes it, so a row near a bound is left out rather than returned on the more favourable reading. A bonded row must also be physically possible: a pool holds at most twice the market cap in liquidity, so a bonded row listed with more than a set multiple of its market cap (2.2 by default) is re-read from its pools. Its liquidity becomes the pools' own total, and its market cap is kept only when the deepest pool confirms it within a set tolerance (10% by default); otherwise marketCapUsd is null, and the row is unverified with missing ["marketCapUsd"] whenever a market-cap bound or sortBy=marketCapUsd was asked for. excludeSpam=true keeps only rows whose spam is false: no mint authority, no freeze authority (on Solana the freeze authority is what makes a token unsellable) and not flagged rugged. Every row carries manipulationRisk: flagged with its reasons when the market cap of its pools outside the curve is far above their liquidity, nearly every trade is a buy, or supply is spread evenly across thousands of wallets. excludeManipulationRisk=true keeps only rows whose manipulationRisk.flagged is false. Bonded rows get a liquidity floor by default (echoed in appliedCriteria.minLiquidityUsd) because without one the bonded population in a market-cap band is dominated by farmed tokens with a few dollars of liquidity; pass minLiquidityUsd to change it, 0 to remove it. Results are sorted by sortBy (marketCapUsd, volume1hUsd or txns1h) in sortOrder, rows without the sort value last. The screen reads the leading candidates in that order, so a very narrow screen can return fewer rows than exist; unverified and counts show how much was read. ONLY SOL IS SERVED — every other chain 400s naming itself.
Chains
- Solana SOL
Request
Send your key in the x-dex-data-key header. Replace each <placeholder> with a real value.
curl -H "x-dex-data-key: $DEXDATA_API_KEY" \ "https://api.dexdata.io/api/external/discovery/token-screen/SOL"
| Parameter | Description |
|---|---|
stagequery · string · optional | Optional. "bonded" (left the bonding curve; graduated, migrated) or "bonding" (still on the curve). Omit it to screen both stages at once, and set it only when the user says bonded/graduated or on the curve.bondedbonding |
minMarketCapUsdquery · number · optional | Lowest USD market cap, inclusive; every reading known must pass it. |
maxMarketCapUsdquery · number · optional | Highest USD market cap, inclusive; every reading known must pass it. |
minHoldersquery · integer · optional | Fewest holders, inclusive. |
maxTop10HolderPctquery · number · optional | Highest share of supply in the ten largest wallets, 0-100, inclusive. |
minCurvePctquery · number · optional | stage=bonding only; a 400 without it. Lowest curve completion, 0-100, inclusive. |
maxCurvePctquery · number · optional | stage=bonding only; a 400 without it. Highest curve completion, 0-100, inclusive. |
minLiquidityUsdquery · number · optional | Bonded rows only: stage=bonded, or no stage (curve rows are not checked on it); a 400 on stage=bonding. Lowest USD liquidity, inclusive. Defaults to the deployment floor; 0 removes it. |
createdWithinHoursquery · number · optional | Created in the last N hours, above 0 and at most 720. Not with createdAfterUnixTime. |
createdAfterUnixTimequery · integer · optional | Created at or after this Unix time, seconds. Not with createdWithinHours. |
excludeSpamquery · boolean · optional | Keep only rows whose spam is false. Default false. |
excludeManipulationRiskquery · boolean · optional | Keep only rows whose manipulationRisk.flagged is false. Default false. |
sortByquery · string · optional | marketCapUsd (default), volume1hUsd or txns1h (1-hour activity).marketCapUsdvolume1hUsdtxns1h |
sortOrderquery · string · optional | desc (default) or asc.ascdesc |
limitquery · integer · optional | Rows to return, 1-50. Default 20. limit ≤ 30 keeps the answer under 32 KB. |
chainpath · string · required | Only SOL answers this endpoint — every other chain 400s naming itself rather than returning a blank not-found. |
Response fields
A JSON response. Every field is always present; null means the value could not be established, never zero.
| Field | Type | Description |
|---|---|---|
stage | string | Echoes the requested stage, or "all" when none was given and both were screened. |
sortBy | string | |
sortOrder | string | |
appliedCriteria | object | The criteria applied, defaults included; null means not applied. A query parameter this endpoint does not declare is refused with a 400 that names it, so a misspelled criterion never goes unapplied. |
appliedCriteria.minMarketCapUsd | number | null | |
appliedCriteria.maxMarketCapUsd | number | null | |
appliedCriteria.minLiquidityUsd | number | null | The liquidity floor applied to bonded rows, the caller's or the default: every row on stage=bonded, only the bonded rows when no stage was given. null on stage=bonding. |
appliedCriteria.minHolders | number | null | |
appliedCriteria.maxTop10HolderPct | number | null | |
appliedCriteria.minCurvePct | number | null | |
appliedCriteria.maxCurvePct | number | null | |
appliedCriteria.createdAfterUnixTime | number | null | Unix seconds; createdWithinHours resolved against the time the screen ran. |
appliedCriteria.excludeSpam | boolean | |
appliedCriteria.excludeManipulationRisk | boolean | |
appliedCriteria.limit | number | |
results | object[] | Rows that passed EVERY criterion on a known value, in sort order. |
results[].address | string | The token mint. Identify rows by address, never by symbol. |
results[].name | string | null | The token name, or null when none was reported. |
results[].symbol | string | null | The ticker, or null when none was reported. |
results[].bonded | boolean | null | Whether the token has left its launchpad bonding curve for a regular pool. null when unknown, or when two independent readings disagree. |
results[].curvePct | number | null | Bonding-curve completion, 0-100, while still on the curve; null once bonded or when unknown. |
results[].marketCapUsd | number | null | USD market capitalisation, or null when unknown; the figure rows are sorted on. A market-cap bound is also checked on a second reading taken from the deepest pool, and a row passes only when both do. null on a bonded row whose figures were implausible and the pool did not confirm the market cap. |
results[].liquidityUsd | number | null | Pooled USD liquidity, or null when unknown. On stage=bonding it counts the curve, not a tradeable pool; with no stage it is null on every row that is not bonded, for the same reason. On a bonded row whose figures were implausible it is the pools' own total, or null when that is implausible too. |
results[].holders | number | null | Wallets holding the token, or null when unknown. |
results[].top10HolderPct | number | null | Share of supply held by the ten largest wallets, 0-100, with pools and burn addresses excluded — the same figure GET /token-analysis/distribution publishes as top10Pct. null when unknown. |
results[].spam | boolean | null | true when any spamReasons applies; false only when mint authority, freeze authority and the rugged flag are all known and clear; null otherwise. |
results[].spamReasons | string[] | Why spam is true: "mint-authority-active" (the deployer can still create supply), "freeze-authority-active" (the deployer can freeze any holder's account, which makes the token unsellable), "rugged" (its liquidity was pulled). Empty when none applies. |
results[].mintAuthorityActive | boolean | null | Whether a mint authority is still set. null when unknown. |
results[].freezeAuthorityActive | boolean | null | Whether a freeze authority is still set. null when unknown. |
results[].rugged | boolean | null | Whether the token is flagged as rugged (liquidity pulled). null when unknown. |
results[].createdAtUnixTime | number | null | Unix seconds of the token's creation, or null when unknown. |
results[].createdAtIso | string | null | The same instant as createdAtUnixTime, as ISO 8601 UTC, e.g. 2026-09-20T09:16:16Z. null exactly when createdAtUnixTime is null. |
results[].volume1hUsd | number | null | USD traded in the last hour, or null when unknown. |
results[].txns1h | number | null | Trades in the last hour, or null when unknown. |
results[].manipulationRisk | object | Whether the token's market looks manufactured (bot-driven buying into thin liquidity) rather than traded. Read from the same evidence as the other fields, never from top-10 concentration alone: a supply spread evenly across thousands of fresh wallets shows a tiny top-10 share. |
results[].manipulationRisk.flagged | boolean | null | true when any reason applies; false when every check could be made and none applies; null otherwise. |
results[].manipulationRisk.reasons | object[] | Why flagged is true, each with the figure measured and the threshold it crossed. Empty when none applies. |
results[].manipulationRisk.reasons[].reason | string | "market-cap-far-above-liquidity": the market cap of the token's pools outside the bonding curve is at least threshold times their liquidity, so the price could not absorb a sale. "one-sided-buying": buys are at least threshold of all trades (0-1). "even-holder-spread": the top-10 share is at most threshold times what an even split across every holder gives ten wallets. |
results[].manipulationRisk.reasons[].value | number | The figure measured, in the same unit as threshold. |
results[].manipulationRisk.reasons[].threshold | number | The threshold it crossed. |
results[].manipulationRisk.reasons[].evidence | object | The readings value was computed from: marketCapUsd and liquidityUsd, buys and sells (trade counts), or holders and top10HolderPct. |
unverified | object[] | Rows that failed no criterion but could not be checked on at least one (at most 50 listed; counts.unverified has the total). Never treat these as matches. |
unverified[].address | string | |
unverified[].missing | string[] | The criteria that could not be checked. |
counts | object | |
counts.candidates | number | Candidates read in sort order after their listed values were checked. |
counts.checked | number | Candidates whose full evidence was fetched. |
counts.matched | number | Rows returned in results. |
counts.dropped | number | Checked candidates that failed a criterion. |
counts.unverified | number | Checked candidates missing a value for some criterion. |
counts.manipulationRiskFlagged | number | Checked candidates whose manipulationRisk.flagged is true, whether or not they were excluded. |
counts.byStage | object | Only when no stage was given: the same five counts for each stage, by the list the candidate was read from. A token read in both counts once, as bonded. |
counts.byStage.bonded | object | |
counts.byStage.bonded.candidates | number | |
counts.byStage.bonded.checked | number | |
counts.byStage.bonded.matched | number | |
counts.byStage.bonded.dropped | number | |
counts.byStage.bonded.unverified | number | |
counts.byStage.bonding | object | |
counts.byStage.bonding.candidates | number | |
counts.byStage.bonding.checked | number | |
counts.byStage.bonding.matched | number | |
counts.byStage.bonding.dropped | number | |
counts.byStage.bonding.unverified | number |
Start building with DexData today
Sign in, create a key and make your first call in a minute. Free credits every month, and you only pay for calls that succeed.
Get a free API key