Every Solana memecoin trader ends up wanting the same thing eventually: a scanner that filters the firehose down to their specific rules — not GMGN's defaults, not Photon's "trending" sort, not Bullx's hot tab. You want mc between $100K and $2M, liquidity above $5K, on PumpSwap, less than 25% MEV share, gained at least 30% in the last hour, authorities revoked. Eleven filters in one query, exactly that combination.
GET /api/v1/tokens is that endpoint. It exposes a growing server-side filter surface over the live token-price view, with nine sort modes and pagination up to a 10,000 raw-candidate offset. This post walks through the full filter surface, what each filter is doing under the hood, and an example scanner you can run from a single API call.
The filter surface in one table
| Filter | Type | What it does |
|---|
min_mc / max_mc | USD float | Filter on current market cap (price × on-chain supply). |
min_liq | USD float | Filter on USD-denominated liquidity in the primary pool. Defaults to 2000 — opt out with min_liq=0. |
active_h | hours, 0–8760 | Drop tokens whose last trade is older than this many hours. Defaults to 168h (7 days); 0 disables the freshness bound. |
primary_dex | enum | One of pumpfun, pumpswap, raydium, raydium_clmm, meteora, orca. |
authority_revoked | bool | true requires BOTH mint and freeze authority revoked. false returns tokens with at least one authority live. |
exclude_token2022 | bool | true excludes Token-2022 mints. |
lp_burned | bool | Deprecated. Matches only verified LP-custody evidence; unknown matches neither value. There is currently no Solana LP-custody writer, so do not use this as a safety gate. |
deployer_tier | enum | One of elite, good, moderate, rising, cold, unranked. |
launchpad | enum | Launchpad of origin — pumpfun, launchlab, or bags (distinct from primary_dex, since bags tokens trade on Meteora). |
supply_burn | bool | Filter on the token-supply burn signal (separate from LP custody). |
min_volume_1h_usd | USD float | Floor on organic (MEV-stripped) volume in the last hour. |
max_mev_share_pct | 0–100 | Cap on the MEV share of cumulative tracked volume. |
min_liq_mc_ratio / max_liq_mc_ratio | ratio | Bound liquidity relative to market cap. A conservative default minimum is applied unless you pass 0. |
mc_change_1h_min_pct / mc_change_1h_max_pct | percent | Band-pass filter on % MC change over the last hour. |
sort | enum | mc_desc, mc_asc, last_trade_desc, liquidity_desc, cumulative_volume_desc, mc_change_5m_desc, mc_change_1h_desc, volume_1h_desc, trending. |
limit / offset | int | Up to 100 per page, up to 10K offset. |
min_volume_1h_usd, max_mev_share_pct, and mc_change_1h_* run DB-side too, against a read-time view that exposes the velocity metrics as sortable/filterable columns — no over-fetch. deployer_tier and launchpad are post-filters because they come from joined metadata. The current route scans candidate pages in sort order until it fills the requested result set, exhausts candidates, or reaches a scan/time budget. Read pagination.post_filtered, scan_truncated and next_offset rather than assuming one fixed over-fetch window.
Why a min_liq=2000 default
If you look at the long tail of token_prices, there are tens of thousands of tokens with absurd MC values that nobody trades — pools with $50 of liquidity but a stamped supply of one trillion, producing a phantom $5M MC. They're not interesting and they crowd the top of any naive MC sort. The default min_liq=2000 filters them out at the API layer.
For everyone except the most fringe sniper use case, leave the default in place. To explicitly include the phantom tokens (e.g., to audit them or to scan brand-new mints that haven't pooled yet), pass min_liq=0.
Response shape
{
"tokens": [
{
"mint": "4k3Dyj...",
"symbol": "PEPECAT",
"name": "Pepe Cat",
"price_usd": 0.00029,
"market_cap_usd": 287_000,
"fdv_usd": 290_000,
"liquidity_usd": 14_200,
"primary_dex": "pumpswap",
"authorities_revoked": true,
"lp_burned": true,
"is_token_2022": false,
"last_trade_time": "2026-05-14T11:42:17Z",
"mc_change_5m_pct": 8.2,
"mc_change_1h_pct": 41.7,
"organic_volume_1h_usd": 81_000,
"mev_share_pct": 18.4
}
],
"pagination": {
"limit": 50,
"offset": 0,
"returned": 28,
"has_more": false,
"post_filtered": true
},
"filters": { "min_mc": 100000, "max_mc": 2000000, "min_liq": 5000, ... }
}
The filters echo is useful when debugging scanners — it tells you what the server actually applied, including the min_liq_default_applied: true flag when you didn't override the default.
Example: a momentum-aware safety scanner
Here's the eleven-filter query from the intro, in a single SDK call:
import { MadeOnSol } from "madeonsol";
const client = new MadeOnSol({ apiKey: process.env.MADEONSOL_API_KEY! });
async function safeMomentumScanner() {
const { tokens } = await client.tokens.list({
min_mc: 100_000,
max_mc: 2_000_000,
min_liq: 5000,
active_h: 2,
primary_dex: "pumpswap",
authority_revoked: true,
min_volume_1h_usd: 25_000,
max_mev_share_pct: 25,
mc_change_1h_min_pct: 30,
sort: "mc_change_1h_desc",
limit: 50,
});
return tokens.map((t) => ({
symbol: t.symbol,
mint: t.mint,
mc: t.market_cap_usd,
pct_1h: t.mc_change_1h_pct,
vol_1h: t.organic_volume_1h_usd,
mev: t.mev_share_pct,
}));
}
In English: "PumpSwap-graduated tokens between $100K and $2M MC, with both authorities revoked, with at least $25K of clean organic volume in the last hour, MEV share below 25%, gained at least 30% in the last hour, traded in the last 2 hours."
Choose the cadence from your quota and use case rather than copying a 5-second loop. A request every 5 seconds is 17,280 calls/day — above PRO's 10,000/day allowance. A 30-second cadence is 2,880/day and fits PRO comfortably; higher-frequency scanners should use ULTRA/BUSINESS or event-driven feeds.
Picking the right sort
Nine sorts cover the obvious scanner patterns:
mc_desc — "biggest first." Default. Useful when you've banded MC and want the top movers in the band.
mc_asc — "smallest first." Pair with min_mc=50000 to scan fresh micro-caps without the dust tail.
last_trade_desc — "freshest tape." The best sort for entry signals — your top results are tokens that are actually trading right now.
liquidity_desc — "deepest first." For larger size; deepest pools take the best fills.
cumulative_volume_desc — "most-traded since we started tracking." Useful for liveness checks; a token at the top of this list is one with sustained interest, not a one-candle wonder.
mc_change_5m_desc / mc_change_1h_desc — direct momentum sorts, computed DB-side off the same velocity view the filters read from. This is the native pct_1h_desc view scanners used to have to fake with last_trade_desc + mc_change_1h_min_pct.
volume_1h_desc — "hottest tape right now" by organic 1h volume, distinct from cumulative_volume_desc.
trending — a composite rank (recent volume × positive momentum), recency-bounded so stale tokens can't camp at the top.