DEV Community

nyam
nyam

Posted on Edited on

Build a 60-second Solana memecoin risk checker in Python with free APIs

Most Solana memecoins die. A few look clean on a chart and still rug. Before you ape, you want a short technical pass: mint/freeze authority, holder concentration (without counting the bonding curve as a whale), LP lock, and a couple of RugCheck signals.

Below is a free, keyless Python 3 script (solcheck.py) that does that in about a minute. Full copy-paste source: solcheck.py on Telegraph. Stdlib only. No API keys.

Not financial advice. A green flag summary does not mean "safe". Bundles and snipers still need a manual look.

What you get

python3 solcheck.py <MINT_ADDRESS>
python3 solcheck.py <MINT_ADDRESS> --json
Enter fullscreen mode Exit fullscreen mode

Output includes name/symbol/age/launchpad, mcap, liquidity, 24h volume and buys/sells, mint & freeze authority from chain RPC, holder count, top-10 % excluding bonding curve / AMM pools / lockers, dev wallet + holdings + other launches, LP locked %, RugCheck score/risks/insiders, and a RED / YELLOW / GREEN flag list.

Data sources (all free, no keys)

Source What we take
RugCheck api.rugcheck.xyz token meta, top holders, knownAccounts, markets/LP lock, creator, risks, graph insiders, score
DexScreener api.dexscreener.com pair age, mcap, liquidity, 24h volume/txns
Jupiter lite-api price/liquidity fallback, token search meta
Solana public RPC getAccountInfo mintAuthority / freezeAuthority (source of truth)

The script retries on HTTP 429/5xx with simple backoff and prints n/a for fields that failed instead of crashing.

Fetch helper

UA = {"User-Agent": "solana-memecoin-checker/1.0", "Accept": "application/json"}
RPC = "https://api.mainnet-beta.solana.com"

def fetch(url, body=None, retries=3, timeout=20):
    data = json.dumps(body).encode() if body is not None else None
    hdrs = dict(UA, **({"Content-Type": "application/json"} if data else {}))
    err = None
    for i in range(retries):
        try:
            req = urllib.request.Request(url, data=data, headers=hdrs)
            with urllib.request.urlopen(req, timeout=timeout) as r:
                return json.loads(r.read().decode()), None
        except urllib.error.HTTPError as e:
            err = f"HTTP {e.code}"
            if e.code in (429, 500, 502, 503, 504):
                time.sleep(1.5 * (i + 1)); continue
            return None, err
        except Exception as e:
            err = type(e).__name__ + ": " + str(e)[:80]
            time.sleep(1)
    return None, err
Enter fullscreen mode Exit fullscreen mode

Five calls per mint:

rc, errors["rugcheck"] = fetch(f"https://api.rugcheck.xyz/v1/tokens/{mint}/report")
ds, errors["dexscreener"] = fetch(f"https://api.dexscreener.com/tokens/v1/solana/{mint}")
jp, errors["jupiter_price"] = fetch(f"https://lite-api.jup.ag/price/v3?ids={mint}")
js, errors["jupiter_search"] = fetch(f"https://lite-api.jup.ag/tokens/v2/search?query={mint}")
rpc, errors["rpc"] = fetch(RPC, {
    "jsonrpc": "2.0", "id": 1,
    "method": "getAccountInfo",
    "params": [mint, {"encoding": "jsonParsed"}],
})
Enter fullscreen mode Exit fullscreen mode

Pick the DexScreener pair with the most USD liquidity so you don't report a dust pool.

The holder trap: exclude the curve and the pools

If you sum "top holders" raw on a pump.fun token, the bonding curve often shows up as ~90% and looks like a whale. It isn't. Same for AMM/LP accounts and lockers.

known = rc.get("knownAccounts") or {}
excl = {a for a, v in known.items() if v.get("type") in ("AMM", "LOCKER")}
for m in rc.get("markets") or []:
    for k in ("pubkey", "liquidityAAccount", "liquidityBAccount"):
        v = m.get(k)
        if isinstance(v, str): excl.add(v)
        elif isinstance(v, dict):
            excl.update(x for x in (v.get("pubkey"), v.get("owner")) if isinstance(x, str))

holders = rc.get("topHolders") or []
real = [h for h in holders
        if h.get("owner") not in excl and h.get("address") not in excl]
top10 = sum(h.get("pct") or 0 for h in real[:10]) if holders else None
Enter fullscreen mode Exit fullscreen mode

