{"openapi":"3.1.0","info":{"title":"FomoScan Identity API","version":"1.1.0","description":"Resolve a FOMO trader handle to their on-chain wallet addresses, and\nany wallet back to the traders behind it. Every returned address is a\nverified match.\n\n## Authentication\nSend your API key as `Authorization: Bearer fsk_live_…` (or the\n`X-Api-Key` header).","contact":{"name":"FomoScan","url":"https://fomoscan.sh","email":"fomoscan-support@proton.me"}},"servers":[{"url":"https://api.fomoscan.sh","description":"Production"}],"tags":[{"name":"Identities","description":"Resolve handles ↔ wallets."},{"name":"Thesis Feed","description":"Trader \"thesis\" posts, newest first — by token, by author, or both. Each endpoint serves two jobs: poll it with no `before` for a live feed, or walk `before` backwards to backfill. Clients MUST dedupe on `id`, and can resolve any thesis to its author with `authorId`."},{"name":"Leaderboards","description":"FOMO's platform-wide rankings — top traders, top clans, and most-held tokens. Sampled every 30 seconds and stored as whole snapshots: a ranking has no id and never settles, so these are read by instant. Omit `at` for the current board, or pass one to read it as it stood then."},{"name":"WebSocket","description":"The one streaming surface. Everything above is a request you make; this is a connection you hold, and it is priced by the minute rather than by the call — which is why it sits on its own rather than under the feed it carries. It delivers the same thesis objects `Thesis Feed` returns, sooner."},{"name":"Pump","description":"**Experimental** — this surface is new and may change or be withdrawn without the usual notice; do not build anything load-bearing on it yet. pump.fun mirror: the same lookups over our own pump.fun scrape — a user by wallet, the global callout feed or one coin's or wallet's wall, and the PnL leaderboard. On pump.fun the wallet IS the account, so a wallet lookup here is a cheap stored read, not a resolution."},{"name":"Account","description":"Your key: entitlements and usage."}],"paths":{"/v2/user/handle/{handle}":{"get":{"summary":"Resolve a FOMO handle to the user and their verified wallet","tags":["Identities"],"description":"Looks a user up by their fomo.family handle.\n\n- **404** when we do not know the handle.\n- A **200** with both addresses null means we know the user but\n  hold no verified wallet — never that they have none.\n- **2,500 CU** on a hit, **250 CU** on a 404. Charged every call.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64},"example":"frankdegods","in":"path","name":"handle","required":true,"description":"fomo.family username (case-insensitive; leading @ is stripped)."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["id","handle","name","bio","banner","profilePicture","twitter","solanaAddress","evmAddress"],"properties":{"id":{"type":"string","format":"uuid","description":"Stable FOMO user id — the key to cache and join on, and the one `/v2/user/id/{id}` takes. Unlike `handle` it never changes, and it is the same id FOMO itself uses, so it joins directly against anything you already hold from them."},"handle":{"type":"string","description":"The canonical handle. Users rename — do not key on it."},"name":{"type":["null","string"],"description":"Display name."},"bio":{"type":["null","string"],"description":"Profile blurb as written by the user, or null if they have none."},"banner":{"type":["null","string"],"description":"Cover photo URL, or null. Not guaranteed to still resolve."},"profilePicture":{"type":["null","string"],"description":"Avatar URL, or null. Not guaranteed to still resolve."},"twitter":{"type":["null","string"],"description":"Canonical X profile link, `https://x.com/<handle>`, or null. Normalised from whatever the user typed on FOMO; null when that could not be reduced to a plausible handle. Self-declared and NOT verified by us — a claim, not a proof.","example":"https://x.com/frankdegods"},"solanaAddress":{"type":["null","string"],"description":"Verified Solana address (base58), or null."},"evmAddress":{"type":["null","string"],"description":"Verified EVM address, `0x`-prefixed, or null."},"socials":{"type":"array","description":"Verified social account links. Present only when SOCIAL_LINKS_ENABLED is set on the API server. Absent (not null) when disabled — existing integrations are unaffected.","items":{"type":"object","required":["provider","subject","handle"],"properties":{"provider":{"type":"string","description":"Platform identifier: 'x', 'telegram', 'youtube', 'reddit', etc."},"subject":{"type":"string","description":"Provider-stable user id (e.g. Twitter numeric id '12345678'). Stable across renames; use this as your join key."},"handle":{"type":["null","string"],"description":"Display username at verification time. May change; do not key on it."}}}}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v2/user/wallet/{address}":{"get":{"summary":"Resolve a wallet address to the user(s) behind it","tags":["Identities"],"description":"The same record as `/v2/user/handle/{handle}`.\n\n- **EVM addresses must be `0x`-prefixed.** The chain is inferred\n  from the address.\n- **404** when we hold no user for the address.\n- If the address is shared by several users, the longest-standing\n  verified link wins.\n- **50,000 CU** on a hit — this resolution is what you are buying —\n  and **250 CU** on a 404. Charged every call.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"example":"498g1rVnFcnjBjpfw1xyqA1WvgQXUU8RWuELjxkjAayQ","in":"path","name":"address","required":true,"description":"Solana address (base58) or EVM address (**must** include the `0x` prefix)."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["id","handle","name","bio","banner","profilePicture","twitter","solanaAddress","evmAddress"],"properties":{"id":{"type":"string","format":"uuid","description":"Stable FOMO user id — the key to cache and join on, and the one `/v2/user/id/{id}` takes. Unlike `handle` it never changes, and it is the same id FOMO itself uses, so it joins directly against anything you already hold from them."},"handle":{"type":"string","description":"The canonical handle. Users rename — do not key on it."},"name":{"type":["null","string"],"description":"Display name."},"bio":{"type":["null","string"],"description":"Profile blurb as written by the user, or null if they have none."},"banner":{"type":["null","string"],"description":"Cover photo URL, or null. Not guaranteed to still resolve."},"profilePicture":{"type":["null","string"],"description":"Avatar URL, or null. Not guaranteed to still resolve."},"twitter":{"type":["null","string"],"description":"Canonical X profile link, `https://x.com/<handle>`, or null. Normalised from whatever the user typed on FOMO; null when that could not be reduced to a plausible handle. Self-declared and NOT verified by us — a claim, not a proof.","example":"https://x.com/frankdegods"},"solanaAddress":{"type":["null","string"],"description":"Verified Solana address (base58), or null."},"evmAddress":{"type":["null","string"],"description":"Verified EVM address, `0x`-prefixed, or null."},"socials":{"type":"array","description":"Verified social account links. Present only when SOCIAL_LINKS_ENABLED is set on the API server. Absent (not null) when disabled — existing integrations are unaffected.","items":{"type":"object","required":["provider","subject","handle"],"properties":{"provider":{"type":"string","description":"Platform identifier: 'x', 'telegram', 'youtube', 'reddit', etc."},"subject":{"type":"string","description":"Provider-stable user id (e.g. Twitter numeric id '12345678'). Stable across renames; use this as your join key."},"handle":{"type":["null","string"],"description":"Display username at verification time. May change; do not key on it."}}}}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v2/user/id/{id}":{"get":{"summary":"Resolve a FOMO user id to the user and their verified wallet","tags":["Identities"],"description":"Looks a user up by the `id` from a previous response. `id` is stable\nwhere `handle` is not, so it is the value to store against your own\nrecords and come back with.\n\n- **404** when we do not know the id.\n- A **200** with both addresses null means we know the user but hold\n  no verified wallet — never that they have none.\n- **2,500 CU** on a hit, **250 CU** on a 404. Charged every call.","parameters":[{"schema":{"type":"string","format":"uuid"},"example":"193fb089-854e-5152-94f5-f55dd86e2900","in":"path","name":"id","required":true,"description":"FOMO user id, as returned in `id` on any user record. Same id FOMO uses."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["id","handle","name","bio","banner","profilePicture","twitter","solanaAddress","evmAddress"],"properties":{"id":{"type":"string","format":"uuid","description":"Stable FOMO user id — the key to cache and join on, and the one `/v2/user/id/{id}` takes. Unlike `handle` it never changes, and it is the same id FOMO itself uses, so it joins directly against anything you already hold from them."},"handle":{"type":"string","description":"The canonical handle. Users rename — do not key on it."},"name":{"type":["null","string"],"description":"Display name."},"bio":{"type":["null","string"],"description":"Profile blurb as written by the user, or null if they have none."},"banner":{"type":["null","string"],"description":"Cover photo URL, or null. Not guaranteed to still resolve."},"profilePicture":{"type":["null","string"],"description":"Avatar URL, or null. Not guaranteed to still resolve."},"twitter":{"type":["null","string"],"description":"Canonical X profile link, `https://x.com/<handle>`, or null. Normalised from whatever the user typed on FOMO; null when that could not be reduced to a plausible handle. Self-declared and NOT verified by us — a claim, not a proof.","example":"https://x.com/frankdegods"},"solanaAddress":{"type":["null","string"],"description":"Verified Solana address (base58), or null."},"evmAddress":{"type":["null","string"],"description":"Verified EVM address, `0x`-prefixed, or null."},"socials":{"type":"array","description":"Verified social account links. Present only when SOCIAL_LINKS_ENABLED is set on the API server. Absent (not null) when disabled — existing integrations are unaffected.","items":{"type":"object","required":["provider","subject","handle"],"properties":{"provider":{"type":"string","description":"Platform identifier: 'x', 'telegram', 'youtube', 'reddit', etc."},"subject":{"type":"string","description":"Provider-stable user id (e.g. Twitter numeric id '12345678'). Stable across renames; use this as your join key."},"handle":{"type":["null","string"],"description":"Display username at verification time. May change; do not key on it."}}}}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v2/user/handle/{handle}/resolve":{"post":{"summary":"Resolve a handle to its wallets, live","tags":["Identities"],"description":"Returns the wallet we hold for this handle, and if we hold none, sends the crawler fleet to\nfomo.family to go and prove one — waiting for it.\n\n- **Answers from our tables when we already have a verified wallet**, without touching the\n  fleet. `x-fomoscan-resolve: cached` on that response, `live` when we actually went out.\n- Typically answers in **4-7s**. Held for at most 15s.\n- **200** returns the ordinary user record, re-read from our tables after the proof lands.\n- **202** `{ pending: true, reason }` means accepted-but-not-yet, never failed. Usually\n  `reason: \"queued\"` with a `position` and `retryAfterSeconds`: the fleet works its line\n  at a fixed rate. Asking again does not start a second resolve — it joins the same one.\n- **404** when fomo.family has no such user.\n- **202** with `reason: \"rate_limited\"` when every browser has spent its budget for the\n  minute — the fleet is capped per logged-in account, independently of your key.\n- **100,000 CU on every call** — a 200, a 202 and a 404 alike. There is no miss price:\n  you are paying for the fleet to go and look, which it did.\n- Rate limited to **10/minute** per IP — an anti-abuse backstop, not your plan rate:\n  each call spends a live action on a real account, and the fleet can start only ~2 a minute.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64},"example":"frankdegods","in":"path","name":"handle","required":true,"description":"fomo.family username (case-insensitive; leading @ is stripped)."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["id","handle","name","bio","banner","profilePicture","twitter","solanaAddress","evmAddress"],"properties":{"id":{"type":"string","format":"uuid","description":"Stable FOMO user id — the key to cache and join on, and the one `/v2/user/id/{id}` takes. Unlike `handle` it never changes, and it is the same id FOMO itself uses, so it joins directly against anything you already hold from them."},"handle":{"type":"string","description":"The canonical handle. Users rename — do not key on it."},"name":{"type":["null","string"],"description":"Display name."},"bio":{"type":["null","string"],"description":"Profile blurb as written by the user, or null if they have none."},"banner":{"type":["null","string"],"description":"Cover photo URL, or null. Not guaranteed to still resolve."},"profilePicture":{"type":["null","string"],"description":"Avatar URL, or null. Not guaranteed to still resolve."},"twitter":{"type":["null","string"],"description":"Canonical X profile link, `https://x.com/<handle>`, or null. Normalised from whatever the user typed on FOMO; null when that could not be reduced to a plausible handle. Self-declared and NOT verified by us — a claim, not a proof.","example":"https://x.com/frankdegods"},"solanaAddress":{"type":["null","string"],"description":"Verified Solana address (base58), or null."},"evmAddress":{"type":["null","string"],"description":"Verified EVM address, `0x`-prefixed, or null."},"socials":{"type":"array","description":"Verified social account links. Present only when SOCIAL_LINKS_ENABLED is set on the API server. Absent (not null) when disabled — existing integrations are unaffected.","items":{"type":"object","required":["provider","subject","handle"],"properties":{"provider":{"type":"string","description":"Platform identifier: 'x', 'telegram', 'youtube', 'reddit', etc."},"subject":{"type":"string","description":"Provider-stable user id (e.g. Twitter numeric id '12345678'). Stable across renames; use this as your join key."},"handle":{"type":["null","string"],"description":"Display username at verification time. May change; do not key on it."}}}}}}}}},"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["pending","reason"],"properties":{"pending":{"type":"boolean","enum":[true]},"reason":{"type":"string","enum":["queued","timeout","unavailable","rate_limited","no_preview","not_ingested"],"description":"`queued` — accepted and waiting for a browser. `position` and `retryAfterSeconds` say where and when.\n`timeout` — the fleet did not answer in time; the resolve is still running, so ask again.\n`unavailable` — the fleet is down or not configured; nothing was attempted.\n`rate_limited` — the fleet’s queue is full. Unlike `queued`, this one was NOT accepted.\n`no_preview` — FOMO would not issue a transfer preview for this user. Retrying rarely helps.\n`not_ingested` — the fleet resolved the user but the write has not landed. Ask again shortly."},"retryable":{"type":"boolean","description":"False only for `no_preview`, where a prompt retry is wasted effort."},"retryAfterSeconds":{"type":"integer","description":"When the fleet expects to have your answer. Present on `queued` and `rate_limited`."},"position":{"type":"integer","description":"On `queued` only: places ahead of you in the fleet’s line. 1 is next up."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"429":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v2/user/handle/{handle}/pnl":{"get":{"summary":"One trader's realized PnL, measured on chain","tags":["Identities"],"description":"Realized profit and loss for one trader, from the same daily on-chain\nsweep behind `GET /v2/leaderboard/traders-fomoscan` — measured across\nthe trader's entire on-chain activity, not only their trades on FOMO.\n\n- All three windows (`24h`, `7d`, `30d`) in one call, each with the\n  trader's `rank` out of `walletsRanked`.\n- **404** when we do not know the handle.\n- A **200** with `wallet: null` means we hold no verified wallet.\n- A **200** with a wallet but every window null means that wallet is\n  outside the swept population — never that the trader made no trades.\n- `capturedAt` is a daily sweep, so expect it to be many hours old.\n- **2,500 CU**, charged on every call including a 404.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64},"example":"frankdegods","in":"path","name":"handle","required":true,"description":"fomo.family username (case-insensitive; leading @ is stripped)."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["handle","wallet","windows"],"properties":{"handle":{"type":"string"},"displayName":{"type":["null","string"]},"avatarUrl":{"type":["null","string"]},"twitter":{"type":["null","string"],"description":"Bare X username."},"followers":{"type":["null","integer"]},"wallet":{"type":["null","string"],"description":"The proven Solana address these numbers describe. Null means we hold no verified wallet for this user — never that they have none."},"windows":{"type":"object","description":"One entry per window, always all three keys. A null value means we hold no numbers for that window — for a live read, a wallet the provider has never seen; for a sweep read, a wallet outside the swept population. Never a claim that the trader did not trade.","properties":{"24h":{"type":["null","object"],"properties":{"pnlUsd":{"type":["null","number"],"description":"Realized profit for the window, USD."},"pnlUsdExNative":{"type":["null","number"],"description":"Realized profit EXCLUDING the wallet's trades in SOL itself — token picking in isolation. Can carry the opposite sign to pnlUsd."},"returnPct":{"type":["null","number"],"description":"Realized profit as a percentage."},"returnPctExNative":{"type":["null","number"]},"avgProfitPerTradeUsd":{"type":["null","number"]},"volumeUsd":{"type":["null","number"]},"avgTradeSizeUsd":{"type":["null","number"]},"trades":{"type":["null","integer"],"description":"Swaps in the window."},"uniqueTokens":{"type":["null","integer"]},"winRate":{"type":["null","number"],"description":"0-1. Codex reports 0-100; it is normalised here so one surface owns the scale."},"avgHoldSeconds":{"type":["null","number"],"description":"Null for roughly half of wallets, high-volume ones included."},"tokensCreated":{"type":["null","integer"],"description":"A wallet that launches its own tokens is a launcher, not a trader."},"tokensMigrated":{"type":["null","integer"]},"walletFirstSeenAt":{"type":["null","integer"],"description":"Epoch ms of first on-chain tx."},"walletLastSeenAt":{"type":["null","integer"]}}},"7d":{"type":["null","object"],"properties":{"pnlUsd":{"type":["null","number"],"description":"Realized profit for the window, USD."},"pnlUsdExNative":{"type":["null","number"],"description":"Realized profit EXCLUDING the wallet's trades in SOL itself — token picking in isolation. Can carry the opposite sign to pnlUsd."},"returnPct":{"type":["null","number"],"description":"Realized profit as a percentage."},"returnPctExNative":{"type":["null","number"]},"avgProfitPerTradeUsd":{"type":["null","number"]},"volumeUsd":{"type":["null","number"]},"avgTradeSizeUsd":{"type":["null","number"]},"trades":{"type":["null","integer"],"description":"Swaps in the window."},"uniqueTokens":{"type":["null","integer"]},"winRate":{"type":["null","number"],"description":"0-1. Codex reports 0-100; it is normalised here so one surface owns the scale."},"avgHoldSeconds":{"type":["null","number"],"description":"Null for roughly half of wallets, high-volume ones included."},"tokensCreated":{"type":["null","integer"],"description":"A wallet that launches its own tokens is a launcher, not a trader."},"tokensMigrated":{"type":["null","integer"]},"walletFirstSeenAt":{"type":["null","integer"],"description":"Epoch ms of first on-chain tx."},"walletLastSeenAt":{"type":["null","integer"]}}},"30d":{"type":["null","object"],"properties":{"pnlUsd":{"type":["null","number"],"description":"Realized profit for the window, USD."},"pnlUsdExNative":{"type":["null","number"],"description":"Realized profit EXCLUDING the wallet's trades in SOL itself — token picking in isolation. Can carry the opposite sign to pnlUsd."},"returnPct":{"type":["null","number"],"description":"Realized profit as a percentage."},"returnPctExNative":{"type":["null","number"]},"avgProfitPerTradeUsd":{"type":["null","number"]},"volumeUsd":{"type":["null","number"]},"avgTradeSizeUsd":{"type":["null","number"]},"trades":{"type":["null","integer"],"description":"Swaps in the window."},"uniqueTokens":{"type":["null","integer"]},"winRate":{"type":["null","number"],"description":"0-1. Codex reports 0-100; it is normalised here so one surface owns the scale."},"avgHoldSeconds":{"type":["null","number"],"description":"Null for roughly half of wallets, high-volume ones included."},"tokensCreated":{"type":["null","integer"],"description":"A wallet that launches its own tokens is a launcher, not a trader."},"tokensMigrated":{"type":["null","integer"]},"walletFirstSeenAt":{"type":["null","integer"],"description":"Epoch ms of first on-chain tx."},"walletLastSeenAt":{"type":["null","integer"]}}}}}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v2/thesis":{"get":{"summary":"Every thesis, newest first — the unfiltered feed","tags":["Thesis Feed"],"description":"Every thesis on the platform, newest first, 20 to a page — no token filter,\nno author filter.\n\n- **Live:** poll with no `before` and dedupe on each item’s `id`.\n- **Backfill:** feed `nextBefore` back as `before` until `hasMore` is false.\n\nEach item carries `authorId`; pass it to `/v2/user/id/{id}` for the profile\nor `/v2/thesis/user/{id}` for everything else that author has posted.\n\nNarrow before you poll if you can: `/v2/thesis/token/{tokenAddress}` and\n`/v2/thesis/user/{id}` answer the same question about one subject and cost\nthe same 250 CU, so polling this one and filtering client-side buys nothing.\n\n- **250 CU** per call, hit or miss.","parameters":[{"schema":{"type":"string"},"in":"query","name":"before","required":false,"description":"The `id` of the last thesis you saw — the previous page’s `nextBefore`. Omit for the newest 20."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["count","hasMore","nextBefore","updatedAt","items"],"properties":{"count":{"type":"integer","description":"Items in this page (0..20)."},"hasMore":{"type":"boolean","description":"Whether older theses exist behind this page."},"nextBefore":{"type":["null","string"],"description":"The OLDEST item’s `id`. Send back as `?before=` to continue. Null on the last page."},"updatedAt":{"type":["null","integer"],"description":"Post time of the NEWEST thesis in this page, epoch millis."},"items":{"type":"array","items":{"type":"object","required":["id","authorHandle","thesis","fomoCreatedAt"],"properties":{"id":{"type":"string","description":"Thesis id. Dedupe on this, and send it back as `?before=` to paginate. Not related to a user `id`."},"tokenAddress":{"type":["null","string"],"description":"The token this thesis is about. Present on every item, including the narrowed feeds, so one item shape works everywhere."},"tokenNetwork":{"type":["null","string"],"description":"Chain name (`sol`, `eth`, `base`, …)."},"tokenSymbol":{"type":["null","string"],"description":"Ticker."},"authorId":{"type":["null","string"],"description":"The author’s `id` — the same value a user record carries. Pass it straight to `/v2/user/id/{id}` for their profile, or `/v2/thesis/user/{id}` for everything else they have posted."},"authorHandle":{"type":["null","string"],"description":"Author’s handle. Display it; to look the author up prefer `authorId`, which does not change when they rename."},"authorName":{"type":["null","string"],"description":"Author display name."},"authorIsDev":{"type":["null","boolean"],"description":"Author is the token’s dev."},"thesis":{"type":["null","string"],"description":"The thesis text."},"segments":{"type":["null","array"],"description":"Rich-text runs of the thesis text (text + optional link).","items":{"type":"object","additionalProperties":true}},"likeCount":{"type":["null","integer"]},"holdingsUsd":{"type":["null","number"],"description":"Author’s position value in USD at post time."},"authorTradeUsd":{"type":["null","number"],"description":"USD size of the trade behind the thesis."},"pnl":{"type":["null","number"],"description":"Legacy single figure: realized, falling back to unrealized."},"realizedPnlUsd":{"type":["null","number"],"description":"Realized PnL in USD."},"unrealizedPnlUsd":{"type":["null","number"],"description":"Unrealized PnL in USD."},"percentageRealizedPnl":{"type":["null","number"],"description":"Realized PnL as a percentage."},"percentageUnrealizedPnl":{"type":["null","number"],"description":"Unrealized PnL as a percentage."},"tokenAmount":{"type":["null","number"],"description":"Author’s token balance behind the thesis."},"closedAt":{"type":["null","integer"],"description":"When the author’s position closed, epoch millis. Null while open — the PnL above is then a running figure, not a final one."},"fomoCreatedAt":{"type":["null","integer"],"description":"Post time, epoch milliseconds."}}}}}}}}}}}},"/v2/thesis/token/{tokenAddress}":{"get":{"summary":"Theses for a token, newest first — poll this for a live feed","tags":["Thesis Feed"],"description":"The 25 most recent theses for one token, newest first by post time.\n\n- **Live:** call with no `before` on a timer; dedupe on `id`.\n- **Backfill:** feed `nextBefore` back as `?before=` until `hasMore` is false.\n- `before` is a thesis **`id`** — the last thesis you saw — not a timestamp.\n  An id positions the walk exactly, including inside a group of theses\n  posted in the same millisecond, which a timestamp cannot.\n- An unknown `before` is a **400**: we cannot resume from a thesis that no\n  longer exists, and restarting at the top would loop a backfill forever.\n- **250 CU** a page, and **250 CU** for an empty one. Charged every call, so\n  poll on a sensible timer rather than in a tight loop.\n\nIf we hold nothing for the token yet, the first call fetches it from FOMO\nbefore answering, so you normally get real theses straight away. If that\nfetch cannot finish in time the page comes back empty and the harvest\ncontinues in the background — ask again on your normal polling interval and\nit will be there. An empty page is charged the same 250 CU as a full one.","parameters":[{"schema":{"type":"string"},"in":"query","name":"before","required":false,"description":"The `id` of the last thesis you saw — the previous page’s `nextBefore`. Returns the ones after it. Omit for the newest 25."},{"schema":{"type":"string"},"example":"Ai66LHZG9MCzg1WKdawwqduVAXpNDUuV8M3uyq5ppump","in":"path","name":"tokenAddress","required":true,"description":"Mint / contract address. Case-sensitive for Solana base58; EVM `0x…` is lower-cased for you."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["tokenAddress","tokenNetwork","symbol","updatedAt","count","hasMore","nextBefore","items"],"properties":{"tokenAddress":{"type":"string","description":"Normalised (EVM lower-cased)."},"tokenNetwork":{"type":["null","string"],"description":"Chain name (`sol`, `eth`, `base`, …). Null when the page is empty."},"symbol":{"type":["null","string"],"description":"Ticker. Null when the page is empty."},"updatedAt":{"type":["null","integer"],"description":"Post time of the NEWEST thesis in this page, epoch millis."},"count":{"type":"integer","description":"Items in this page (0..25)."},"hasMore":{"type":"boolean","description":"Whether older theses exist behind this page."},"nextBefore":{"type":["null","string"],"description":"The OLDEST item’s `id`. Send back as `?before=` to continue. Null on the last page."},"items":{"type":"array","items":{"type":"object","required":["id","authorHandle","thesis","fomoCreatedAt"],"properties":{"id":{"type":"string","description":"Thesis id. Dedupe on this, and send it back as `?before=` to paginate. Not related to a user `id`."},"tokenAddress":{"type":["null","string"],"description":"The token this thesis is about. Present on every item, including the narrowed feeds, so one item shape works everywhere."},"tokenNetwork":{"type":["null","string"],"description":"Chain name (`sol`, `eth`, `base`, …)."},"tokenSymbol":{"type":["null","string"],"description":"Ticker."},"authorId":{"type":["null","string"],"description":"The author’s `id` — the same value a user record carries. Pass it straight to `/v2/user/id/{id}` for their profile, or `/v2/thesis/user/{id}` for everything else they have posted."},"authorHandle":{"type":["null","string"],"description":"Author’s handle. Display it; to look the author up prefer `authorId`, which does not change when they rename."},"authorName":{"type":["null","string"],"description":"Author display name."},"authorIsDev":{"type":["null","boolean"],"description":"Author is the token’s dev."},"thesis":{"type":["null","string"],"description":"The thesis text."},"segments":{"type":["null","array"],"description":"Rich-text runs of the thesis text (text + optional link).","items":{"type":"object","additionalProperties":true}},"likeCount":{"type":["null","integer"]},"holdingsUsd":{"type":["null","number"],"description":"Author’s position value in USD at post time."},"authorTradeUsd":{"type":["null","number"],"description":"USD size of the trade behind the thesis."},"pnl":{"type":["null","number"],"description":"Legacy single figure: realized, falling back to unrealized."},"realizedPnlUsd":{"type":["null","number"],"description":"Realized PnL in USD."},"unrealizedPnlUsd":{"type":["null","number"],"description":"Unrealized PnL in USD."},"percentageRealizedPnl":{"type":["null","number"],"description":"Realized PnL as a percentage."},"percentageUnrealizedPnl":{"type":["null","number"],"description":"Unrealized PnL as a percentage."},"tokenAmount":{"type":["null","number"],"description":"Author’s token balance behind the thesis."},"closedAt":{"type":["null","integer"],"description":"When the author’s position closed, epoch millis. Null while open — the PnL above is then a running figure, not a final one."},"fomoCreatedAt":{"type":["null","integer"],"description":"Post time, epoch milliseconds."}}}}}}}}}}}},"/v2/thesis/user/{id}":{"get":{"summary":"Theses by a user, newest first — poll this for their feed","tags":["Thesis Feed"],"description":"Every thesis this user has posted, newest first, 20 to a page.\n\nThe `id` is the one on any user record — resolve a handle with\n`/v2/user/handle/{handle}` and pass the `id` straight through.\n\n- **Live**: poll with no `before` and dedupe on each item’s `id`.\n- **Backfill**: feed `nextBefore` back as `before` until `hasMore` is false.\n\nAn empty page means the user has posted nothing we hold — unlike the token\nwall there is no on-demand fetch, so there is no `pending` state to wait on.\n\n- **250 CU** per call, hit or miss.","parameters":[{"schema":{"type":"string"},"in":"query","name":"before","required":false,"description":"The `id` of the last thesis you saw — the previous page’s `nextBefore`. Omit for the newest 20."},{"schema":{"type":"string","format":"uuid"},"example":"193fb089-854e-5152-94f5-f55dd86e2900","in":"path","name":"id","required":true,"description":"The author’s `id`, as carried on any user record."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["userId","tokenAddress","count","hasMore","nextBefore","updatedAt","items"],"properties":{"userId":{"type":"string","description":"The author, echoed back — the same `id` a user record carries."},"tokenAddress":{"type":["null","string"],"description":"The token this page was narrowed to, normalised. Null on the unfiltered feed."},"count":{"type":"integer","description":"Items in this page (0..20)."},"hasMore":{"type":"boolean","description":"Whether older theses exist behind this page."},"nextBefore":{"type":["null","string"],"description":"The OLDEST item’s `id`. Send back as `?before=` to continue. Null on the last page."},"updatedAt":{"type":["null","integer"],"description":"Post time of the NEWEST thesis in this page, epoch millis."},"items":{"type":"array","items":{"type":"object","required":["id","authorHandle","thesis","fomoCreatedAt"],"properties":{"id":{"type":"string","description":"Thesis id. Dedupe on this, and send it back as `?before=` to paginate. Not related to a user `id`."},"tokenAddress":{"type":["null","string"],"description":"The token this thesis is about. Present on every item, including the narrowed feeds, so one item shape works everywhere."},"tokenNetwork":{"type":["null","string"],"description":"Chain name (`sol`, `eth`, `base`, …)."},"tokenSymbol":{"type":["null","string"],"description":"Ticker."},"authorId":{"type":["null","string"],"description":"The author’s `id` — the same value a user record carries. Pass it straight to `/v2/user/id/{id}` for their profile, or `/v2/thesis/user/{id}` for everything else they have posted."},"authorHandle":{"type":["null","string"],"description":"Author’s handle. Display it; to look the author up prefer `authorId`, which does not change when they rename."},"authorName":{"type":["null","string"],"description":"Author display name."},"authorIsDev":{"type":["null","boolean"],"description":"Author is the token’s dev."},"thesis":{"type":["null","string"],"description":"The thesis text."},"segments":{"type":["null","array"],"description":"Rich-text runs of the thesis text (text + optional link).","items":{"type":"object","additionalProperties":true}},"likeCount":{"type":["null","integer"]},"holdingsUsd":{"type":["null","number"],"description":"Author’s position value in USD at post time."},"authorTradeUsd":{"type":["null","number"],"description":"USD size of the trade behind the thesis."},"pnl":{"type":["null","number"],"description":"Legacy single figure: realized, falling back to unrealized."},"realizedPnlUsd":{"type":["null","number"],"description":"Realized PnL in USD."},"unrealizedPnlUsd":{"type":["null","number"],"description":"Unrealized PnL in USD."},"percentageRealizedPnl":{"type":["null","number"],"description":"Realized PnL as a percentage."},"percentageUnrealizedPnl":{"type":["null","number"],"description":"Unrealized PnL as a percentage."},"tokenAmount":{"type":["null","number"],"description":"Author’s token balance behind the thesis."},"closedAt":{"type":["null","integer"],"description":"When the author’s position closed, epoch millis. Null while open — the PnL above is then a running figure, not a final one."},"fomoCreatedAt":{"type":["null","integer"],"description":"Post time, epoch milliseconds."}}}}}}}}}}}},"/v2/thesis/user/{id}/token/{tokenAddress}":{"get":{"summary":"Theses by a user about one token","tags":["Thesis Feed"],"description":"Everything one user has posted about one token, newest first.\n\nThe intersection of the two walls above. Same page size, same `?before=`\ncursor, same item shape — only the filter is narrower.\n\nAn empty page means this user has said nothing we hold about this token. It\ndoes NOT mean the token is unknown to us: ask `/v2/thesis/token/{tokenAddress}`\nfor that.\n\n- **250 CU** per call, hit or miss — the same as the unfiltered feed.","parameters":[{"schema":{"type":"string"},"in":"query","name":"before","required":false,"description":"The `id` of the last thesis you saw — the previous page’s `nextBefore`. Omit for the newest 20."},{"schema":{"type":"string","format":"uuid"},"example":"193fb089-854e-5152-94f5-f55dd86e2900","in":"path","name":"id","required":true,"description":"The author’s `id`, as carried on any user record."},{"schema":{"type":"string"},"example":"9cRCn9rGT8V2imeM2BaKs13yhMEais3ruM3rPvTGpump","in":"path","name":"tokenAddress","required":true,"description":"Mint / contract address. Case-sensitive for Solana base58; EVM `0x…` is lower-cased for you."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["userId","tokenAddress","count","hasMore","nextBefore","updatedAt","items"],"properties":{"userId":{"type":"string","description":"The author, echoed back — the same `id` a user record carries."},"tokenAddress":{"type":["null","string"],"description":"The token this page was narrowed to, normalised. Null on the unfiltered feed."},"count":{"type":"integer","description":"Items in this page (0..20)."},"hasMore":{"type":"boolean","description":"Whether older theses exist behind this page."},"nextBefore":{"type":["null","string"],"description":"The OLDEST item’s `id`. Send back as `?before=` to continue. Null on the last page."},"updatedAt":{"type":["null","integer"],"description":"Post time of the NEWEST thesis in this page, epoch millis."},"items":{"type":"array","items":{"type":"object","required":["id","authorHandle","thesis","fomoCreatedAt"],"properties":{"id":{"type":"string","description":"Thesis id. Dedupe on this, and send it back as `?before=` to paginate. Not related to a user `id`."},"tokenAddress":{"type":["null","string"],"description":"The token this thesis is about. Present on every item, including the narrowed feeds, so one item shape works everywhere."},"tokenNetwork":{"type":["null","string"],"description":"Chain name (`sol`, `eth`, `base`, …)."},"tokenSymbol":{"type":["null","string"],"description":"Ticker."},"authorId":{"type":["null","string"],"description":"The author’s `id` — the same value a user record carries. Pass it straight to `/v2/user/id/{id}` for their profile, or `/v2/thesis/user/{id}` for everything else they have posted."},"authorHandle":{"type":["null","string"],"description":"Author’s handle. Display it; to look the author up prefer `authorId`, which does not change when they rename."},"authorName":{"type":["null","string"],"description":"Author display name."},"authorIsDev":{"type":["null","boolean"],"description":"Author is the token’s dev."},"thesis":{"type":["null","string"],"description":"The thesis text."},"segments":{"type":["null","array"],"description":"Rich-text runs of the thesis text (text + optional link).","items":{"type":"object","additionalProperties":true}},"likeCount":{"type":["null","integer"]},"holdingsUsd":{"type":["null","number"],"description":"Author’s position value in USD at post time."},"authorTradeUsd":{"type":["null","number"],"description":"USD size of the trade behind the thesis."},"pnl":{"type":["null","number"],"description":"Legacy single figure: realized, falling back to unrealized."},"realizedPnlUsd":{"type":["null","number"],"description":"Realized PnL in USD."},"unrealizedPnlUsd":{"type":["null","number"],"description":"Unrealized PnL in USD."},"percentageRealizedPnl":{"type":["null","number"],"description":"Realized PnL as a percentage."},"percentageUnrealizedPnl":{"type":["null","number"],"description":"Unrealized PnL as a percentage."},"tokenAmount":{"type":["null","number"],"description":"Author’s token balance behind the thesis."},"closedAt":{"type":["null","integer"],"description":"When the author’s position closed, epoch millis. Null while open — the PnL above is then a running figure, not a final one."},"fomoCreatedAt":{"type":["null","integer"],"description":"Post time, epoch milliseconds."}}}}}}}}}}}},"/v2/leaderboard/traders":{"get":{"summary":"FOMO's top traders","tags":["Leaderboards"],"description":"FOMO's top traders, ranked by realised PnL over the window.\n\nWindows: `24h`, `7d`, `30d`, `all` (default `24h`).\n\n`capturedAt` is when the ranking was read from FOMO, and a snapshot is one\ninstant. Boards are sampled every 30 seconds, so a `capturedAt` much older\nthan a minute means the sampler is behind rather than that the board is\nquiet.\n\nPass `?at=` to read the board as it stood at an instant — the most recent\nsnapshot taken at or before it.\n\nBoards are at most 100 entries — FOMO caps them there — so there is no\npagination.\n\n- **250 CU** per call, hit or miss.","parameters":[{"schema":{"type":"string","enum":["24h","7d","30d","all"]},"in":"query","name":"window","required":false,"description":"Ranking window. Defaults to `24h`."},{"schema":{"type":"integer","minimum":0},"in":"query","name":"at","required":false,"description":"Epoch milliseconds, e.g. `1755765600000` — the same unit as `capturedAt` in the response. Returns the ranking current at that moment: the most recent snapshot taken at or before it. Omit for the newest."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["board","window","capturedAt","count","entries"],"properties":{"board":{"type":"string","enum":["traders","clans","tokens/most-held","tokens/trending","tokens/graduated"]},"window":{"type":["null","string"],"description":"Null for boards with no window."},"capturedAt":{"type":"integer","description":"When this ranking was read from FOMO, epoch millis. Boards are sampled every 30s, so anything much older than a minute means the sampler is behind."},"count":{"type":"integer"},"entries":{"type":"array","items":{"type":"object","required":["rank","id"],"properties":{"rank":{"type":"integer","description":"Position on the board, 1 = top."},"id":{"type":"string","description":"The subject's FOMO id: a trader uuid, a clan id, or a token address."},"handle":{"type":["null","string"],"description":"Trader @-handle, clan name, or token symbol."},"label":{"type":["null","string"],"description":"Display name, clan name, or token name."},"avatarUrl":{"type":["null","string"]},"pnl":{"type":["null","number"],"description":"The measure the board is sorted by, in USD. Named per window by FOMO (pnl24h / pnl7d / pnl30d / totalPnL) and normalised here."},"volume":{"type":["null","number"]},"followers":{"type":["null","integer"],"description":"Traders only."},"numTrades":{"type":["null","integer"],"description":"Traders only."},"memberCount":{"type":["null","integer"],"description":"Clans only."},"marketCap":{"type":["null","number"],"description":"Tokens only."},"price":{"type":["null","number"],"description":"Tokens only."},"liquidity":{"type":["null","number"],"description":"Tokens only."}}}}}}}}}}}},"/v2/leaderboard/clans":{"get":{"summary":"FOMO's top clans","tags":["Leaderboards"],"description":"FOMO's top clans, ranked by their members' combined PnL over the window.\n\nWindows: `24h`, `7d`, `30d` (default `24h`). There is no `all` window.\n\n`capturedAt` is when the ranking was read from FOMO, and a snapshot is one\ninstant. Boards are sampled every 30 seconds, so a `capturedAt` much older\nthan a minute means the sampler is behind rather than that the board is\nquiet.\n\nPass `?at=` to read the board as it stood at an instant — the most recent\nsnapshot taken at or before it.\n\nBoards are at most 100 entries — FOMO caps them there — so there is no\npagination.\n\n- **250 CU** per call, hit or miss.","parameters":[{"schema":{"type":"string","enum":["24h","7d","30d"]},"in":"query","name":"window","required":false,"description":"Ranking window. Defaults to `24h`. Clans have no `all` window."},{"schema":{"type":"integer","minimum":0},"in":"query","name":"at","required":false,"description":"Epoch milliseconds, e.g. `1755765600000` — the same unit as `capturedAt` in the response. Returns the ranking current at that moment: the most recent snapshot taken at or before it. Omit for the newest."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["board","window","capturedAt","count","entries"],"properties":{"board":{"type":"string","enum":["traders","clans","tokens/most-held","tokens/trending","tokens/graduated"]},"window":{"type":["null","string"],"description":"Null for boards with no window."},"capturedAt":{"type":"integer","description":"When this ranking was read from FOMO, epoch millis. Boards are sampled every 30s, so anything much older than a minute means the sampler is behind."},"count":{"type":"integer"},"entries":{"type":"array","items":{"type":"object","required":["rank","id"],"properties":{"rank":{"type":"integer","description":"Position on the board, 1 = top."},"id":{"type":"string","description":"The subject's FOMO id: a trader uuid, a clan id, or a token address."},"handle":{"type":["null","string"],"description":"Trader @-handle, clan name, or token symbol."},"label":{"type":["null","string"],"description":"Display name, clan name, or token name."},"avatarUrl":{"type":["null","string"]},"pnl":{"type":["null","number"],"description":"The measure the board is sorted by, in USD. Named per window by FOMO (pnl24h / pnl7d / pnl30d / totalPnL) and normalised here."},"volume":{"type":["null","number"]},"followers":{"type":["null","integer"],"description":"Traders only."},"numTrades":{"type":["null","integer"],"description":"Traders only."},"memberCount":{"type":["null","integer"],"description":"Clans only."},"marketCap":{"type":["null","number"],"description":"Tokens only."},"price":{"type":["null","number"],"description":"Tokens only."},"liquidity":{"type":["null","number"],"description":"Tokens only."}}}}}}}}}}}},"/v2/leaderboard/tokens/most-held":{"get":{"summary":"Most-held tokens","tags":["Leaderboards"],"description":"The tokens held by the most FOMO traders right now.\n\nNo window — token lists are \"right now\" boards.\n\n`capturedAt` is when the ranking was read from FOMO, and a snapshot is one\ninstant. Boards are sampled every 30 seconds, so a `capturedAt` much older\nthan a minute means the sampler is behind rather than that the board is\nquiet.\n\nPass `?at=` to read the board as it stood at an instant — the most recent\nsnapshot taken at or before it.\n\nBoards are at most 100 entries — FOMO caps them there — so there is no\npagination.\n\n- **250 CU** per call, hit or miss.","parameters":[{"schema":{"type":"integer","minimum":0},"in":"query","name":"at","required":false,"description":"Epoch milliseconds, e.g. `1755765600000` — the same unit as `capturedAt` in the response. Returns the ranking current at that moment: the most recent snapshot taken at or before it. Omit for the newest."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["board","window","capturedAt","count","entries"],"properties":{"board":{"type":"string","enum":["traders","clans","tokens/most-held","tokens/trending","tokens/graduated"]},"window":{"type":["null","string"],"description":"Null for boards with no window."},"capturedAt":{"type":"integer","description":"When this ranking was read from FOMO, epoch millis. Boards are sampled every 30s, so anything much older than a minute means the sampler is behind."},"count":{"type":"integer"},"entries":{"type":"array","items":{"type":"object","required":["rank","id"],"properties":{"rank":{"type":"integer","description":"Position on the board, 1 = top."},"id":{"type":"string","description":"The subject's FOMO id: a trader uuid, a clan id, or a token address."},"handle":{"type":["null","string"],"description":"Trader @-handle, clan name, or token symbol."},"label":{"type":["null","string"],"description":"Display name, clan name, or token name."},"avatarUrl":{"type":["null","string"]},"pnl":{"type":["null","number"],"description":"The measure the board is sorted by, in USD. Named per window by FOMO (pnl24h / pnl7d / pnl30d / totalPnL) and normalised here."},"volume":{"type":["null","number"]},"followers":{"type":["null","integer"],"description":"Traders only."},"numTrades":{"type":["null","integer"],"description":"Traders only."},"memberCount":{"type":["null","integer"],"description":"Clans only."},"marketCap":{"type":["null","number"],"description":"Tokens only."},"price":{"type":["null","number"],"description":"Tokens only."},"liquidity":{"type":["null","number"],"description":"Tokens only."}}}}}}}}}}}},"/v2/leaderboard/tokens/trending":{"get":{"summary":"Trending tokens","tags":["Leaderboards"],"description":"FOMO's trending board — the tokens seeing the most recent activity.\n\nNo window — token lists are \"right now\" boards.\n\n`capturedAt` is when the ranking was read from FOMO, and a snapshot is one\ninstant. Boards are sampled every 30 seconds, so a `capturedAt` much older\nthan a minute means the sampler is behind rather than that the board is\nquiet.\n\nPass `?at=` to read the board as it stood at an instant — the most recent\nsnapshot taken at or before it.\n\nBoards are at most 100 entries — FOMO caps them there — so there is no\npagination.\n\n- **250 CU** per call, hit or miss.","parameters":[{"schema":{"type":"integer","minimum":0},"in":"query","name":"at","required":false,"description":"Epoch milliseconds, e.g. `1755765600000` — the same unit as `capturedAt` in the response. Returns the ranking current at that moment: the most recent snapshot taken at or before it. Omit for the newest."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["board","window","capturedAt","count","entries"],"properties":{"board":{"type":"string","enum":["traders","clans","tokens/most-held","tokens/trending","tokens/graduated"]},"window":{"type":["null","string"],"description":"Null for boards with no window."},"capturedAt":{"type":"integer","description":"When this ranking was read from FOMO, epoch millis. Boards are sampled every 30s, so anything much older than a minute means the sampler is behind."},"count":{"type":"integer"},"entries":{"type":"array","items":{"type":"object","required":["rank","id"],"properties":{"rank":{"type":"integer","description":"Position on the board, 1 = top."},"id":{"type":"string","description":"The subject's FOMO id: a trader uuid, a clan id, or a token address."},"handle":{"type":["null","string"],"description":"Trader @-handle, clan name, or token symbol."},"label":{"type":["null","string"],"description":"Display name, clan name, or token name."},"avatarUrl":{"type":["null","string"]},"pnl":{"type":["null","number"],"description":"The measure the board is sorted by, in USD. Named per window by FOMO (pnl24h / pnl7d / pnl30d / totalPnL) and normalised here."},"volume":{"type":["null","number"]},"followers":{"type":["null","integer"],"description":"Traders only."},"numTrades":{"type":["null","integer"],"description":"Traders only."},"memberCount":{"type":["null","integer"],"description":"Clans only."},"marketCap":{"type":["null","number"],"description":"Tokens only."},"price":{"type":["null","number"],"description":"Tokens only."},"liquidity":{"type":["null","number"],"description":"Tokens only."}}}}}}}}}}}},"/v2/leaderboard/tokens/graduated":{"get":{"summary":"Graduated tokens","tags":["Leaderboards"],"description":"Tokens that have completed their bonding curve and migrated to a DEX. Each entry carries the launchpad it graduated from under `raw`.\n\nNo window — token lists are \"right now\" boards.\n\n`capturedAt` is when the ranking was read from FOMO, and a snapshot is one\ninstant. Boards are sampled every 30 seconds, so a `capturedAt` much older\nthan a minute means the sampler is behind rather than that the board is\nquiet.\n\nPass `?at=` to read the board as it stood at an instant — the most recent\nsnapshot taken at or before it.\n\nBoards are at most 100 entries — FOMO caps them there — so there is no\npagination.\n\n- **250 CU** per call, hit or miss.","parameters":[{"schema":{"type":"integer","minimum":0},"in":"query","name":"at","required":false,"description":"Epoch milliseconds, e.g. `1755765600000` — the same unit as `capturedAt` in the response. Returns the ranking current at that moment: the most recent snapshot taken at or before it. Omit for the newest."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["board","window","capturedAt","count","entries"],"properties":{"board":{"type":"string","enum":["traders","clans","tokens/most-held","tokens/trending","tokens/graduated"]},"window":{"type":["null","string"],"description":"Null for boards with no window."},"capturedAt":{"type":"integer","description":"When this ranking was read from FOMO, epoch millis. Boards are sampled every 30s, so anything much older than a minute means the sampler is behind."},"count":{"type":"integer"},"entries":{"type":"array","items":{"type":"object","required":["rank","id"],"properties":{"rank":{"type":"integer","description":"Position on the board, 1 = top."},"id":{"type":"string","description":"The subject's FOMO id: a trader uuid, a clan id, or a token address."},"handle":{"type":["null","string"],"description":"Trader @-handle, clan name, or token symbol."},"label":{"type":["null","string"],"description":"Display name, clan name, or token name."},"avatarUrl":{"type":["null","string"]},"pnl":{"type":["null","number"],"description":"The measure the board is sorted by, in USD. Named per window by FOMO (pnl24h / pnl7d / pnl30d / totalPnL) and normalised here."},"volume":{"type":["null","number"]},"followers":{"type":["null","integer"],"description":"Traders only."},"numTrades":{"type":["null","integer"],"description":"Traders only."},"memberCount":{"type":["null","integer"],"description":"Clans only."},"marketCap":{"type":["null","number"],"description":"Tokens only."},"price":{"type":["null","number"],"description":"Tokens only."},"liquidity":{"type":["null","number"],"description":"Tokens only."}}}}}}}}}}}},"/v2/leaderboard/traders-fomoscan":{"get":{"summary":"FomoScan's own trader ranking, from on-chain realized PnL","tags":["Leaderboards"],"description":"Realized profit and loss for every wallet FomoScan has proven on chain, measured across a trader's entire on-chain activity rather than only the trades they made on FOMO, and refreshed once a day. Distinct from GET /v2/leaderboard/traders, which mirrors FOMO's own scoreboard. Returns one board of 100 — the top by realized PnL, or the bottom with order=worst. Nothing is filtered out: the board is the sweep, ranked. Entries carry the trader's numbers and wallet age, and `tokensCreated` for spotting launchers.","parameters":[{"schema":{"type":"string","enum":["24h","7d","30d"],"default":"24h"},"in":"query","name":"window","required":false},{"schema":{"type":"string","enum":["best","worst"],"default":"best"},"in":"query","name":"order","required":false}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["window","capturedAt","order","count","entries"],"properties":{"window":{"type":"string","enum":["24h","7d","30d"]},"capturedAt":{"type":"integer","description":"Epoch ms when the sweep STARTED asking Codex. The only freshness signal — a daily job, so expect this to be up to ~24h old."},"complete":{"type":"boolean","description":"False when any batch failed. The board is still served; judge it with coverage."},"coverage":{"type":"number","description":"Share of requested wallets Codex returned data for. Drifting below ~0.99 means we are resolving wallets that do not exist on chain."},"walletsRanked":{"type":"integer","description":"Wallets in the sweep this board was drawn from."},"order":{"type":"string","enum":["best","worst"]},"count":{"type":"integer"},"entries":{"type":"array","items":{"type":"object","required":["rank","wallet"],"properties":{"rank":{"type":"integer","description":"Position within this board, 1 = top."},"wallet":{"type":"string","description":"The Solana address these numbers describe."},"handle":{"type":["null","string"],"description":"FOMO handle, when we hold one."},"displayName":{"type":["null","string"]},"avatarUrl":{"type":["null","string"]},"twitter":{"type":["null","string"],"description":"Bare X username."},"followers":{"type":["null","integer"]},"pnlUsd":{"type":["null","number"],"description":"Realized profit for the window, USD."},"pnlUsdExNative":{"type":["null","number"],"description":"Realized profit EXCLUDING the wallet's trades in SOL itself — token picking in isolation. Can carry the opposite sign to pnlUsd."},"returnPct":{"type":["null","number"],"description":"Realized profit as a percentage."},"returnPctExNative":{"type":["null","number"]},"avgProfitPerTradeUsd":{"type":["null","number"]},"volumeUsd":{"type":["null","number"]},"avgTradeSizeUsd":{"type":["null","number"]},"trades":{"type":["null","integer"],"description":"Swaps in the window."},"uniqueTokens":{"type":["null","integer"]},"winRate":{"type":["null","number"],"description":"0-1. Codex reports 0-100; it is normalised here so one surface owns the scale."},"avgHoldSeconds":{"type":["null","number"],"description":"Null for roughly half of wallets, high-volume ones included."},"tokensCreated":{"type":["null","integer"],"description":"A wallet that launches its own tokens is a launcher, not a trader."},"tokensMigrated":{"type":["null","integer"]},"walletFirstSeenAt":{"type":["null","integer"],"description":"Epoch ms of first on-chain tx."},"walletLastSeenAt":{"type":["null","integer"]}}},"description":"Up to 100 traders — the top by realized PnL, or the bottom when order=worst."}}}}}}}}},"/v2/me":{"get":{"summary":"Introspect the calling API key","tags":["Account"],"description":"Returns the account, project, plan, scopes, and entitlement bound to\nthe presented API key, plus a snapshot of both unit buckets.\n\n- Requires any valid API key (no specific scope). Costs 0 CU.\n- `usage.period` is the UTC billing bucket (`YYYY-MM`);\n  `unitsRemaining` never goes below 0 and does not roll over.\n- `usage.additionalUnits` is the top-up balance: it never expires and\n  is spent only once the month’s grant cannot cover a call.\n- `entitlement.unmetered` is true on a key that is never refused for\n  quota. Calls are still counted — `usage.unitsUsed` climbs as always\n  — but `usage.unitsRemaining` is `null`, because nothing is running\n  out. `monthlyUnits` still reports the plan’s grant; it is simply not\n  a limit on such a key.","security":[{"b2bApiKey":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"429":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v2/pump/user/wallet/{wallet}":{"get":{"summary":"[Experimental] A pump.fun user by wallet","tags":["Pump"],"description":"The pump.fun profile for a wallet: handle, bio, follower counts, linked X handle.\n\n- On pump.fun the **wallet is the account**, so this is a stored read, not a\n  resolution — **250 CU**, not the 50,000 CU of `/v2/user/wallet`.\n- **404** when we hold no user for the wallet.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"wallet","required":true}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"wallet":{"type":"string"},"username":{"type":["null","string"]},"bio":{"type":["null","string"]},"profileImage":{"type":["null","string"]},"xUsername":{"type":["null","string"],"description":"Linked X handle — the bridge to a FOMO identity."},"isVerified":{"type":["null","boolean"]},"isPumpUser":{"type":["null","boolean"],"description":"False for a wallet that traded but never made an account."},"followers":{"type":["null","integer"]},"following":{"type":["null","integer"]},"groupBadges":{"type":"array","items":{"type":"string"}}}}}}}}}},"/v2/pump/thesis":{"get":{"summary":"[Experimental] Every pump.fun callout, newest first","tags":["Pump"],"description":"The 25 most recent pump.fun callouts across ALL coins — the\nglobal thesis feed pump.fun itself does not expose.\n\n- **Live:** poll with no `before`; dedupe on `id`.\n- **Backfill:** feed `nextBefore` back as `?before=` until `hasMore` is false.\n- `before` is a callout **`id`**; an unknown one is a **400**. **250 CU**.","parameters":[{"schema":{"type":"string"},"in":"query","name":"before","required":false,"description":"Previous page’s `nextBefore`."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["count","hasMore","nextBefore","items"],"properties":{"count":{"type":"integer","description":"Items in this page (0..25)."},"hasMore":{"type":"boolean"},"nextBefore":{"type":["null","string"],"description":"Oldest item's `id`; send as `?before=` to continue. Null on the last page."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"wallet":{"type":"string","description":"The author. On pump.fun the wallet is the identity."},"userName":{"type":["null","string"]},"xUsername":{"type":["null","string"]},"isVerified":{"type":["null","boolean"]},"coinMint":{"type":"string"},"chain":{"type":["null","string"]},"coinSymbol":{"type":["null","string"]},"coinName":{"type":["null","string"]},"thesis":{"type":["null","string"]},"mediaUrl":{"type":["null","string"]},"calledOutAtMcap":{"type":["null","number"],"description":"Market cap (USD) when the callout was posted."},"multiple":{"type":["null","number"],"description":"Price move since the callout, ×."},"maxMultiplier":{"type":["null","number"]},"createdAt":{"type":["null","integer"],"description":"Post time, epoch ms."}}}}}}}}}}}},"/v2/pump/thesis/token/{mint}":{"get":{"summary":"[Experimental] A coin's callout wall, newest first","tags":["Pump"],"description":"The 25 most recent pump.fun callouts (their theses) for a coin.\n\n- **Live:** poll with no `before`; dedupe on `id`.\n- **Backfill:** feed `nextBefore` back as `?before=` until `hasMore` is false.\n- `before` is a callout **`id`**; an unknown one is a **400**.\n- **250 CU** a page, empty or not.","parameters":[{"schema":{"type":"string"},"in":"query","name":"before","required":false,"description":"Previous page’s `nextBefore`."},{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"mint","required":true}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["count","hasMore","nextBefore","items"],"properties":{"count":{"type":"integer","description":"Items in this page (0..25)."},"hasMore":{"type":"boolean"},"nextBefore":{"type":["null","string"],"description":"Oldest item's `id`; send as `?before=` to continue. Null on the last page."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"wallet":{"type":"string","description":"The author. On pump.fun the wallet is the identity."},"userName":{"type":["null","string"]},"xUsername":{"type":["null","string"]},"isVerified":{"type":["null","boolean"]},"coinMint":{"type":"string"},"chain":{"type":["null","string"]},"coinSymbol":{"type":["null","string"]},"coinName":{"type":["null","string"]},"thesis":{"type":["null","string"]},"mediaUrl":{"type":["null","string"]},"calledOutAtMcap":{"type":["null","number"],"description":"Market cap (USD) when the callout was posted."},"multiple":{"type":["null","number"],"description":"Price move since the callout, ×."},"maxMultiplier":{"type":["null","number"]},"createdAt":{"type":["null","integer"],"description":"Post time, epoch ms."}}}}}}}}}}}},"/v2/pump/thesis/wallet/{wallet}":{"get":{"summary":"[Experimental] A wallet's callouts, newest first","tags":["Pump"],"description":"The 25 most recent callouts posted by one wallet. Same paging as\nthe token wall: poll with no `before`, backfill with `nextBefore`. **250 CU**.","parameters":[{"schema":{"type":"string"},"in":"query","name":"before","required":false},{"schema":{"type":"string","minLength":1,"maxLength":128},"in":"path","name":"wallet","required":true}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["count","hasMore","nextBefore","items"],"properties":{"count":{"type":"integer","description":"Items in this page (0..25)."},"hasMore":{"type":"boolean"},"nextBefore":{"type":["null","string"],"description":"Oldest item's `id`; send as `?before=` to continue. Null on the last page."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"wallet":{"type":"string","description":"The author. On pump.fun the wallet is the identity."},"userName":{"type":["null","string"]},"xUsername":{"type":["null","string"]},"isVerified":{"type":["null","boolean"]},"coinMint":{"type":"string"},"chain":{"type":["null","string"]},"coinSymbol":{"type":["null","string"]},"coinName":{"type":["null","string"]},"thesis":{"type":["null","string"]},"mediaUrl":{"type":["null","string"]},"calledOutAtMcap":{"type":["null","number"],"description":"Market cap (USD) when the callout was posted."},"multiple":{"type":["null","number"],"description":"Price move since the callout, ×."},"maxMultiplier":{"type":["null","number"]},"createdAt":{"type":["null","integer"],"description":"Post time, epoch ms."}}}}}}}}}}}},"/v2/pump/leaderboard/traders":{"get":{"summary":"[Experimental] The pump.fun PnL leaderboard","tags":["Pump"],"description":"pump.fun's top 100 traders by realized+unrealized PnL, as a snapshot.\n\n- `period` is `daily`, `weekly` (default) or `monthly`.\n- `capturedAt` is when the fleet read the board — the only freshness signal.\n- **Point-in-time:** `?at=` (epoch ms or ISO) returns the latest snapshot at or\n  before that instant. **250 CU**.","parameters":[{"schema":{"type":"string","enum":["daily","weekly","monthly"],"default":"weekly"},"in":"query","name":"period","required":false},{"schema":{"type":"string"},"in":"query","name":"at","required":false,"description":"Epoch ms or ISO timestamp."}],"security":[{"b2bApiKey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["period","capturedAt","count","entries"],"properties":{"period":{"type":"string"},"capturedAt":{"type":["null","integer"],"description":"Epoch ms the board was read."},"count":{"type":"integer"},"entries":{"type":"array","items":{"type":"object","properties":{"rank":{"type":"integer"},"wallet":{"type":"string"},"username":{"type":["null","string"]},"isVerified":{"type":["null","boolean"]},"pnlSol":{"type":["null","number"]},"pnlUsd":{"type":["null","number"]},"pnlPercent":{"type":["null","number"]},"realizedPnlUsd":{"type":["null","number"]},"unrealizedPnlUsd":{"type":["null","number"]}}}}}}}}}}}},"/v2/ws":{"get":{"tags":["WebSocket"],"summary":"Live theses, the firehose or a token list (WebSocket)","description":"One WebSocket for the thesis feed. Attach once, then declare what you want\n— the firehose or a list of tokens — and change it at any time without\nreconnecting.\n\n**This path is an upgrade, not a request.** A plain `GET` returns `404`;\nonly a request carrying the WebSocket handshake headers is served. The\n\"Try it out\" button above cannot exercise it.\n\n**The key cannot come from a browser.** Authentication is the same\n`authorization: Bearer <key>` header as every other endpoint, and the\nbrowser `WebSocket` constructor cannot set headers. Hold the socket in your\nbackend and relay to your front end from there.\n\nMost keys reach this path. Two do not behave that way: a **socket-only** key\n(scoped `live:read`) holds this open and is refused on every REST endpoint,\nand a **REST-only** key (scoped `http:read`) is the mirror — every other\nendpoint, and this one refused. A key that lacks the scope is refused during\nthe handshake with a `403`, before the upgrade, so you read it as an HTTP\nstatus rather than a dropped socket.\n\n### Subscribing\n\nA connection holds **one** subscription. It is one of two shapes:\n\n```json\n{ \"type\": \"subscribe\", \"subscription\": { \"type\": \"all\" } }\n{ \"type\": \"subscribe\", \"subscription\": { \"type\": \"tokens\", \"tokens\": [\"So1111…\", \"0xabc…\"] } }\n```\n\nA `subscribe` **replaces** whatever you had before — it does not add to it.\nTo change your watchlist, send the new list in full. To stop receiving:\n\n```json\n{ \"type\": \"unsubscribe\" }\n```\n\nThat is the one thing here likely to catch you out, so we say it out loud\non the wire too — see the `replaced` frame below.\n\nYou attach unsubscribed and receive nothing until you ask. A `tokens`\nsubscription may name up to **50** addresses; past that, use `all` — it is\none subscription, it already carries every token, and it is the same price.\n\n### Frames you receive\n\nOn attach:\n\n```json\n{ \"type\": \"ready\", \"connectionId\": \"4f9c…\" }\n```\n\nAfter every accepted frame, your current subscription — the whole of it, so\nthere is nothing to reconcile (`null` once you unsubscribe):\n\n```json\n{ \"type\": \"subscription\", \"subscription\": { \"type\": \"tokens\", \"tokens\": [\"So1111…\"] } }\n```\n\nAnd, when a `subscribe` tore down an earlier subscription, one frame ahead\nof it saying what stopped:\n\n```json\n{ \"type\": \"replaced\", \"previous\": { \"type\": \"tokens\", \"tokens\": [\"EPjF…\"] } }\n```\n\nThe two always arrive in that order, so a client applying frames as they\nland never ends up holding the dead subscription. A rejected `subscribe`\nproduces neither — nothing was torn down.\n\nThen one frame per newly indexed thesis matching it:\n\n```json\n{ \"type\": \"thesis\", \"data\": { \"id\": \"…\", \"tokenAddress\": \"…\", \"tokenNetwork\": \"sol\", … } }\n```\n\n`data` is the **same object** `GET /v2/thesis/token/{tokenAddress}` returns\nin `items` — same field names, same `id`, same epoch-millisecond\ntimestamps — plus `authorAvatarUrl` and `tokenImageUrl`, which a live\nclient needs at the moment a thesis lands. It is not a second shape to code\nagainst. Read `data.tokenAddress` to route a frame back to its token.\n\nA rejected frame is answered, does not close the socket, and does **not**\nchange what you are subscribed to — so a typo costs you nothing:\n\n```json\n{ \"type\": \"error\", \"error\": \"too many addresses: 91 named, 50 is the cap\" }\n```\n\n### What you have to know before you build on it\n\n- **There is no replay.** You hear what arrives after you subscribe,\n  nothing before it. Take a snapshot from the polling endpoint and\n  reconcile on `id`, which is stable and is the same id the polling\n  endpoint returns. If the socket drops,\n  poll to close the gap, then reattach and resubscribe.\n- **Subscriptions do not survive a reconnect.** A new socket starts empty.\n- **Only newly indexed theses are pushed.** One already in our index is\n  never re-sent, even when we re-read the page it sits on.\n- **Only recent theses are pushed.** A thesis posted more than 10 minutes\n  before we indexed it is not sent. That happens when a token first enters\n  our coverage and its existing wall is read for the first time; those\n  theses are in the polling endpoint, not on the socket. So\n  `now − fomoCreatedAt` on a frame is a fair measure of end-to-end delay.\n- **Frames are not ordered by `fomoCreatedAt`.** They arrive in the order we\n  indexed them. Sort client-side if order matters.\n- **`null` means not observed, never zero.** An absent `likeCount` is \"we\n  did not see one\", not \"nobody liked it\".\n- **The pong is mandatory.** The server pings every 30s and terminates any\n  socket that has not ponged by the next ping. Browser and Node `ws` clients\n  answer automatically; a hand-rolled client must reply itself. A socket\n  killed mid-path by a proxy stays open and silent at both ends, and the\n  pong is the only thing that detects it.\n\n### Close codes\n\n| code | meaning |\n|---|---|\n| `1008` | the key is out of quota |\n\n### Billing\n\nCharged **20,000 CU per minute held open** — on attach and again on each\nminute after. The meter stops when the socket closes, so you pay for the\nminutes you actually held and nothing more.\n\n**Budget for it before you build.** A per-minute price compounds in a way a\nper-call one does not: an hour is 1,200,000 CU, and a socket left open all\nmonth exceeds any self-serve plan many times over. This is priced to be\nattached when you are watching something and closed when you are not. If\nyou need a permanently open tap, talk to us — that is a contract, not a\nplan.\n\nThe price is **per socket, not per token**: one address, fifty, or the whole\nfirehose all read the same on the meter. Once you are holding a socket,\nwidening what it watches is free. See `GET /v2/me` for your remaining\nbalance.","security":[{"b2bApiKey":[]}],"responses":{"101":{"description":"Switching Protocols — the socket is open."},"401":{"description":"Missing or invalid API key."},"404":{"description":"The request carried no WebSocket upgrade."}}}}},"components":{"securitySchemes":{"b2bApiKey":{"type":"http","scheme":"bearer","description":"B2B API key (`fsk_live_…` / `fsk_test_…`), issued by FomoScan. Also accepted via `X-Api-Key`."}},"schemas":{"ErrorBody":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, lower-snake-case error code.","enum":["NOT_FOUND","VALIDATION_ERROR","QUERY_TOO_SHORT","RATE_LIMITED","UNAUTHORIZED","QUEUE_UNAVAILABLE","SCRAPER_UNHEALTHY","INTERNAL_ERROR","NOT_IMPLEMENTED","EVM_REQUIRES_VERIFIED_SVM","SYNC_ALREADY_RUNNING","TIMEOUT","BAD_REQUEST","FORBIDDEN","CONFLICT","FLEET_UNAVAILABLE","FORBIDDEN_SCOPE","QUOTA_EXCEEDED","B2B_DISABLED","IDEMPOTENCY_CONFLICT","NOT_OBSERVED"]},"message":{"type":"string"},"retryAfter":{"type":"integer","description":"Seconds; only on RATE_LIMITED."},"addUrl":{"type":"string","description":"Hint URL for NOT_FOUND on get-user."},"syncId":{"type":"string","description":"Present on SYNC_ALREADY_RUNNING."},"pollUrl":{"type":"string","description":"Present on SYNC_ALREADY_RUNNING."}}}}},"def-0":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, lower-snake-case error code.","enum":["NOT_FOUND","VALIDATION_ERROR","QUERY_TOO_SHORT","RATE_LIMITED","UNAUTHORIZED","QUEUE_UNAVAILABLE","SCRAPER_UNHEALTHY","INTERNAL_ERROR","NOT_IMPLEMENTED","EVM_REQUIRES_VERIFIED_SVM","SYNC_ALREADY_RUNNING","TIMEOUT","BAD_REQUEST","FORBIDDEN","CONFLICT","FLEET_UNAVAILABLE","FORBIDDEN_SCOPE","QUOTA_EXCEEDED","B2B_DISABLED","IDEMPOTENCY_CONFLICT","NOT_OBSERVED"]},"message":{"type":"string"},"retryAfter":{"type":"integer","description":"Seconds; only on RATE_LIMITED."},"addUrl":{"type":"string","description":"Hint URL for NOT_FOUND on get-user."},"syncId":{"type":"string","description":"Present on SYNC_ALREADY_RUNNING."},"pollUrl":{"type":"string","description":"Present on SYNC_ALREADY_RUNNING."}}}},"title":"ErrorBody"}}}}