You can embed a Solana data API in your app on a standard plan — but the moment you resell the data, expose it as your own feed, or strip the branding, you're in licensing territory most builders discover too late. This guide maps the three usage zones (internal use, user-facing display, redistribution), the questions to ask any data vendor before you build, and what an Enterprise data license actually covers: white-label, bulk export, extended caching, SLA, and dedicated limits.
On pump.fun-style launches, the first 20 wallets to buy tell you nearly everything you need to know. If those 20 are 14 bundlers and 3 snipe bots, the deployer is about to dump on whoever arrives next. If they're 4 known-alpha wallets and 2 KOLs, you're looking at an organic launch with smart money interest. Same chart, same market cap, completely different trade.
GET /api/v1/tokens/{mint}/cap-table returns that first cohort, enriched. Each buyer comes with their historical win rate, PnL, bot confidence, bundle flag, and KOL identity — so you're not just looking at anonymous addresses, you're looking at a scorecard of who got in early and whether that's good news or not.
The call
GET /api/v1/tokens/{mint}/cap-table
Authorization: Bearer msk_...
No query params. The mint is a standard base58 Solana address.
Tier gating:
- BASIC — 403. This endpoint requires paid access.
- PRO — top 10 non-deployer buyers.
- ULTRA — top 20 non-deployer buyers.
Important: the endpoint excludes the deployer's own rank-1 buy. "First 10 buyers" means first 10 other wallets to buy — which is the cohort that actually carries signal about whether the launch is organic.
The response
{
"mint": "26s2aY...pump",
"buyers": [
{
"rank": 2,
"wallet": "86AE...EdD",
"first_buy_sol": 2.022,
"first_buy_at": "2026-04-22T19:14:03Z",
"is_bundle": false,
"is_kol": true,
"kol_name": "Publix",
"bot_confidence": 0.12,
"historical_win_rate": 0.67,
"historical_pnl_sol": 248.3,
"historical_tokens": 142
},
{
"rank": 3,
"wallet": "5tQz...xPk",
"first_buy_sol": 0.45,
"first_buy_at": "2026-04-22T19:14:05Z",
"is_bundle": true,
"is_kol": false,
"kol_name": null,
"bot_confidence": 0.89,
"historical_win_rate": 0.18,
"historical_pnl_sol": -14.2,
"historical_tokens": 1847
}
],
"summary": {
"known_alpha_wallets": 3,
"known_kols": 2,
"bundle_buyers": 14,
"buyer_quality_score": 32,
"confidence": "high",
"signal": "negative"
}
}
Two sections: the buyers[] array with per-wallet enrichment, and the summary block with the aggregate quality score.
The buyers array
Ordered by rank (rank 2 first, because rank 1 is the deployer and is excluded). Each row has three categories of data:
Identity
wallet — raw address (PRO and ULTRA, not truncated).
is_kol / kol_name — is this wallet in the MadeOnSol KOL list? If yes, who? Strong positive signal.
is_bundle — did this wallet buy as part of a bundle transaction with the deployer or adjacent wallets? Bundles are the #1 rug tell on pump.fun.
Historical performance (from mv_alpha_wallets)
historical_win_rate — their win rate across every token they've traded historically.
historical_pnl_sol — lifetime realized PnL.
historical_tokens — how many distinct tokens they've touched.
bot_confidence — 0.0–1.0 score for how bot-like their behavior is (flat hour distribution, burst frequency, uniform sizing).
These fields come from our alpha wallet intelligence dataset (~25,000 scored wallets). If the buyer is a fresh wallet we haven't scored yet, these fields are null — itself a signal (no track record).
Position context
rank — their slot in the buyer queue, starting at 2 (deployer is rank 1, excluded).
first_buy_sol — size of their first buy, in SOL.
first_buy_at — timestamp.
A rank-2 wallet buying 2 SOL 3 seconds after deploy looks very different from a rank-15 wallet buying 0.3 SOL 8 minutes in. Use rank + timing together.
The summary block
The summary is the aggregate verdict on the cohort. Note: it always scores against all 20 buyers, not the tier-capped visible ones. So on PRO you see 10 wallets but the score reflects all 20. You're not buying a half-picture.
buyer_quality_score (0–100)
A composite score derived from:
- Average historical win rate of non-bot buyers → positive
- Count of known alpha wallets (up to +20 bonus) → positive
- Count of known KOLs (up to +20 bonus) → positive
- Count of bundle buyers → heavy negative
- Bot-dominated cohort (>50% high bot confidence) → heavy negative
Rough interpretation:
- ≥ 75 — very strong cohort. Multiple alpha wallets, few to no bundles.
- 60–75 — clean launch, some alpha signal.
- 40–60 — mixed / ambiguous.
- 20–40 — bundle-heavy or bot-heavy, caution warranted.
- < 20 — avoid. Dominated by bundles or low-quality actors.
confidence
high — cohort has enough historical data to score meaningfully.
medium — partial data coverage.
low — mostly fresh wallets with no track record. The score defaults to 50 with low confidence. Don't over-interpret.
signal
A categorical shortcut derived directly from buyer_quality_score:
positive — score > 60
neutral — score in [40, 60]
negative — score < 40
Use the raw score when you need fine-grained filtering; use the signal for quick gating.
A decision workflow
import { MadeOnSol } from "madeonsol";
const client = new MadeOnSol({ apiKey: process.env.MADEONSOL_API_KEY! });
async function analyzeLaunch(mint: string) {
const { buyers, summary } = await client.tokens.capTable({ mint });
// Hard kills
if (summary.bundle_buyers >= 10) return "skip: bundle-heavy launch";
if (summary.confidence === "low" && buyers.length < 5) return "skip: not enough buyers";
// Look for alpha concentration in rank 2-5
const topFive = buyers.slice(0, 4);
const alphaInTop = topFive.filter(b => (b.historical_win_rate ?? 0) >= 0.5).length;
const bundlesInTop = topFive.filter(b => b.is_bundle).length;
if (bundlesInTop >= 2) return "skip: bundles in first-four seats";
if (alphaInTop >= 2 && summary.buyer_quality_score >= 60) return "long signal";
return "neutral, watch volume";
}
The pattern worth internalizing: a bad signal in the first 4 ranks is more important than a bad summary score. Bundlers piled in at ranks 15-20 matter less than the same pattern at ranks 2-5.
The KOL ranks matter more than you'd think
When a known KOL shows up at rank 2, 3, or 4, it's not that they had a secret tip — it's that they have the tooling to detect launches fast and the filter to skip junk. Their presence in the top few ranks is a pre-computed signal. Five KOLs in the first 10 ranks is almost never a coincidence.
Inverse signal: a launch where ranks 2-10 are all is_kol: false and historical_win_rate: null is a launch that smart money didn't notice or deliberately ignored. Either way, not your trade.
Pairing with /tokens/{mint}/buyer-quality
/buyer-quality returns a simplified version of this endpoint: just the summary score + breakdown counts, no per-wallet rows. It's cheaper (5-minute LRU cache), available on every tier, and perfect for bulk pre-filtering.
The usual workflow: call /buyer-quality on every fresh mint as a gate, then call /cap-table only on the ones that pass to see the full per-wallet breakdown and decide entry.
const { score, signal } = await client.tokens.buyerQuality({ mint });
if (signal !== "negative" && score >= 50) {
const details = await client.tokens.capTable({ mint });
// deep decision on details
}
One API call per candidate at the gate stage, one only when warranted. Keeps you under quota.
Tier considerations
| Field | BASIC | PRO | ULTRA |
|---|
| Endpoint access | ✗ (403) | ✓ | ✓ |
buyers[] returned | — | top 10 | top 20 |
| Per-buyer wallet address |