CommRadar API

Three feeds -- fomo.family, pump.fun, and X -- real-time, no delay. Every request returns {"rows": [...], "total": N}, newest first. Base URL is https://commradar.xyz.

Every request requires a key, sent as an X-API-Key header:

curl "https://commradar.xyz/api/v1/fomo-feed?limit=1" \
  -H "X-API-Key: YOUR_KEY"
Don't have a key yet? Get in touch (contact details coming soon). Want to see it working first? Check the live demo — same data, ~15 minutes delayed, no key needed.

CORS is enabled on all endpoints below (Access-Control-Allow-Origin: *) — safe to call directly from browser JS, no server-side proxy needed. Want a machine-readable version of this page instead? GET /api/v1/endpoints returns the same reference as JSON.

GET/api/v1/fomo-feed

Token theses and callouts sourced from fomo.family's global feed, real-time — one row per post, across Solana, Ethereum, Base, BNB, Robinhood, and Monad.

Query Params

ParamTypeDefaultDescription
limitint50Max rows to return (capped at 200).
beforestringA created_at value from a previous response — returns rows older than it, for pagination.
chainstringFilter to one chain: solana, ethereum, base, bnb, robinhood, or monad.

Example

curl "https://commradar.xyz/api/v1/fomo-feed?limit=1" \
  -H "X-API-Key: YOUR_KEY"

Example Response

{
  "rows": [
    {
      "id": "6e2a69c0-12a1-4536-8f0f-26ca8b5bcd63",
      "feed_type": "thesis_created",
      "token_address": "Ai66LHZG9MCzg1WKdawwqduVAXpNDUuV8M3uyq5ppump",
      "chain": "solana",
      "ticker": "CATE",
      "token_image_url": "https://token-media.defined.fi/...",
      "display_name": "Faisal",
      "user_handle": "Faisac67",
      "user_image_url": "https://prod-fomo-profile-pics.s3.amazonaws.com/...",
      "comment_text": "gm ratio htx\n\nhttps://x.com/poorgoat_/status/2092899810697253217",
      "created_at": "2026-08-27T09:58:39.374Z",
      "likes": 0,
      "pct_unrealized_pnl": -8.099,
      "pct_realized_pnl": 0.0,
      "market_cap": 58666059.67,
      "position_value_usd": 10091.17,
      "convergence": { "fomo": 2, "pumpfun": 0 }
      // ...plus every field in the table below
    }
  ],
  "total": 1625
}

Row Fields

FieldDescription
idUnique post ID.
feed_typethesis_created (a position-backed thesis) or manual (a plain callout with no position data — title is set instead).
token_addressToken contract address.
network_idNumeric chain ID (1=Ethereum, 56=BNB, 143=Monad, 4663=Robinhood, 8453=Base, 1399811149=Solana).
chainHuman-readable chain name.
tickerToken symbol.
token_image_urlToken logo, or null.
user_id / display_name / user_handle / twitter / user_image_urlCaller identity. user_handle is the caller's fomo.family account handle, not their X handle — fomo doesn't reliably expose a real X handle (twitter is typically empty). Cross-platform caller merges (see /api/v1/caller/<handle>) only catch fomo callers whose fomo handle happens to match their X handle.
comment_textThe post body / thesis text.
title / linkOnly set when feed_type is manual.
created_atISO8601 UTC timestamp the post was made.
likesLike count at time of discovery (not re-checked afterward).
unrealized_pnl_usd / realized_pnl_usdCaller's position PnL in USD.
pct_unrealized_pnl / pct_realized_pnlSame, as a percentage.
market_capToken market cap at time of discovery.
position_value_usdCaller's position value in USD.
human_token_amountCaller's token holding, in whole tokens.
current_priceToken price at time of discovery.
discovered_atWhen CommRadar's own scraper first saw this post (not the same as created_at).
convergence{"fomo": N, "pumpfun": M, "x": K, "both_apps": bool} -- unique callers (deduped by handle) who mentioned this same token on each platform in the last 15 minutes. both_apps is true when both fomo and pumpfun are non-zero. Always live/real-time, even on the delayed tier -- it's an aggregate signal, not the underlying post content.
GET/api/v1/pumpfun-callouts

Token callouts from pump.fun's home feed, real-time, threaded — each row is an original callout with any follow-up replies nested under it in updates, matching pump.fun's own UI.

Query Params

ParamTypeDefaultDescription
limitint50Max rows to return (capped at 200).
beforestringA last_activity value from a previous response — returns threads older than it, for pagination.

Example

curl "https://commradar.xyz/api/v1/pumpfun-callouts?limit=1" \
  -H "X-API-Key: YOUR_KEY"

Example Response

