The Solana memecoin market generates thousands of new tokens per day. Most die within minutes. A few go to millions in market cap. The challenge is not finding tokens — it's finding the right tokens early enough to matter.
Most traders rely on social media calls, Telegram groups, or chart-watching. These methods have two problems: they're slow (by the time a call reaches Twitter, the earliest buyers already have their positions), and they're noisy (for every legitimate call, there are ten paid promotions and exit-liquidity traps).
On-chain data is faster and harder to fake. When a KOL with a verified 60% win rate buys a token at $30K market cap, that's a measurable signal. When five KOLs buy the same token within an hour, that's consensus. When the token's peak history shows it bonded fast and hasn't hit its ATH yet, that's context.
MadeOnSol's API exposes three endpoints that, chained together, form a token discovery pipeline: the scout leaderboard (who finds tokens first), KOL consensus (how many smart wallets agree), and peak MC history (where the token sits in its lifecycle). This guide shows how to wire them up. For surfacing tokens by KOL attention rather than chaining a pipeline, the hot and trending endpoints offer two ready-made scorers — we cover the difference between hot vs trending KOL tokens separately.
The Three Endpoints
Scout Leaderboard
Not all KOLs are equal at finding tokens early. Some consistently appear as the first KOL buyer on tokens that later attract follow-on interest. MadeOnSol scores this behavior with the mv_kol_scout_score materialized view, refreshed hourly.
Each KOL gets a scout tier based on their first-touch track record over the last 30 days:
| Tier | What It Means |
|---|
| S | Elite scouts. Their first-touch tokens attract 3+ follow-on KOLs within 4 hours roughly 50% of the time — versus a 14% baseline. |
| A | Strong scouts. Consistently early with above-average follow-on rates. |
| B | Above average. Enough first-touch data to score meaningfully. |
| C | Active but not yet distinguished as early movers. |
The key metric is swarm_3plus_pct — the percentage of a scout's first-touch tokens that attracted 3 or more additional KOL buyers within 4 hours. An S-tier scout running 50% on this metric is finding tokens that the broader KOL community validates at a 3.7x rate over baseline.
GET /api/v1/kol/scouts/leaderboard returns the ranked list. ULTRA tier required.
KOL Consensus
Once you have a token of interest (from a scout alert, deployer signal, or any other source), the consensus endpoint tells you how many KOLs have traded it and what their collective behavior looks like.
GET /api/v1/tokens/{mint}/kol-consensus returns:
- total_kol_buyers / total_kol_sellers — raw counts of unique KOL wallets that bought or sold
- kol_exit_rate — fraction of buyers who have also sold (high exit rate = KOLs are taking profit or cutting losses)
- net_flow_sol — total buy volume minus total sell volume in SOL. Positive = net accumulation.
- median_entry_mc_usd — the median market cap at which KOLs entered. If this is $40K and the current MC is $200K, KOLs got in early. If the median entry is $180K and current MC is $200K, they're chasing.
- first_touch_wallet — the wallet that was the very first KOL buyer, cross-referenced with the first-touches system
PRO or ULTRA tier. ULTRA adds the full list of buyer and exited wallet addresses.
Peak MC History
For any token mint, the peak history endpoint returns the full lifecycle data:
GET /api/v1/tokens/{mint}/peak-history returns:
- peak_mc_usd — all-time high market cap
- current_mc_usd — current market cap
- decline_from_peak_pct — how far the token has fallen from its ATH (negative number)
- mc_at_bond — market cap at the moment of bonding curve completion
- mc_1h/6h/24h/7d_after_bond — market cap snapshots at intervals after bonding
- time_to_bond_minutes — how long the bonding curve took to fill
This tells you where the token sits in its lifecycle. A token that bonded 2 hours ago, peaked at $500K, and currently sits at $120K is in a very different situation than one that bonded yesterday, peaked at $2M, and is at $1.8M.
Building the Discovery Pipeline
The strategy: start with the scout leaderboard to identify which KOLs are finding tokens first. Monitor their first-touch activity. When a scout finds a new token, check consensus to see if other KOLs agree. Then check peak history to assess lifecycle timing. Only surface tokens that pass all three filters.
import MadeOnSol from "madeonsol";
const client = new MadeOnSol({ apiKey: process.env.MADEONSOL_API_KEY! });
interface DiscoveryCandidate {
mint: string;
scout: string;
scout_tier: string;
kol_buyers: number;
net_flow_sol: number;
median_entry_mc_usd: number | null;
current_mc_usd: number | null;
peak_mc_usd: number | null;
decline_from_peak_pct: number | null;
bonded_minutes_ago: number | null;
}
async function discoverTokens(): Promise<DiscoveryCandidate[]> {
// Step 1: Get the top scouts
const { scouts } = await client.kol.scouts.leaderboard({
sort: "swarm_3plus_pct",
scout_tier: "S",
limit: 20,
});
console.log(`Found ${scouts.length} S-tier scouts`);
// Step 2: Get recent first-touch events from these scouts
const recentTouches = await client.kol.firstTouches({
preset: "scout",
min_scout_tier: "S",
limit: 50,
token_age_max_min: 120, // tokens less than 2 hours old
});
const candidates: DiscoveryCandidate[] = [];
// Step 3: For each first-touch token, check consensus + peak history
for (const touch of recentTouches.events) {
const [consensus, peakHistory] = await Promise.all([
client.tokens.kolConsensus(touch.token_mint),
client.tokens.peakHistory(touch.token_mint),
]);
// Filter: at least 3 KOL buyers, net positive flow, not already at ATH
if (
consensus.consensus &&
consensus.consensus.total_kol_buyers >= 3 &&
consensus.consensus.net_flow_sol > 0 &&
consensus.consensus.kol_exit_rate < 0.3
) {
const peak = peakHistory.peak_history;
const bondedAt = peak?.bonded_at ? new Date(peak.bonded_at) : null;
const bondedMinutesAgo = bondedAt
? (Date.now() - bondedAt.getTime()) / 60000
: null;
candidates.push({
mint: touch.token_mint,
scout: touch.first_kol.name,
scout_tier: touch.first_kol.scout_tier,
kol_buyers: consensus.consensus.total_kol_buyers,
net_flow_sol: consensus.consensus.net_flow_sol,
median_entry_mc_usd: consensus.consensus.median_entry_mc_usd,
current_mc_usd: consensus.current_mc_usd,
peak_mc_usd: peak?.peak_mc_usd ?? null,
decline_from_peak_pct: peak?.decline_from_peak_pct ?? null,
bonded_minutes_ago: bondedMinutesAgo
? Math.round(bondedMinutesAgo)
: null,
});
}
}
return candidates;
}
Ranking the Candidates
Once you have the filtered list, rank by a composite of signals:
function rankCandidates(candidates: DiscoveryCandidate[]): DiscoveryCandidate[] {
return candidates.sort((a, b) => {
// Prefer more KOL buyers
const kolScore = (b.kol_buyers - a.kol_buyers) * 10;
// Prefer tokens where KOLs entered at low MC
const entryScore =
a.median_entry_mc_usd && b.median_entry_mc_usd
? (a.median_entry_mc_usd < b.median_entry_mc_usd ? 5 : -5)
: 0;
// Prefer tokens that bonded recently
const freshnessScore =
a.bonded_minutes_ago && b.bonded_minutes_ago
? (a.bonded_minutes_ago < b.bonded_minutes_ago ? 3 : -3)
: 0;
// Penalize tokens already near ATH (less upside)
const declineScore =
a.decline_from_peak_pct && b.decline_from_peak_pct
? (a.decline_from_peak_pct < b.decline_from_peak_pct ? 2 : -2)
: 0;
return kolScore + entryScore + freshnessScore + declineScore;
});
}
async function main() {
const candidates = await discoverTokens();
const ranked = rankCandidates(candidates);
console.log(`\nDiscovered ${ranked.length} candidates:\n`);
for (const c of ranked.slice(0, 10)) {
console.log(`${c.mint}`);
console.log(` Scout: ${c.scout} (${c.scout_tier})`);
console.log(` KOL buyers: ${c.kol_buyers}`);
console.log(` Net flow: ${c.net_flow_sol.toFixed(1)} SOL`);
console.log(` Current MC: $${c.current_mc_usd?.toLocaleString() ?? "unknown"}`);
console.log(` Peak MC: $${c.peak_mc_usd?.toLocaleString() ?? "unknown"}`);
console.log(` Decline from peak: ${c.decline_from_peak_pct?.toFixed(1) ?? "N/A"}%`);
console.log(` Bonded: ${c.bonded_minutes_ago ?? "unknown"} minutes ago`);
console.log();
}
}
main().catch(console.error);
Real-Time vs. Polling
The code above runs as a one-shot scan. For continuous monitoring, two options:
Polling with the REST API. Run the pipeline every 2-5 minutes. Use the ?since= parameter on first-touches to only fetch new events since your last check. Practical for PRO tier.
WebSocket push. Subscribe to the kol:first_touches WS channel and receive first-touch events in real-time (sub-second). When one arrives from an S-tier scout, immediately check consensus and peak history. This is the fastest path — the 38-day backtest shows the median lead time before a second KOL buys is 12 seconds. If you're polling every 2 minutes, you're missing that window. ULTRA tier required for WS access.
// WebSocket approach (ULTRA)
const streamToken = await client.stream.getToken();
const ws = new WebSocket(streamToken.ws_url);
ws.on("message", async (raw) => {
const event = JSON.parse(raw.toString());
if (event.channel === "kol:first_touches") {
const touch = event.data;
if (touch.first_kol.scout_tier === "S") {
// Immediately check consensus + peak history
const [consensus, peak] = await Promise.all([
client.tokens.kolConsensus(touch.token_mint),
client.tokens.peakHistory(touch.token_mint),
]);
// ... evaluate and act
}
}
});
Interpreting the Signals Together
Each endpoint alone is useful but not sufficient. Together they answer a sequence of questions:
-
Who found it? (Scout leaderboard) — Was the first buyer a proven scout, or a random wallet? S-tier scouts have 3.7x the follow-on rate of baseline. That matters.
-
Do others agree? (KOL consensus) — One KOL buying is a data point. Five KOLs buying with net positive flow and low exit rate is conviction. Check the exit rate carefully: if 4 out of 5 KOL buyers already sold, the smart money is leaving.
-
Where is it in its lifecycle? (Peak history) — A token that bonded 30 minutes ago and is still climbing has a very different risk profile than one that peaked 6 hours ago and is down 70%. The MC snapshots at 1h, 6h, 24h, and 7d after bonding show the typical decay curve for that specific token.
A strong signal looks like: S-tier scout first-touched it, 5+ KOL buyers with less than 20% exit rate, net flow above 10 SOL, current MC below peak MC (room to run), and bonded within the last 2 hours.
A weak signal looks like: C-tier scout, 2 KOL buyers where one already exited, median entry MC close to current MC (late entries), and the token already hit peak 12 hours ago.
Coordination History for Backtesting
For ULTRA subscribers, GET /api/v1/kol/coordination/history provides historical coordination events — tokens that fired coordination alerts in the past. Each event includes the coordination score, number of KOL buyers, and current MC. Use this for backtesting: did tokens that scored 80+ on coordination outperform those that scored 60? Did S-tier scout first-touches that later hit 5+ KOL consensus deliver higher returns? The data is there to answer these questions quantitatively.