Real-time pump.fun tokens whose website published the CA — with AI scoring and 1h performance tracking.
# latest OK tokens (newest first)
curl -s "/api/v1/tokens?limit=5"
# live feed — every OK token the moment its CA appears on the website
curl -N "/api/v1/stream?types=token.ok"
Pompa watches every new pump.fun token. Tokens with a website in their metadata are checked for up to 10 minutes; when the contract address (CA) appears in the site's HTML or JS bundles the token becomes OK. OK tokens get an AI review (0–100 score) and their market cap is tracked for 1 hour (ATH, multiples, SUCCESS/FAIL). This API exposes exactly what the dashboard shows.
Base URL: /api/v1 · JSON over HTTPS · times are Unix milliseconds · market caps in USD.
The API is public and read-only — no key needed. Requests are rate-limited per IP
(120 requests / minute). If the operator gave you an API key, send it as
x-api-key for a higher limit. Every response has x-ratelimit-remaining; a 429
includes retry-after (seconds).
Responses support ETag / If-None-Match (→ 304) and gzip. CORS is open, so the API can be called from browsers too.
Tip for bots: don't poll — open the live stream and you'll get every OK token the moment it happens.
{ "error": { "code": "rate_limited", "message": "Rate limit exceeded, retry in 4s" } }
| HTTP | code | meaning |
|---|---|---|
| 400 | bad_request | invalid query parameter |
| 404 | not_found | unknown endpoint, or token not found / not OK |
| 429 | rate_limited | slow down, see retry-after |
| 503 | stream_full | too many live-stream clients, retry later |
| 500 | internal | unexpected server error (safe to retry) |
Newest first. Use nextBefore from the response as before to page backwards, or since to fetch only new ones.
| param | type | description |
|---|---|---|
| limit | int 1–200 | page size (default 50) |
| result | TRACKING | SUCCESS | FAIL | filter by 1h performance result |
| minScore | int 0–100 | only tokens whose AI score ≥ value |
| since | ms | only tokens with okAt > since |
| before | ms | only tokens with okAt < before (pagination) |
| minTraders | int | only tokens with at least N unique traders at OK time |
| hidePlatforms | 1 | hide launchpad pages and cloned template sites |
| mints | csv | specific tokens (max 100) — e.g. a watchlist |
curl "/api/v1/tokens?limit=5&minScore=60"
{
"items": [ Token, ... ],
"nextBefore": 1791828000000 // null when there is nothing older
}
Everything in Token plus the full AI review, the website source-code summary, the full description and where the CA was found.
curl "/api/v1/tokens/<MINT>"
A long-lived text/event-stream connection. Each message has an event name, an id and JSON data.
On reconnect send Last-Event-ID (browsers' EventSource does it automatically) to receive missed events (last 200 are buffered).
A : ping comment is sent every 25 s. Optional types filters events.
| event | when | data |
|---|---|---|
| token.ok | CA found on the token's website | mint, symbol, name, website, twitter, telegram, okAt, okAfterSec, caFoundIn, mcapUsd, volumeUsd, image, links |
| token.ai | AI review finished / skipped / failed (~10–20 s after OK) | mint, status (DONE|SKIPPED|ERROR), review {score, verdict, risk_level, one_liner}, reason, token |
| token.milestone | ATH crossed 2x, 3x, 5x, 10x… vs. mcap at OK | mint, symbol, multiple, mcapUsd, entryMcapUsd |
| token.graduated | tracked token graduated to PumpSwap | mint, symbol |
| token.result | 1h window finished | mint, symbol, result (SUCCESS|FAIL), entryMcapUsd, athMcapUsd, athMultiple, finalMcapUsd |
| log | an important log entry (see /logs) | id, ts, level, text, mint |
| system.ai | AI switched on/off or paused (quota) | enabled, paused, pausedUntil, pauseReason |
curl -N "/api/v1/stream?types=token.ok"
id: 42
event: token.ok
data: {"ts":1791828000000,"mint":"7xKX…pump","symbol":"CAT","name":"Moon Cat","okAfterSec":38,"mcapUsd":9800,…}
Funnel (seen → website → OK), win/loss results, ATH multiples, win rate by AI score bucket, realistic (+30 s entry) results, timing (time to ATH / 2x, "2x then crashed"), win rate by hour, by domain, launchpad vs custom sites, and the AI calibration (which sub-scores actually predict wins, AUC, suggested weights). Cached for 3 s.
Get events pushed to you — no open connection needed. Three target types:
| type | fields | delivery |
|---|---|---|
| discord | url (Discord channel webhook) | rich embed with links and AI score |
| telegram | botToken, chatId (your bot from @BotFather, added to your chat/channel) | formatted message with pump / dex / solscan buttons |
| generic | url (public https) | JSON { event, ts, data }, signed with X-Pompa-Signature: sha256=HMAC(secret, body) |
| option | type | description |
|---|---|---|
| events | array | token.ok, token.ai, token.milestone, token.result (default: ok + ai) |
| minScore | 0–100 | if > 0, the OK alert is sent only after the AI review and only when score ≥ minScore |
| hidePlatforms | bool | skip launchpad pages and cloned template sites |
curl -X POST "/api/v1/webhooks" -H "content-type: application/json" -d '{
"type": "discord",
"url": "https://discord.com/api/webhooks/123/abc",
"events": ["token.ok", "token.ai", "token.result"],
"minScore": 60,
"hidePlatforms": true
}'
→ 201 { "id": "wh_…", "manageToken": "whm_…", "secret": "whs_… (generic only)", … }
Keep the manageToken — it is shown once and is required to manage the webhook (send it as x-webhook-token):
Verify generic deliveries (Node.js):
const sig = crypto.createHmac("sha256", SECRET).update(rawBody).digest("hex");
if (req.headers["x-pompa-signature"] !== `sha256=${sig}`) return res.status(401).end();
Limits: 5 webhooks per client, failed deliveries are retried 3×, a webhook is disabled after 50 consecutive failures.
Important events only (the same feed as the dashboard's log panel): OK tokens, AI scores, milestones, graduations, win/loss results, AI pauses, warnings and errors. Newest first, last 500 kept in memory (7 days in the database).
| param | type | description |
|---|---|---|
| levels | csv | levels (ok, ai, win, loss, trk, warn, err, sys) or groups (ok, ai, results, issues, system) |
| limit | int 1–500 | default 100 |
| before | id | only entries with id < before (paging) |
{ "items": [ { "id": 812, "ts": 1791828000000, "level": "ok", "text": "$CAT CA on mooncat.fun after 38s · mcap $9.8K · 42 traders", "mint": "7xKX…pump" } ] }
The self-improving win-probability model behind ai.pWin: a logistic regression over ~30 features (AI sub-scores,
launchpad/template site, time to CA, traders, buy/sell, holders, snipers, dev wallet history, hour). It retrains every 50 new
outcomes or 24 h; a new version goes live only if it beats the active one on outcomes the active one has never seen.
Returns the active version, its test AUC vs. the AI score alone, the strongest signals and the version history.
enabled is the operator switch; paused means the AI provider limit was hit and reviews resume automatically after pausedUntil. While off/paused, OK tokens still arrive with ai.status = "SKIPPED".
{
"mint": "string", // contract address (CA)
"symbol": "string", "name": "string",
"description": "string|null", // truncated to 280 chars in lists
"image": "https://imagedelivery.net/…/coin-image/<mint>/86x86?alpha=true", // pump.fun image CDN, direct
"imageFallback": "https://images.pump.fun/coin-image/<mint>?variant=86x86", // official URL (redirects to the above)
"website": "string", "twitter": "string|null", "telegram": "string|null",
"createdAt": 0, // token creation (ms)
"okAt": 0, // CA detected on website (ms)
"okAfterSec": 0, // seconds from creation to OK
"caFoundIn": "html | script:/assets/index.js",
"domain": "example.fun",
"platform": false, // domain used by ≥ 3 tokens (launchpad page)
"templateReuse": 0, // other token sites with the identical structure (cloned template)
"tradersAtOk": 12,
"screenshot": "/shots/<mint>.jpg|null",
"links": { "pumpfun": "url", "dexscreener": "url", "solscan": "url" },
"ai": {
"status": "DONE | PENDING | SKIPPED | ERROR",
"score": 0-100|null, "verdict": "STRONG | PROMISING | NEUTRAL | WEAK | AVOID",
"riskLevel": "LOW | MEDIUM | HIGH | EXTREME", "oneLiner": "string", "reason": "string|null",
"pWin": 0.23 // learned win probability from the model (null until the first model is active)
},
"performance": { // window starts at okAt
"result": "TRACKING | SUCCESS | FAIL",
"startedAt": 0, "endsAt": 0,
"entryMcapUsd": 0, "currentMcapUsd": 0, "currentMultiple": 0,
"athMcapUsd": 0, "athMultiple": 0, "athAt": 0, "lowMcapUsd": 0,
"mcap5mUsd": 0, "mcap15mUsd": 0, "mcap30mUsd": 0, "mcap60mUsd": 0, "finalMcapUsd": 0,
"volumeSol": 0, "buys": 0, "sells": 0, "graduated": false,
"hit2xAt": 0, // first time the mcap reached 2x of entry
"realistic": { "entryDelaySec": 30, "entryMcapUsd": 0, "athMcapUsd": 0, "athMultiple": 0, "result": "TRACKING | SUCCESS | FAIL" },
"series": [[0, 3200], [30, 3550], …] // [seconds since OK, mcap USD] — 40 points in lists, full in detail
},
"live": { // only while TRACKING (on-chain, real-time)
"priceSol": 0, "priceUsd": 0, "mcapUsd": 0, "volumeUsd": 0, "buys": 0, "sells": 0,
"uniqueTraders": 0, "devSellSol": 0, "bondingCurveProgressPct": 0, "graduated": false, "solUsd": 0
}
}
{
"creator": "string", "caContext": "…text around the CA on the site…",
"creatorIntel": { "wallet", "launchesSeen", "devSoldCount", "quickDumps", "graduatedCount", "walletAgeDays", "txCount",
"solBalance", "fundedBy", "otherCreatorsFromSameFunder", "quickDumpsFromSameFunder" },
"snipers": { "sniperWallets", "bundledInCreateSlot", "sniperBoughtPct", "sniperHoldingPct", "sniperBuySol", "devInitialBuyPct" },
"ai": { …, "review": {
"score": 0, "verdict": "…", "riskLevel": "…",
"subscores": { "websiteQuality": 0-10, "narrativeStrength": 1|3|5|7|9, "marketHealth": 0-10, "teamEffort": 0-10 },
"scoreReasons": { "website_quality": ["+2 site reachable with content", …], "market_health": […], … },
"oneLiner": "…", "tokenSummary": "…", "websiteSummary": "…",
"strengths": ["…"], "redFlags": ["…"], "comment": "…", "model": "…" } },
"websiteSource": { "url", "httpStatus", "title", "metaDescription", "stack": ["Next.js", …], "htmlBytes", "jsBytes",
"scripts": {…}, "sections", "headings": [], "socialLinks": [], "otherSolanaAddresses": [], … }
}
websiteQuality, marketHealth, teamEffort and riskLevel are computed deterministically from measured data (see scoreReasons); the AI judges narrativeStrength and writes the text. Score = 3·websiteQuality + 2·narrativeStrength + 3·marketHealth + 2·teamEffort − (HIGH 10 | EXTREME 25). SUCCESS = ATH ≥ 2x of the mcap at OK within 60 min.
// Node 18+ — stream OK tokens and react instantly
const BASE = "";
async function stream() {
const res = await fetch(`${BASE}/api/v1/stream?types=token.ok,token.ai`);
const reader = res.body.getReader();
const dec = new TextDecoder();
let buf = "";
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buf += dec.decode(value, { stream: true });
let i;
while ((i = buf.indexOf("\n\n")) >= 0) {
const msg = buf.slice(0, i); buf = buf.slice(i + 2);
const event = /^event: (.+)$/m.exec(msg)?.[1];
const data = /^data: (.+)$/m.exec(msg)?.[1];
if (!event || !data) continue;
const d = JSON.parse(data);
if (event === "token.ok") console.log("OK", d.symbol, d.mint, d.links.pumpfun);
if (event === "token.ai" && d.review?.score >= 60) console.log("AI", d.token.symbol, d.review.score);
}
}
}
(async () => { for (;;) { try { await stream(); } catch (e) { console.error(e.message); } await new Promise(r => setTimeout(r, 3000)); } })();