{
  "rows": [
    {
      "callout_id": "a3080158-5aef-4e2e-bcc0-5e0ad7de7e58",
      "coin_mint": "B8xtQz9CK7xr56dboGx8gL2Zu4dB2MiydFe4b8NPpump",
      "coin_name": "My Life Is Solana",
      "symbol": "Solana",
      "chain": "solana",
      "user_name": "thirty300",
      "x_username": "gaynes_g",
      "thesis": "Solana $105 — my life is Solana",
      "called_out_at_mcap": 29240.0,
      "multiple": 0.595,
      "pnl_percentage": -28.76,
      "callout_timestamp": "2026-08-27T09:55:15.528Z",
      "last_activity": "2026-08-27T09:58:32.242Z",
      "updates": [
        {
          "user_name": "thirty300",
          "thesis": "All the reasons not to sell the dust...",
          "callout_timestamp": "2026-08-27T09:58:32.242Z"
          // ...same fields as a top-level row
        }
      ],
      "convergence": { "fomo": 0, "pumpfun": 3 }
      // ...plus every field in the table below
    }
  ],
  "total": 10960
}

Row Fields

FieldDescription
callout_idUnique ID for this callout or update.
entry_typecallout for a top-level post, update for a threaded reply (only appears inside updates).
parent_callout_idSet on updates only — the callout_id they're threaded under.
coin_mint / coin_name / symbol / coin_image / chainToken identity.
market_capToken market cap at time of discovery.
user_id / user_name / profile_image / wallet_addressCaller identity.
is_verified1 if pump.fun shows a verified badge on the caller, else 0.
x_username / x_profile_image / follower_countCaller's linked X account, if any.
thesisThe callout or reply text.
called_out_at_mcapToken market cap at the moment this was posted — compare to current market_cap for "since call" movement. Null on updates.
multipleToken price growth multiple since the call (market performance — not the caller's own entry price). Null on updates.
callout_timestampISO8601 UTC timestamp this was posted.
likes / view_count / repost_count / comment_countEngagement counts at time of discovery (not re-checked afterward).
value_usd / cost_basis_usdCaller's current position value and cost basis in USD.
pnl_usd / pnl_percentageCaller's own PnL on this specific position (their real entry price, not the call-moment price).
max_multiplierHighest multiple this callout has reached since being posted. Null on updates.
discovered_atWhen CommRadar's own scraper first saw this (not the same as callout_timestamp).
last_activityTop-level rows only — whichever is more recent, the callout itself or its newest update. This is what the feed sorts by.
updatesTop-level rows only — array of threaded replies, oldest first, same field shape minus updates/last_activity/follow_up_count.
follow_up_countTop-level rows only — updates.length, provided so you don't have to count the array yourself.
is_koltrue if x_username matches a handle on CommRadar's own curated X KOL List (the same list /api/v1/x-calls is sourced from). Reflects our own list-import bookkeeping, not a live X membership check — added 2026-08-29, see "Known limitations" below.
convergenceTop-level rows only — {"fomo": N, "pumpfun": M, "x": K, "both_apps": bool}, unique callers (deduped by X handle, or wallet if unlinked) who mentioned this same token on each platform in the last 15 minutes. both_apps is true when both fomo and pumpfun are non-zero. Always live/real-time, even on the delayed tier.
GET/api/v1/x-calls

Token mentions from X (Twitter), real-time -- tweets from a curated KOL list that mention a resolvable token. A tweet only counts as a call if it contains either a raw contract address, or a $CASHTAG that resolves unambiguously against fomo/pumpfun's own recent ticker history. An ambiguous or unresolvable cashtag (no match, or the ticker collides across more than one real token) is dropped rather than guessed at -- a missed tweet is a gap, a wrong token attached to someone's real call is a correctness bug. Cashtag-only chatter with no resolvable token, and tweets with no token mention at all, never appear here.

Query Params

ParamTypeDefaultDescription
limitint50Max rows to return (capped at 200).
beforestringA tweet_created_at value from a previous response — returns tweets older than it, for pagination.
chainstringFilter to one chain (e.g. solana).

Example

curl "https://commradar.xyz/api/v1/x-calls?limit=1" \
  -H "X-API-Key: YOUR_KEY"

Example Response

{
  "rows": [
    {
      "tweet_id": "1234567890123456789",
      "author_handle": "gsksolana",
      "tweet_text": "This is the $CATE community...",
      "image_url": null,
      "avatar_url": "https://pbs.twimg.com/profile_images/.../abc_normal.jpg",
      "token_address": "Ai66LHZG9MCzg1WKdawwqduVAXpNDUuV8M3uyq5ppump",
      "chain": "solana",
      "extraction_source": "cashtag_resolved:fomo",
      "tweet_url": "https://x.com/gsksolana/status/1234567890123456789",
      "tweet_created_at": "2026-08-27T13:21:37.000Z",
      "discovered_at": "2026-08-27T13:22:04.118203+00:00",
      "convergence": { "fomo": 2, "pumpfun": 1, "x": 1 }
    }
  ],
  "total": 340,
  "list_member_count": 105
}

Row Fields

FieldDescription
tweet_idThe tweet's own ID.
author_handleCaller's X handle, no @.
tweet_textFull tweet text (fetched from the tweet's own page -- X's timeline view truncates long tweets, so this is never the clipped preview).
image_urlFirst attached image, if any. Null for text-only tweets or ones whose only media is a link-preview card, not an uploaded photo.
avatar_urlCaller's profile picture at time of discovery.
token_address / chainThe resolved token. See extraction_source for how it was resolved.
extraction_sourceHow the address was found: text_address (raw address in the tweet), pumpfun_link / birdeye_link / solscan_link (a direct token-address link), or cashtag_resolved:fomo / cashtag_resolved:pumpfun (a $CASHTAG matched against that feed's recent ticker history).
tweet_urlLink to the tweet on x.com.
tweet_created_atISO8601 UTC timestamp the tweet was actually posted (not when CommRadar saw it).
discovered_atWhen CommRadar's own scraper first saw this (not the same as tweet_created_at).
convergence{"fomo": N, "pumpfun": M, "x": K, "both_apps": bool}, unique callers who mentioned this same token on each platform in the last 15 minutes. both_apps is true when both fomo and pumpfun are non-zero. Always live/real-time, even on the delayed tier.

Response-level Fields

Alongside the usual rows and total, this endpoint also returns list_member_count -- the current real size of the curated KOL list this feed sources from (checked directly against X roughly every 15 minutes). It's a measure of coverage, not of this specific response's row count.

GET/api/v1/token/<address>

Every call for one specific token, real-time, merged across all three platforms and sorted newest first. Pass an exact contract address for an unambiguous match, or a ticker/symbol (e.g. CATE) as a convenience -- since tickers aren't unique, a symbol search can surface multiple distinct tokens that happen to share a name; each result still shows its own real address so it's easy to tell them apart. Ticker search only matches fomo/pumpfun -- x-calls has no raw ticker field, only already-resolved addresses, so an address search is the only way to reach x-calls rows.

Example

curl "https://commradar.xyz/api/v1/token/Ai66LHZG9MCzg1WKdawwqduVAXpNDUuV8M3uyq5ppump" \
  -H "X-API-Key: YOUR_KEY"

Response

Same shape as /api/v1/fomo-feed, /api/v1/pumpfun-callouts, and /api/v1/x-calls rows, merged into one list -- each row also carries a _source field ("fomo", "pumpfun", or "x") so you can tell which platform it came from. pump.fun rows here are flat, not threaded -- entry_type: "update" replies show up as their own individual rows instead of nested under a parent, since for "who's talking about this token" every reply is a real post by a real person.

GET/api/v1/caller/<handle>

Every call by one specific caller, real-time, merged across all three platforms. Matches on handle (with or without the @) -- x_username on pump.fun and author_handle on x-calls are real X handles, but user_handle on fomo is the caller's fomo.family account handle, not their X handle. A fomo caller only merges with their pump.fun/x-calls activity here if their fomo handle happens to match their X handle.

Example

curl "https://commradar.xyz/api/v1/caller/gaynes_g" \
  -H "X-API-Key: YOUR_KEY"

Same response shape as the token endpoint above.

GET/api/v1/trending-tokens

Top 5 tokens right now by rolling 15-minute distinct-caller count across fomo.family and pump.fun (the same 🔥 mechanic shown on the live demo -- a call landing is +1, a call aging out past 15 minutes is -1, so this reorders live). X is excluded from this particular ranking by design. Each token comes with live price_usd/market_cap/price_change_24h from DexScreener already attached.

Example

curl "https://commradar.xyz/api/v1/trending-tokens" \
  -H "X-API-Key: YOUR_KEY"

Response

{"tokens": [...]} -- each entry has token_address, chain, ticker, image, fomo/pumpfun (distinct-caller counts feeding the total), total, both_apps (true when both fomo and pumpfun are non-zero -- added 2026-08-29), plus the DexScreener fields above.

GET/api/v1/fomo-top-callers

fomo.family callers ranked by win rate (min. 3 calls to qualify, override with min_calls). Important: every PnL figure in this product is frozen at the moment a call was scraped and never re-checked afterward -- this is not a live-updated or outcome-verified win rate, it's a snapshot ratio. Treat it as a rough signal, not a guarantee.

Example

curl "https://commradar.xyz/api/v1/fomo-top-callers?limit=10&hours=24" \
  -H "X-API-Key: YOUR_KEY"

Params

ParamDescription
min_callsMinimum calls to qualify. Default 3.
limitDefault 5, max 50.
hoursOptional. Window the ranking to calls made in the last N hours (e.g. hours=24) instead of the default lifetime scope. Added 2026-08-29 -- ranking methodology is unchanged (still win rate, not $ value); this only narrows the time window it's computed over.
GET/api/v1/pumpfun-top-callers

Same as /api/v1/fomo-top-callers above, for pump.fun callers, plus the same hours param. Same frozen-at-scrape-time caveat applies. Each row also carries is_kol (see /api/v1/pumpfun-callouts row fields for what that reflects).

Example

curl "https://commradar.xyz/api/v1/pumpfun-top-callers?limit=10&hours=24" \
  -H "X-API-Key: YOUR_KEY"
GET/api/v1/top-kol-positions

Top individual KOL positions by $ value, across fomo.family and pump.fun. Not a sum -- each row is one caller's single largest known position on one token, deduped to their latest snapshot per (caller, token) pair. This sidesteps a real problem elsewhere in this product: the same real position gets re-logged as a new row on every repost/update (confirmed live -- one caller had 24 rows on one token), so summing raw position rows massively inflates the true number. Nothing here is added together, so that problem doesn't apply. Still frozen-at-scrape-time like every other $ figure in this product -- a position's value is whatever it was worth the moment it was last seen, not a live mark-to-market number. Added 2026-08-29.

Example

curl "https://commradar.xyz/api/v1/top-kol-positions?limit=10&hours=24" \
  -H "X-API-Key: YOUR_KEY"

Params

ParamDescription
hoursWindow to look for positions in. Default 24.
limitDefault 15, max 50.

Row Fields

FieldDescription
sourcefomo or pumpfun.
caller_handle / caller_name / caller_avatarCaller identity. For fomo, caller_handle is fomo's own account handle, not necessarily a real X handle.
token_address / chain / ticker / token_imageToken identity.
position_value_usdThe single position snapshot value this row was ranked on.
market_capToken market cap as of this snapshot.
called_out_at_mcappump.fun only -- market cap at the moment of the original call. Always null for fomo rows; fomo has no equivalent distinct entry-MC field.
is_kolpump.fun rows only -- see /api/v1/pumpfun-callouts row fields. Always false for fomo rows (can't reliably KOL-match fomo's handle).
tsISO8601 UTC timestamp of this snapshot.

Errors

A missing or invalid X-API-Key returns 401 with {"error": "missing or invalid API key"}. There's no rate limit enforced yet — please keep polling reasonable (once every 15-20s is plenty; that's what the live demo itself does).

Webhooks

Provisioned per-partner on request. Rather than polling, we push each new row to your URL the instant it's written — this is the low-latency option if you need calls as they happen, not on a fixed interval. A short-poll cadence against the REST endpoints above still works fine and remains available as a reconciliation fallback (e.g. to catch anything during downtime on your end).

Payload

{
  "event_id": "a8b51b6f-59af-479c-a8b9-ea9d92b9a08c",
  "event_type": "fomo_feed_post",
  "sent_at": "2026-08-27T14:21:38.382523+00:00",
  "data": { ... same row shape as the matching REST endpoint ... }
}
event_typefomo_feed_post (fomo.family), pumpfun_callout or pumpfun_callout_update (pump.fun — mirrors the entry_type split on /api/v1/pumpfun-callouts), or x_call (X).
dataSame field names as the row-level fields documented above for that platform, but only the raw fields captured at insert time. Not included on webhook events (GET-endpoint-only, computed at request time, not at insert time): convergence (and its both_apps flag), pump.fun's follow_up_count and nested updates array (updates arrive as their own pumpfun_callout_update event instead), and is_kol. If you need any of these, poll the matching REST endpoint rather than relying on the webhook payload alone.

Verifying deliveries

Every request carries an X-CommRadar-Signature header: sha256= followed by the HMAC-SHA256 hex digest of the raw request body, keyed with your webhook secret. Verify against the raw bytes before parsing JSON.

import hashlib, hmac

expected = "sha256=" + hmac.new(SECRET.encode(), raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers["X-CommRadar-Signature"]):
    raise Exception("invalid signature")
X-CommRadar-Signaturesha256=<hex digest>, as above.
X-CommRadar-EventSame value as the payload's event_type, for routing without a JSON parse.
X-CommRadar-DeliveryMatches event_id — use as an idempotency key, since retries reuse the same id.

Retries

Any response outside 2xx, or a timeout, is retried up to 3 attempts total (immediate, then +2s, then +10s). Respond 2xx as soon as the delivery is durably received on your end — do your own processing after responding, not before, so a slow handler doesn't read as a failed delivery and trigger a redundant retry. A delivery that still fails after all 3 attempts is logged to a dead-letter file on our side, not automatically retried again — if a delivery is missing, tell us and we can check/replay it manually, or just poll the REST endpoint as a reconciliation fallback (a call is never dropped from the data itself, only from the push side).