GET/api/launch-risk?mint={mint}
Live · free in betaLaunch risk for one token, read from Solana mainnet when you call: who holds it, whether the top holders got their tokens straight from one wallet, whether the issuer can still mint or freeze, and a verdict.
Query parameters
| Name | Type | Description |
|---|---|---|
| mintrequired | string | Token mint address, base58. A link that contains it works too, for example a pump.fun or Solscan token page. |
Example
curl -s "https://www.assayonsol.io/api/launch-risk?mint=$MINT"
const url = `https://www.assayonsol.io/api/launch-risk?mint=${mint}`;
const res = await fetch(url);
const risk = await res.json();
if (!res.ok) throw new Error(risk.error); // 400, 404, 429, 502: plain words
if (risk.verdict === 'high') {
throw new Error(`Assay: launch risk is high. ${risk.summary} ${risk.explorer}`);
}
Responses
| Status | Description |
|---|---|
| 200 | A LaunchRisk object. Cache-Control: public, max-age=300. |
| 400 | Not a mint address: missing, wrong length, not base58, or a transaction signature. |
| 404 | Not a token: no account on mainnet, a wallet, a program, a pool address or an NFT. A token account answers with mint_hint, the mint it holds. |
| 429 | Too many checks from one address. Wait for Retry-After. |
| 502 | The mint itself couldn’t be read from the Solana data provider. Try again in a moment. |
Every error is {"error": "…plain words…"}, with usage on a 400. When a later read runs out of time (the largest holders, the holder count, the transfers), the answer is still a 200: those fields are null and coverage says what wasn’t read.
LaunchRisk
| Field | Type | Description |
|---|---|---|
| mint, name, symbol | string | The token. Name and symbol come from its metadata and can be null. |
| decimals, supply | integer, string | Supply in whole tokens, as an exact decimal string. |
| token_program | spl-token · spl-token-2022 | Which token program the mint uses. |
| holders, holders_more, holders_display | integer · null, boolean, string · null | Wallets with a balance, counted on one page of up to 1,000 token accounts. Show holders_display: the count, or “1,000+” when the page came back (near) full or the count timed out on a large token. The count is best effort: when it doesn’t come back, holders is null and coverage.holders_note says why. |
| top1_share, top10_share | number, 0 to 1 | Share of supply held by the largest holder, and by the ten largest together. Pools, the burn address and program accounts are set apart first. |
| linked_share | number, 0 to 1 · null | Of the tokens the top 20 holders hold, the share held by the largest group of them linked by direct transfers. null when the transfers couldn’t be read. |
| linked_supply_share | number · null | The same group’s holdings as a share of total supply. |
| linked_source | string · null | The wallet that sent tokens to most of the group. |
| linked_wallets | string[] | The wallets in that group, largest first. |
| top_holders[] | object[] | Up to 20 holder wallets: wallet, share, from (the wallet that sent it the most tokens, if any) and linked. |
| pools[] | object[] | What was set apart: label (for example “PumpSwap pool”, “Burn address”), kind (pool · burn · mint · program), owner, share. |
| mint_authority, freeze_authority | boolean | Whether the issuer can still mint more, or freeze holders’ tokens. The addresses are in authorities. |
| verdict | low · medium · high · null | By linked_share: low below 0.10, medium from 0.10 to 0.25, high above 0.25. An active freeze authority or a permanent delegate raises it to at least medium. null for tokens that aren’t launch tokens (USDC, USDT, wrapped SOL; see note) and when the transfers couldn’t be read. |
| summary, flags[] | string, string[] | The answer in one sentence, and what stands out, in plain words: “Freeze authority is active — the issuer can freeze your tokens”, “Mint authority is active — supply can be inflated”, “Top holder owns 31%”. |
| coverage | object | What was read to link holders: transactions, from, to, complete, holders, traced and a note. When complete is false, the linked share is a lower bound. |
| checked_at, explorer | string | When mainnet was read (answers are cached up to 5 minutes), and the token on Solscan. |
Response · 200 · shape
From the function’s offline test fixture, shortened: the addresses are made up, the fields are exactly what the API returns.
{
"mint": "2rrVfjh9pDocit4YxxAkPhV5UYv86mW8rYrgzCk3cQRY",
"name": "Kettle Test",
"symbol": "KTT",
"decimals": 6,
"supply": "1000000000",
"token_program": "spl-token",
"holders": 37,
"holders_more": false,
"holders_display": "37",
"top1_share": 0.06,
"top10_share": 0.257,
"linked_share": 0.7198,
"linked_supply_share": 0.185,
"linked_source": "HsSCaYFSZ4RoRr3JJ7myV9gAWcRMVejjfcfEwEx8QeUk",
"linked_wallets": ["HsSCaYFSZ4RoRr3JJ7myV9gAWcRMVejjfcfEwEx8QeUk", "FBJTFuRUeCbW2G4jTZ94mMxpf4xwaUHQxmQ9LgLriMGG", "…"],
"top_holders": [{ "wallet": "HsSC…QeUk", "share": 0.06, "from": null, "linked": true }, "…"],
"pools": [{ "owner": "5zXH…LMUu", "label": "Pump.fun bonding curve", "kind": "pool", "share": 0.4 }, "…"],
"mint_authority": false,
"freeze_authority": false,
"authorities": { "mint": null, "freeze": null },
"verdict": "high",
"summary": "5 of the top 10 holders are linked by direct transfers (HsSC…QeUk sent tokens to 3 of them). Together they hold 72% of the top holders’ tokens, 19% of supply.",
"flags": ["5 top holders are linked by direct transfers — HsSC…QeUk sent tokens to 3 of them"],
"coverage": { "read": true, "order": "oldest first", "transactions": 8, "complete": true, "holders": 10, "traced": 9, "note": "…" },
"checked_at": "2026-10-14T20:39:19.280Z",
"explorer": "https://solscan.io/token/2rrVfjh9pDocit4YxxAkPhV5UYv86mW8rYrgzCk3cQRY"
}