pompa@solana:~/api$

Real-time pump.fun tokens whose website published the CA — with AI scoring and 1h performance tracking.

Quick startOverviewAuth & limitsErrorsGET /tokensGET /tokens/:mint Live stream (SSE)WebhooksGET /logsstats · health · aiSchemasBot examples

Quick start

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

Overview

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.

Authentication & rate limits

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.

Errors

{ "error": { "code": "rate_limited", "message": "Rate limit exceeded, retry in 4s" } }
HTTPcodemeaning
400bad_requestinvalid query parameter
404not_foundunknown endpoint, or token not found / not OK
429rate_limitedslow down, see retry-after
503stream_fulltoo many live-stream clients, retry later
500internalunexpected server error (safe to retry)

List OK tokens

GET/api/v1/tokens

Newest first. Use nextBefore from the response as before to page backwards, or since to fetch only new ones.

paramtypedescription
limitint 1–200page size (default 50)
resultTRACKING | SUCCESS | FAILfilter by 1h performance result
minScoreint 0–100only tokens whose AI score ≥ value
sincemsonly tokens with okAt > since
beforemsonly tokens with okAt < before (pagination)
minTradersintonly tokens with at least N unique traders at OK time
hidePlatforms1hide launchpad pages and cloned template sites
mintscsvspecific 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
}

Token detail

GET/api/v1/tokens/:mint

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


  

Live stream (Server-Sent Events)

SSE/api/v1/stream?types=token.ok,token.ai

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.

eventwhendata
token.okCA found on the token's websitemint, symbol, name, website, twitter, telegram, okAt, okAfterSec, caFoundIn, mcapUsd, volumeUsd, image, links
token.aiAI review finished / skipped / failed (~10–20 s after OK)mint, status (DONE|SKIPPED|ERROR), review {score, verdict, risk_level, one_liner}, reason, token
token.milestoneATH crossed 2x, 3x, 5x, 10x… vs. mcap at OKmint, symbol, multiple, mcapUsd, entryMcapUsd
token.graduatedtracked token graduated to PumpSwapmint, symbol
token.result1h window finishedmint, symbol, result (SUCCESS|FAIL), entryMcapUsd, athMcapUsd, athMultiple, finalMcapUsd
logan important log entry (see /logs)id, ts, level, text, mint
system.aiAI 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,…}

Statistics · health · AI

GET/api/v1/stats

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.



  

Webhooks

Get events pushed to you — no open connection needed. Three target types:

typefieldsdelivery
discordurl (Discord channel webhook)rich embed with links and AI score
telegrambotToken, chatId (your bot from @BotFather, added to your chat/channel)formatted message with pump / dex / solscan buttons
genericurl (public https)JSON { event, ts, data }, signed with X-Pompa-Signature: sha256=HMAC(secret, body)
optiontypedescription
eventsarraytoken.ok, token.ai, token.milestone, token.result (default: ok + ai)
minScore0–100if > 0, the OK alert is sent only after the AI review and only when score ≥ minScore
hidePlatformsboolskip launchpad pages and cloned template sites

POST/api/v1/webhooks

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

GET/api/v1/webhooks/:idstatus, deliveries, last error

POST/api/v1/webhooks/:id/testsend a sample event

DELETE/api/v1/webhooks/:id

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.

Logs

GET/api/v1/logs

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

paramtypedescription
levelscsvlevels (ok, ai, win, loss, trk, warn, err, sys) or groups (ok, ai, results, issues, system)
limitint 1–500default 100
beforeidonly 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" } ] }


  

Health

GET/api/v1/health



  

Learning model

GET/api/v1/model

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.



  

AI status

GET/api/v1/ai

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



  

Schemas

Token

{
  "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
  }
}

Token detail extras

{
  "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.

Bot examples

// 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)); } })();