That top10 number is what the flags use.

Authorities from RPC first

RugCheck usually has mint/freeze fields, but the mint account on chain is the source of truth:

info = (((rpc or {}).get("result") or {}).get("value") or {}).get("data", {})
info = info.get("parsed", {}).get("info", {}) if isinstance(info, dict) else {}

def auth(key):
    if info: return info.get(key)
    return rc.get(key) if rc else "unknown"

mint_auth, freeze_auth = auth("mintAuthority"), auth("freezeAuthority")
Enter fullscreen mode Exit fullscreen mode

None means revoked. Any pubkey means the authority is still active (dev can mint more, or freeze wallets).

Flag thresholds

Check Green Yellow Red
Mint / freeze authority revoked – active
Top-10 ex pools ≤15% 15–30% >30%
Dev holdings ≤3% 3–10% >10%
LP locked ≥90% 50–90% <50%
RugCheck risk level – warn danger
Graph / top-holder insiders – – any

Extra yellows: still on the pump.fun curve, liquidity under $10k after migration, buy/sell ratio > 8 (possible bots/wash), age under 1 hour, serial launcher (dev with >3 other tokens).

red, yellow, green = [], [], []
(red if mint_auth else green).append(
    "mint authority " + ("ACTIVE (dev can print tokens)" if mint_auth else "revoked")
)
(red if freeze_auth else green).append(
    "freeze authority " + ("ACTIVE (dev can freeze wallets)" if freeze_auth else "revoked")
)
if top10 is not None:
    (red if top10 > 30 else yellow if top10 > 15 else green).append(
        f"top-10 holders (ex pools/curve) {top10:.1f}%"
    )
# ... LP lock, dev %, curve, age, RugCheck risks, insiders ...
verdict = ("RED - high risk" if red
           else "YELLOW - caution" if yellow
           else "GREEN - basics look clean (still risky!)")
Enter fullscreen mode Exit fullscreen mode

What it does NOT check

There is no reliable free public API for bundle/sniper detection. The script prints manual links instead:

https://trench.bot/bundles/<MINT>
https://app.bubblemaps.io/sol/token/<MINT>
Enter fullscreen mode Exit fullscreen mode

Also watch socials and whether the site/X account was copied from another project. That stays human work.

Real run (2026-11-05 ~20:11 Europe/Moscow)

I ran the same logic over 28 tokens from DexScreener's Solana boosts/profiles feeds. Snapshot counts that hour: 19 RED / 5 YELLOW / 4 GREEN. Numbers go stale in minutes; this is a freeze-frame, not a ranking.

Example A: $Democat (The Democrat Cat), mint 9Y1F6dZ6HpjZDYZdYuUGwMbkUiPsyU7WPTqTQU4Tpump:

age 19.3 h | mcap ~$417k | liq ~$54k | holders 5896
mint/freeze revoked | top-10 ex pools 5.6% | LP locked 100%
dev holds 0% | RugCheck score 1 | graph insiders 0
summary: GREEN - basics look clean (still risky!)
Enter fullscreen mode Exit fullscreen mode

Example B: $Raul (Raul Bautista), mint C1dNqHWwrLC6Hmtbr75BL3FmH4UCN3ztam7VDtwhpump:

age 45.6 h | mcap ~$35k | liq ~$15k | holders 327
mint/freeze revoked | top-10 ex pools 38.0% | LP locked 100%
dev holds 0% | RugCheck graph insiders 6
[RED] top-10 holders (ex pools/curve) 38.0%
[RED] RugCheck detected 6 insider wallets via graph analysis
summary: RED - high risk
Enter fullscreen mode Exit fullscreen mode

Same green authorities and LP lock in both. The difference is concentration and insider graph. That is why the top-10-ex-pools step matters.

Run it yourself

  1. Copy solcheck.py from the Telegraph script page.
  2. python3 solcheck.py <MINT> (Python 3, no pip installs).
  3. Open the trench.bot and Bubblemaps links it prints.
  4. Re-run before you trade; data ages fast.

If you want machine-readable output for a sheet or a small dataset, add --json.

Disclaimer

Education only. Not financial advice. Memecoins can go to zero. Flags are heuristics. Confirm on-chain yourself.

Disclosure: if you trade on Axiom, signing up via my referral link gets you their documented 10% fee discount (and I get a share of fees): https://axiom.trade/@anteikq

Top comments (0)