Solutions · Token risk
The MadeOnSol token risk API returns, for a Solana mint, a 0 to 100 risk score in which every point is attributed to a named factor, together with the raw inputs, the inputs it could not observe and the creator's own activity on that mint.
Check a mint before your bot, scanner or review queue acts on it.
Scoring starts on Pro. A Free key reads the raw checks (GET /token/{mint}) and creator profiles (GET /deployer-hunter/{wallet}), without the score.
One call, four blocks
GET /tokens/{mint}/riskPro
risk_score · band0 to 100, higher is riskier: "safe" under 30, "caution" to 65, "danger" above.
risk_scorebandscore_version
factors[]Each factor's status and points. The score is their sum.
keystatuspointsdetail
assessmentWhat could not be observed, and why.
statusunknown_inputsnot_assessedexplanations
devThe creator's own buys, sells and live balance on this mint.
buy_supply_pctsold_tokenswallet_emptytransfer_status
The problem
A single safety number hides what matters: whether the authorities are live, how thin the pool is, who bought the launch, what the creator did before and with this mint, and what could not be observed at all. A green light built on missing data is the failure to design against.
Show me
Real keys, illustrative values. This input produces exactly this score in the scorer.
One GET per mint. A PumpSwap pool address is answered for its mint; a wallet or token account gets a named 400.
GET /api/v1/tokens/9kQuW3...pump/risk
Authorization: Bearer msk_...
// SDK: await rest.tokenRisk("9kQuW3...pump")Nine factors fired here and add up to 47, "caution". The creator factor is at danger, LP custody is not assessed, and the creator has sold most of its self-buy, with a balance that matches its trades.
{
"mint": "9kQuW3...pump",
"risk_score": 47,
"band": "caution",
"factors": [
{ "key": "mint_authority", "label": "Mint authority", "status": "ok", "points": 0, "detail": "Mint authority revoked …" },
{ "key": "freeze_authority", "label": "Freeze authority", "status": "ok", "points": 0, "detail": "Freeze authority revoked …" },
{ "key": "liquidity", "label": "Liquidity", "status": "warn", "points": 8, "detail": "Low liquidity ($6,400)." },
{ "key": "liquidity_ratio", "label": "Liquidity vs market cap", "status": "warn", "points": 5, "detail": "Liquidity is 3.1% of market cap." },
{ "key": "transfer_fee", "label": "Transfer fee", "status": "ok", "points": 0, "detail": "No transfer fee." },
{ "key": "lp_burn", "label": "LP burn/lock", "status": "warn", "points": 8, "detail": "LP burn/lock status unknown …" },
{ "key": "bundled_open", "label": "Launch concentration", "status": "warn", "points": 6, "detail": "Elevated launch concentration (27.4 SOL in the first 20 buyers)." },
{ "key": "deployer_reputation", "label": "Deployer track record", "status": "danger", "points": 12, "detail": "Deployer has bonded only 4% of 50 tokens." },
{ "key": "kol_distribution", "label": "Smart-money flow", "status": "warn", "points": 8, "detail": "Tracked KOLs are net-distributing this token." }
],
"inputs": {
"mint_authority_revoked": true,
"freeze_authority_revoked": true,
"liquidity_usd": 6400,
"liquidity_to_mc_ratio": 0.031,
"transfer_fee_bps": 0,
"is_token_2022": false,
"token_supply_burn_detected": false,
"burn_detected": false,
"lp_burn_status": "unknown",
"launch_cohort_sol": 27.4,
"launch_cohort_size": 20,
"sniper_footprint": { "buys": 11, "buyers": 7, "sol": 9.8, "supply_pct": 4.2, "sniper_wallet_buys": 3, "data_available": true, "as_of": "2026-09-25T09:40:00.000Z" },
"deployer_bonding_rate": 0.04,
"deployer_total_deployed": 50,
"deployer_history_status": "established",
"deployer_reputation_scored": true,
"kol_signal": "distributing",
"is_blacklisted": false,
"supply_inflation_pct": null,
"liquidity_observed_at": "2026-09-25T09:58:12.000Z"
},
"assessment": {
"status": "complete",
"unknown_inputs": [],
"not_assessed": ["lp_custody"],
"explanations": { "lp_custody": "LP-token custody (burn/lock) is not observed for Solana pools; no LP evidence source exists yet." }
},
"score_version": "v2",
"dev": {
"wallet": "7Dv4tP...Qm2e",
"launchpad": "pumpfun",
"deployed_at": "2026-09-25T09:31:07.000Z",
"buy_sol": 1.2,
"buy_tokens": 34120000,
"buy_supply_pct": 3.41,
"bought_tokens_after": 0,
"sold_tokens": 30000000,
"sold_sol": 1.7,
"first_sell_at": "2026-09-25T09:36:44.000Z",
"holdings_tokens": 4120000,
"holdings_supply_pct": 0.412,
"wallet_empty": false,
"holdings_observed_at": "2026-09-25T09:59:58.000Z",
"expected_tokens_from_trades": 4120000,
"transfer_status": "none_detected",
"transfer_reason": "on-chain balance is consistent with the dev's recorded trades"
},
"dev_status": "ok",
"coverage": {
"scope": "pump.fun pipeline (pump.fun, LaunchLab/bonk, bags)",
"in_scope": true,
"eligibility": "eligible",
"eligibility_basis": "unresolved_launch",
"completeness": "not_verified"
},
"as_of": "2026-09-25T10:00:00.000Z"
}dev.wallet leads to the record behind that factor: 2 bonds in 50 launches, ten straight dumps, tier cold.
GET /api/v1/deployer-hunter/7Dv4tP...Qm2e
{
"is_deployer": true,
"deployer": {
"wallet_address": "7Dv4tP...Qm2e",
"tier": "cold",
"is_tracked": false,
"total_tokens_deployed": 50,
"total_bonded": 2,
"instant_bonds": 0,
"bonding_rate": 0.04,
"recent_bond_rate": 0,
"recent_outcomes": "DDDDDDDDDD",
"runner_rate": 0,
"last_deploy_at": "2026-09-25T09:31:07.000Z"
},
"launchpad_tokens": []
}Your code decides. These thresholds are an example, not a recommendation.
import { MadeOnSolREST } from "madeonsol-x402";
const rest = new MadeOnSolREST({ apiKey: process.env.MADEONSOL_API_KEY! });
type Decision = "allow" | "review" | "warn" | "ignore";
// Your thresholds. A 503 risk_inputs_unavailable is retryable, never a pass.
export async function decide(mint: string): Promise<Decision> {
const r = await rest.tokenRisk(mint);
const f = Object.fromEntries(r.factors.map((x) => [x.key, x]));
if (r.inputs.is_blacklisted) return "ignore";
if (f.mint_authority?.status === "danger" || f.freeze_authority?.status === "danger") return "ignore";
if (r.assessment?.status === "incomplete") return "review"; // score is a lower bound
if (r.dev?.transfer_status === "suspected") return "review";
if (f.deployer_reputation?.status === "danger") return "warn";
return r.risk_score < 30 ? "allow" : "review";
}Try it
The token lookup shows authority, fee and LP checks, liquidity, creator tier and KOL activity, with no key. The scored breakdown needs Pro.
Methodology
Read from the scorer. Each row is one entry in factors[]; the score is the sum of their points.
| Factor | Reads | How it scores |
|---|---|---|
Mint authoritymint_authority | mint_authority_revoked | Revoked 0. Live 22, and the band cannot read "safe". Unknown 8. |
Freeze authorityfreeze_authority | freeze_authority_revoked | Revoked 0. Live 18, and the band cannot read "safe". Unknown 6. |
Liquidityliquidity | liquidity_usd | $10,000 or more 0, under $10,000 8, under $2,000 15. Unknown 6. |
Liquidity vs market capliquidity_ratio | liquidity_to_mc_ratio | 5 % or more 0, under 5 % 5, under 2 % 10. Left out without a ratio. |
Transfer feetransfer_fee | transfer_fee_bpsis_token_2022 | No fee 0. A Token-2022 fee adds 4 per 1 %, 1 to 12. Unread 4: not "no fee". |
LP burn/locklp_burn | lp_burn_status | Only verified LP custody scores 0. Solana LP custody is not observed today, so every token carries these 8 points. A token-supply burn is not LP evidence. |
Launch concentrationbundled_open | launch_cohort_sollaunch_cohort_size | SOL the first buyers spent: up to 20 is 0, over 20 is 6, over 50 is 12. |
Deployer track recorddeployer_reputation | deployer_bonding_ratedeployer_total_deployeddeployer_history_status | Bonding rate 20 % or more 0, under 20 % 6, under 5 % 12, for an established creator only; otherwise 0, unknown or not_assessed. |
Smart-money flowkol_distribution | kol_signal | Tracked KOLs net-selling adds 8. |
Supply inflationsupply_inflation | supply_inflation_pct | Supply above expected in the last 30 days: 0.5 % or more 7, 5 % or more 18. |
Blacklistblacklist | is_blacklisted | Listed (stablecoins, wrapped SOL, LSTs, flagged rugs): 25, and the band is "danger". |
Sum, then bucket
Points are summed, clamped to 0 to 100: under 30 "safe", 30 to 65 "caution", above 65 "danger".
Live authority caps it
A live mint or freeze authority keeps the band at "caution" or worse.
Unknown caps it
Any unknown input makes the assessment incomplete and the score a lower bound; the band stays at "caution" or worse.
No creator, more proof
Without an assessed creator, "safe" also needs healthy liquidity observed in the last 6 hours (a policy threshold; explanations.band_cap says when it applied).
okwarndangerunknownnot_assessedNot a forecast
It adds up conditions observed at request time. It does not estimate how likely a token is to fail.
LP custody is not observed
No Solana LP-custody source exists yet: a locked LP is not credited, an unlocked one not detected.
Trade-derived fields have a scope
Launch concentration, sniper_footprint and creator sells come from the launchpad pipeline; the rest is read from chain. coverage says which applies.
Transfers are only suspected
A creator balance below its own trades is suspected, never confirmed: an uncaptured sell looks the same.
Versioned weights
score_version (v2) rides on every response, so a re-weighting never silently shifts a stored score.
Risk and deployer intelligence
The token check says what the mint looks like now; the creator layer says what its wallet did with every launch, and with this one.
| Layer | What it answers | Plan |
|---|---|---|
In the scoreGET /tokens/{mint}/risk · inputs | Scored only for an established history: a ranked tier, 10 or more observed launches, or 3 or more resolved outcomes. A first launch is not a failure.deployer_bonding_ratedeployer_total_deployeddeployer_history_statusdeployer_reputation_scored | Pro |
On this mintGET /tokens/{mint}/risk · dev | Its self-buy, later buys and sells, and live balance, each with an observation time. Returned, not scored.buy_solbuy_supply_pctsold_tokensfirst_sell_atwallet_emptytransfer_status | Pro |
Across its launchesGET /deployer-hunter/{wallet} | Tier, lifetime and recent bonding rate, last ten outcomes, and self-filled bonds.tierbonding_raterecent_bond_raterecent_outcomesinstant_bondsrunner_rate | Free |
As it stood thenGET /deployer-hunter/{wallet}/as-of · /history | The record on a past date with no look-ahead, for backtests.snapshot_datetierbonding_ratecarried | Pro |
When it moveswallet:scores · deployer:tier_changed | Pushed when a watched creator is reclassified; 25 on Pro, 100 on Ultra, 250 on Business wallets per connection.tier_beforetier_afterentered_rankingstats | From Pro |
01 · cold
Last 10 outcomes without a bond, 3+ launches. Checked first.
02 · elite
5+ launches, 5+ bonds, lifetime 40 %+, recent 50 %+.
03 · good
3+ launches, 3+ bonds, lifetime 25 %+, recent 30 %+.
04 · moderate
3+ launches, 1+ bond, lifetime and recent 15 %+.
05 · rising
1 to 3 launches, every resolved one bonded, more than one self-filled bond.
06 · unranked
Everything else, long histories included. Not the same as new.
A bond within 1 minute of the deploy is instant (the creator's own bundle); all-instant wallets stay out of elite, good and moderate. Launches by elite, good, rising creators also fire deployer:alert. Tier definitions · deployer endpoints · Deployer Hunter
Architecture
MadeOnSol supplies the evidence and never labels a token for your users; two products can read the same response and act differently.
1 · Yours
A mint to check
From a scanner, a KOL trade, a launch alert or a user.
2 · MadeOnSol
Token evidence
Factors, raw inputs, what was not observed.
3 · MadeOnSol
Creator evidence
The dev block, then tier and record.
4 · Yours
Your policy
Your weights, thresholds and handling of unknowns.
5 · Yours
Your action
In your product, under your name.
allowreviewwarnignore
Keep it current
Two scoped channels on /ws/v1/stream push input changes for the mints and creators you name. Streaming reference
token:riskFrom Prorisk:authority_changedrisk:supply_inflated
Means: An authority was revoked, the Token-2022 fee changed, or supply drift crossed 0.5 % or 5 %. risk:inputs snapshot on subscribe.
Does not mean: A new score or band; re-read GET /tokens/{mint}/risk.
filters.mints required, 25 on Pro, 100 on Ultra, 250 on Business mints per connection
wallet:scoresFrom Prodeployer:tier_changed
Means: A watched creator was reclassified, with tier_before, tier_after and stats.
Does not mean: The moment the fact changed: a scheduled recompute can lag; read computed_at and source.
filters.wallets required, 25 on Pro, 100 on Ultra, 250 on Business wallets per connection
Plans
| Check | Where | Plan |
|---|---|---|
| Scored risk, one mint | GET /tokens/{mint}/risk | From Pro |
| Scored risk, many mints | POST /tokens/batch/risk | From ProUp to 50 mints per request |
| Raw checks and creator tier | GET /token/{mint} | FreeNo score |
| Creator profile | GET /deployer-hunter/{wallet} | Free |
| Creator record over time | GET /deployer-hunter/{wallet}/as-of, /history | From Pro |
| Early-buyer bundles | GET /tokens/{mint}/bundle | FreeSummary on Free; top 10 wallets on Pro; full cohort on Ultra |
| Live top holders | GET /tokens/{mint}/holders | From Pro |
| Risk-input changes | token:risk channel | From Pro |
| Creator tier changes | wallet:scores channel | From Pro |
| Keyless, per call | GET /api/x402/tokens/{mint}/risk | No plan: pay per callx402, same score |
Build outcomes
Showing MadeOnSol data to your own users requires a Business plan. See pricing
Keep going
Solution
Trading Bots
Check a mint before an order.
Solution
Token Scanners
Risk next to live token activity.
Product
Deployer Hunter
Creator tiers and launches in the browser.
Guide
Build a rug-check Telegram bot
Reply with the factor breakdown.
Guide
How deployer tiers are scored
The model behind the creator record.
Guide
Score a new pump.fun token in 60 seconds
A checklist to automate.
FAQ
Next step
Check the method, look at a real mint, then score from your own code.