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 

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 

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"}], }) 

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 

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") 

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!)") 

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> 

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!) 

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 

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