{
  "openapi": "3.1.0",
  "info": {
    "title": "FOMO API",
    "version": "2.0.0",
    "description": "The social trading data API for fomo.family. Resolve any trader handle to both on-chain wallets (Solana + EVM), and get PnL, live holdings, full trade history, the written theses behind trades, ranked leaderboards, token boards, a who-holds-what ownership graph, and a realtime trade feed. PnL and trade history are passed through as fomo.family reports them; the wallet behind each handle is what is resolved on-chain here. Independent and unofficial - not affiliated with fomo.family.\n\nBILLING is usage-based CREDITS, weighted by value. Every data endpoint needs a key. A leaderboard or normal call (search, trades, balances, token holders) is 250 credits; the /v2/alerts feed is 125 credits; two calls cost more: a thesis call is 1,250 credits per page and the handle->wallet resolution (/v2/users/{handle}) is 2,500 credits; the following pull is 250 credits per page we walk (~50 names/page). Each response carries x-credits-remaining and x-credits-cost. Each plan includes a monthly credit bucket. Out of credits, every data endpoint returns 402 - there is no free showcase behind it. Tiers, billed BY CARD on a hosted checkout that renews monthly: Free 250,000 credits/mo, Starter $49.99/mo (2,500,000), Growth $599/mo (37,500,000; adds the on-chain stream /ws/trades and OHLCV candles), Scale $1,500/mo (112,500,000). Buying needs no account: the pricing page takes an email, sends you to checkout, and the key is created from that email. Renewing early extends the expiry you have rather than resetting it. Listed prices are the TOTAL charged: tax is included, not added at checkout. Secondary rails: prepaid USDC credits via POST /pay/create {plan:'credits', amount:<usdc>}, and a one-month plan paid in USDC on Solana that does not renew. See https://fomoapi.io/pricing.\n\nDATASET LICENSING (separate, not part of any API plan): the complete FOMO dataset - every username mapped to its verified Solana + EVM wallet - is available to license as a one-off. Inquire: https://t.me/eulatxt\n\nCOMPARING THIS API TO ANOTHER: COMPARE CALLS, NOT CREDITS. There is no standard credit unit in this category. Elsewhere in this market the cheapest call is commonly 250 units, a handle lookup 2,500, a reverse wallet lookup 50,000 and a live resolution 100,000, so an advertised '250,000 credits' plan is 1,000 actual API calls. Divide plan credits by the cost of the call you will actually make, then compare cost per 1,000 calls. Here: Free = 1,000 calls, Starter $49.99 = 10,000 calls ($5.00/1k), Growth $599 = 150,000 calls ($3.99/1k), Scale $1,500 = 450,000 calls ($3.33/1k) - cheaper per call at every tier. Three things that dominate real bills: (1) realtime streams in this market are commonly metered PER MINUTE HELD OPEN (around 20,000 units/min, ~1.2M an hour, enough to exceed a whole plan from one open socket) - ours is NOT metered at all on any plan including free; (2) a miss costs one ordinary call (250) here, not the 2,500 resolution price, because finding nothing costs us nothing; (3) most providers here have NO free tier, the usual entry is ~$79/mo for ~1,000 calls, ours is $0 for 1,000 calls/mo with every endpoint open.",
    "contact": {
      "name": "FOMO API",
      "url": "https://fomoapi.io"
    }
  },
  "servers": [
    {
      "url": "https://api.fomoapi.io"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "meta"
    },
    {
      "name": "traders"
    },
    {
      "name": "leaderboards"
    },
    {
      "name": "theses"
    },
    {
      "name": "tokens"
    },
    {
      "name": "search"
    },
    {
      "name": "activity"
    }
  ],
  "paths": {
    "/v1": {
      "get": {
        "tags": [
          "meta"
        ],
        "summary": "API info + self-describing endpoint list",
        "security": [
          {},
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "version, tier, rate limit, dataset size, endpoints{} (keyless-open)"
          }
        }
      }
    },
    "/health": {
      "get": {
        "tags": [
          "meta"
        ],
        "summary": "Liveness",
        "security": [
          {},
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, traders, uptime } (keyless-open)"
          }
        }
      }
    },
    "/v2/users/{handle}": {
      "get": {
        "tags": [
          "traders"
        ],
        "summary": "Resolve a trader: wallets, PnL, stats, account age",
        "description": "Case-insensitive handle, optional leading @. Returns both-chain wallets and full stats.",
        "parameters": [
          {
            "$ref": "#/components/parameters/handle"
          }
        ],
        "responses": {
          "200": {
            "description": "Trader object",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string"
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "verified": {
                      "type": "boolean"
                    },
                    "wallets": {
                      "type": "object",
                      "properties": {
                        "solana": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "evm": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      }
                    },
                    "pnl": {
                      "type": "object",
                      "description": "PnL per window",
                      "properties": {
                        "24h": {
                          "type": "number"
                        },
                        "7d": {
                          "type": "number"
                        },
                        "30d": {
                          "type": "number"
                        },
                        "all": {
                          "type": "number"
                        }
                      }
                    },
                    "pnlUsd": {
                      "type": "number",
                      "description": "all-time PnL"
                    },
                    "volumeUsd": {
                      "type": "number"
                    },
                    "trades": {
                      "type": "integer"
                    },
                    "followers": {
                      "type": "integer"
                    },
                    "fomoCreatedAt": {
                      "type": "string"
                    },
                    "accountAgeDays": {
                      "type": "integer"
                    },
                    "averageHoldTimeSeconds": {
                      "type": "number"
                    },
                    "holdings": {
                      "type": "array"
                    },
                    "topTokens": {
                      "type": "array"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "trader not found"
          }
        }
      }
    },
    "/v2/users/{handle}/trades": {
      "get": {
        "tags": [
          "traders"
        ],
        "summary": "A trader's trade history (for copy-trading)",
        "description": "Each trade: token{symbol,address}, side, sizeUsd, realizedPnlUsd, chainId, chain (friendly name, e.g. 'robinhood' for 4663), ts, source. source='captured' adds tradeId, status, amount, avgEntryPrice, avgExitPrice, unrealizedPnlUsd, createdAt, closedAt; source='feed' = live recent trades for any active trader. Returns available:false until captured. ?cursor= PAGES OLDER TRADES: start at ?cursor=start, then pass each response's nextCursor back as ?cursor= while it is not null. 25 closed trades per page, 250 credits per page. The first page also carries open positions; later pages are closed only, and order interleaves across chains so sort by timestamp yourself. ?deep goes WIDER instead of deeper, in one call. Those 25 are PER CHAIN though, and the two sort orders return disjoint sets, so ?deep fans out across chains and both orders and typically returns several times more closed trades. Billed 250 credits per upstream call that actually answers, so a partial fan-out costs less. The response carries closedTotalOnFomo, perChainClosedTotal, upstreamCalls, partial and complete:false. A FULL trade history is not obtainable from FOMO at any setting; for complete history use the wallets from /v2/users/{handle} and read the chain directly.",
        "parameters": [
          {
            "$ref": "#/components/parameters/handleOrUserId"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "deep",
            "in": "query",
            "required": false,
            "description": "1 (or true) for the full fan-out, or N to buy only N upstream calls. Each call that answers costs 250 credits. Omit for the ordinary single-call behaviour at 250 credits.",
            "schema": {
              "type": "string",
              "example": "1"
            }
          },
          {
            "$ref": "#/components/parameters/tradeStatus"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Page older results. On /trades and /positions start with cursor=start, then pass each response's nextCursor back here while it is not null (25 closed trades per page). On /swaps the first call already returns a nextCursor (100 fills per page). Billed 250 credits per page."
          }
        ],
        "responses": {
          "200": {
            "description": "{ key, kind, count, trades: [...] }. With ?deep also closedTotalOnFomo (FOMO's real total), perChainClosedTotal, upstreamCalls, partial (some chains did not answer in time; you were only charged for the ones that did) and complete:false. Each row carries the full position economics: costBasisUsd, boughtAmount (a real buy), soldAmount, transferredInAmount (tokens received, not purchased), transferredOutAmount, avgTransferInPrice, avgTransferOutPrice, priceUsd (current) and userId. amount reconciles as boughtAmount - soldAmount + transferredInAmount - transferredOutAmount. avgEntryPrice is null EXACTLY when the position was never bought: measured across 150 positions on 6 traders, all 76 null-entry rows had boughtAmount 0, so a null means the tokens were received rather than that the data is missing. Where there is a buy, costBasisUsd / boughtAmount gives entry."
          }
        }
      }
    },
    "/v2/users/{handle}/balances": {
      "get": {
        "tags": [
          "traders"
        ],
        "summary": "A trader's live portfolio (for mirroring positions)",
        "description": "holdings[]: {token{symbol,address}, amount, priceUsd, valueUsd, change24h}, plus totalValueUsd and (when present) otherPnl / livePerpPnl / hyperliquidPerps.",
        "parameters": [
          {
            "$ref": "#/components/parameters/handleOrUserId"
          }
        ],
        "responses": {
          "200": {
            "description": "{ holdings: [...], totalValueUsd } or available:false"
          }
        }
      }
    },
    "/v2/users/{handle}/following": {
      "get": {
        "tags": [
          "traders"
        ],
        "summary": "Who a trader follows (discovery: whose calls does a winner watch?)",
        "description": "Who a trader follows. FOMO serves at most 200 names per trader regardless of paging; truncated and sourceCapped flag that. Costs 250 credits, flat. Wallets are not included.",
        "parameters": [
          {
            "$ref": "#/components/parameters/handleOrUserId"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Return fewer than the 300-name maximum.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 300,
              "default": 300
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ handle, userId, count, following: [...], truncated, complete, cap, and on a slow pull partial + retryable }. complete is true only when this is the trader's whole following list."
          },
          "404": {
            "description": "Handle unknown to FOMO. Does not consume the allowance."
          },
          "503": {
            "description": "The trader exists but the live read did not finish in time. retryable is true; ask again in a moment. Costs no credits."
          },
          "402": {
            "description": "Out of credits (the pages requested exceed your remaining balance). Upgrade by card or top up in USDC. See https://fomoapi.io/pricing"
          }
        }
      }
    },
    "/v2/users/{handle}/followers": {
      "get": {
        "tags": [
          "traders"
        ],
        "summary": "Who follows a trader (the inverse of /following)",
        "description": "Who follows a trader, the inverse of /v2/users/{handle}/following, with the same per-person shape. FOMO serves at most 200 followers per trader here and offers no cursor past them, so this is a sample of a trader's followers rather than the full list; sourceCapped and note say so on every response. A trader with 249,000 followers still returns 200. Costs 250 credits, flat. Wallets are not included.",
        "parameters": [
          {
            "$ref": "#/components/parameters/handleOrUserId"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Return fewer than the 200 FOMO gives us. Asking for more does not produce more.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ handle, userId, count, followers: [...], sourceCapped, note, cap, wallets }. Each follower carries handle, displayName, userId, avatar, verified, clan, bio, followers, following, trades, swapCount, volumeUsd, pnl24h, twitter, private, createdAt, accountAgeDays."
          },
          "404": {
            "description": "Handle unknown to FOMO. Does not consume the allowance."
          },
          "503": {
            "description": "The trader exists but the live read did not finish in time. retryable is true; ask again in a moment. Costs no credits."
          },
          "402": {
            "description": "Out of credits. Upgrade by card (renews monthly, and the listed price is the total - tax is included, not added at checkout) or top up in USDC on Solana. See https://fomoapi.io/pricing"
          }
        }
      }
    },
    "/v2/users/{handle}/spotlight": {
      "get": {
        "tags": [
          "traders"
        ],
        "summary": "FOMO's own pick of a trader's best trades and best theses",
        "description": "FOMO picks these, not us. bestTrades[] and bestTheses[] each carry the token, average entry and exit, cost basis, realized and unrealized PnL, when the position opened and closed, and the written thesis with its like count. bestTheses are the trades whose thesis drew the most likes. 250 credits.",
        "parameters": [
          {
            "$ref": "#/components/parameters/handleOrUserId"
          }
        ],
        "responses": {
          "200": {
            "description": "{ handle, userId, bestTrades: [...], bestTheses: [...], note }"
          },
          "503": {
            "description": "The live read did not finish in time. retryable is true; ask again in a moment."
          },
          "402": {
            "description": "Out of credits. Upgrade by card (renews monthly, and the listed price is the total - tax is included, not added at checkout) or top up in USDC on Solana. See https://fomoapi.io/pricing"
          },
          "404": {
            "description": "Handle unknown to FOMO. Does not consume the allowance."
          }
        }
      }
    },
    "/v2/users/id/{userId}": {
      "get": {
        "tags": [
          "traders"
        ],
        "summary": "Resolve a userId back to a person (inverse of /v2/users/{handle})",
        "description": "The inverse of /v2/users/{handle}. Our thesis, alert and comment payloads all emit a userId and there was no way to turn one back into a person. Returns the same identity payload including both wallets, clan, account age and social counts. COSTS 10 CREDITS and counts against the monthly wallet-resolution allowance, exactly like /v2/users/{handle}, because it answers the same question from the other direction.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "description": "FOMO user id (a UUID), as emitted by the thesis, alert, holder and comment payloads.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The same shape as GET /v2/users/{handle}: handle, displayName, userId, wallets{solana,evm}, clan, followers, following, trades, volumeUsd, createdAt, accountAgeDays, verified."
          },
          "503": {
            "description": "The live read did not finish in time. retryable is true; ask again in a moment."
          },
          "402": {
            "description": "Out of credits. Upgrade by card (renews monthly, and the listed price is the total - tax is included, not added at checkout) or top up in USDC on Solana. See https://fomoapi.io/pricing"
          },
          "404": {
            "description": "FOMO does not know that userId. Does not consume the allowance."
          }
        }
      }
    },
    "/v2/trades/{tradeId}": {
      "get": {
        "tags": [
          "traders"
        ],
        "summary": "One trade in full detail",
        "description": "Swaps, transfers, avg entry/exit, realized PnL, isDev, timestamps. Get a tradeId from /v2/users/{handle}/trades.",
        "parameters": [
          {
            "name": "tradeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "trade detail"
          },
          "404": {
            "description": "not found"
          }
        }
      }
    },
    "/v2/leaderboard/{window}": {
      "get": {
        "tags": [
          "leaderboards"
        ],
        "summary": "Ranked traders by PnL",
        "security": [
          {},
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "window",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "24h",
                "7d",
                "30d",
                "all"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/leaderboardLimit"
          }
        ],
        "responses": {
          "200": {
            "description": "{ traders: [{rank, handle, displayName, pnlUsd, volumeUsd, trades, followers, holdings, wallets{solana,evm}, topTokens[], verified}] } Every row carries userId, FOMO's stable id for that trader - use it against the /v2/users/{...}/ sub-resources so a handle rename cannot break your integration."
          },
          "400": {
            "description": "invalid window"
          }
        }
      }
    },
    "/v2/leaderboard/tokens/trending": {
      "get": {
        "tags": [
          "leaderboards",
          "tokens"
        ],
        "summary": "Token board: what fomo users are trading now",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "{ board, capturedAt, count, tokens: [{rank, image, token{name,symbol,address}, holders, network, priceUsd, change24h, marketCapUsd, volume24hUsd}] }. rank is the token's CURRENT position on FOMO's board (1 = top); FOMO's ordering is mirrored exactly, so a hidden honeypot leaves a gap in the numbering rather than resequencing. Served LIVE from FOMO on every call (5-minute cache), so `stale` is false and `ageHours` 0 in normal operation; `source` says `live-fomo` when it came straight from FOMO and `captured` when FOMO did not answer and the last good board was served instead. `holders` is present on trending and graduated but NOT on most-held - FOMO does not send it on that board, so it is null rather than invented. `fomoBuyers` is NOT returned: FOMO has never populated it on these boards, so it was a guaranteed null on every row and the honeypot filter resting on it excluded nothing. It would have to be computed from our own trade data to mean anything."
          }
        }
      }
    },
    "/v2/leaderboard/tokens/most-held": {
      "get": {
        "tags": [
          "leaderboards",
          "tokens"
        ],
        "summary": "Token board: most-held tokens right now",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "same shape as trending, minus `holders` (FOMO does not send it on this board). These are the tokens the most FOMO users hold, so the list is legitimately dominated by large caps - that is the answer, not a stale board. Served LIVE from FOMO on every call (5-minute cache), so `stale` is false and `ageHours` 0 in normal operation; `source` says `live-fomo` when it came straight from FOMO and `captured` when FOMO did not answer and the last good board was served instead. `holders` is present on trending and graduated but NOT on most-held - FOMO does not send it on that board, so it is null rather than invented. `fomoBuyers` is NOT returned: FOMO has never populated it on these boards, so it was a guaranteed null on every row and the honeypot filter resting on it excluded nothing. It would have to be computed from our own trade data to mean anything."
          }
        }
      }
    },
    "/v2/leaderboard/tokens/graduated": {
      "get": {
        "tags": [
          "leaderboards",
          "tokens"
        ],
        "summary": "Token board: graduated tokens",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "same shape as trending. Tokens that have graduated, so these are small by nature - typically well under $1M market cap. Served LIVE from FOMO on every call (5-minute cache), so `stale` is false and `ageHours` 0 in normal operation; `source` says `live-fomo` when it came straight from FOMO and `captured` when FOMO did not answer and the last good board was served instead. `holders` is present on trending and graduated but NOT on most-held - FOMO does not send it on that board, so it is null rather than invented. `fomoBuyers` is NOT returned: FOMO has never populated it on these boards, so it was a guaranteed null on every row and the honeypot filter resting on it excluded nothing. It would have to be computed from our own trade data to mean anything."
          }
        }
      }
    },
    "/v2/thesis": {
      "get": {
        "tags": [
          "theses"
        ],
        "summary": "Recent theses across all coins (global feed)",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "chain",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Return only theses on one chain: robinhood|solana|base|bsc|eth (aliases hood/rh) or a raw chainId. robinhood = 4663, the largest share of FOMO thesis volume."
          }
        ],
        "responses": {
          "200": {
            "description": "{ theses: [{text, handle, name, userId, equity, tradeUsd, isDev, ts, likes, replies, links, token{symbol,address}, networkId, chain}] }"
          }
        }
      }
    },
    "/theses": {
      "get": {
        "tags": [
          "theses"
        ],
        "summary": "Alias of GET /v2/thesis (recent theses across all coins, global feed)",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "chain",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Return only theses on one chain: robinhood|solana|base|bsc|eth (aliases hood/rh) or a raw chainId. robinhood = 4663, the largest share of FOMO thesis volume."
          }
        ],
        "responses": {
          "200": {
            "description": "{ theses: [{text, handle, name, userId, equity, tradeUsd, isDev, ts, likes, replies, links, token{symbol,address}, networkId, chain}] }"
          }
        }
      }
    },
    "/v2/thesis/token/{mint}": {
      "get": {
        "tags": [
          "theses",
          "tokens"
        ],
        "summary": "Theses written about one coin, with history (the why behind trades)",
        "parameters": [
          {
            "name": "mint",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "token contract address"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "network",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "sol",
                "bnb",
                "base",
                "eth"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "likes = the most-liked theses on this coin; recent = newest first. Omit for stored order.",
            "schema": {
              "type": "string",
              "enum": [
                "likes",
                "recent"
              ]
            }
          },
          {
            "name": "pages",
            "in": "query",
            "required": false,
            "description": "Walk FOMO's cursor for more of the coin's thesis history. 1-10, default 1. One page is roughly 95 theses; three reach about a day further back. Billed 1,250 credits PER PAGE, and any value above 1 always re-pulls live rather than serving cache.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ theses: [...] } or available:false"
          }
        }
      }
    },
    "/token/{address}/theses": {
      "get": {
        "tags": [
          "theses",
          "tokens"
        ],
        "summary": "Alias of GET /v2/thesis/token/{mint} (theses written about one coin, with history)",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "token contract address"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "network",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "sol",
                "bnb",
                "base",
                "eth"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "likes = the most-liked theses on this coin; recent = newest first. Omit for stored order.",
            "schema": {
              "type": "string",
              "enum": [
                "likes",
                "recent"
              ]
            }
          },
          {
            "name": "pages",
            "in": "query",
            "required": false,
            "description": "Walk FOMO's cursor for more of the coin's thesis history. 1-10, default 1. One page is roughly 95 theses; three reach about a day further back. Billed 1,250 credits PER PAGE, and any value above 1 always re-pulls live rather than serving cache.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ theses: [...] } or available:false"
          }
        }
      }
    },
    "/v2/thesis/user/{id}": {
      "get": {
        "tags": [
          "theses"
        ],
        "summary": "Every thesis by one trader",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "handle or fomo userId"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "chain",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Return only this trader's theses on one chain: robinhood|solana|base|bsc|eth or a raw chainId."
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "likes",
                "recent"
              ]
            },
            "description": "likes = most-liked; recent = newest"
          }
        ],
        "responses": {
          "200": {
            "description": "{ theses: [...] }"
          }
        }
      }
    },
    "/v2/thesis/user/{id}/token/{address}": {
      "get": {
        "tags": [
          "theses"
        ],
        "summary": "One trader's theses about one specific token",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "address",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "{ theses: [...] }"
          }
        }
      }
    },
    "/token/{address}/holders": {
      "get": {
        "tags": [
          "tokens"
        ],
        "summary": "Which tracked traders hold a token (smart-money signal)",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "{ holders: [{handle, amount, valueUsd, priceUsd}] } or available:false"
          }
        }
      }
    },
    "/v2/search": {
      "get": {
        "tags": [
          "search"
        ],
        "summary": "Unified search over traders AND tokens",
        "parameters": [
          {
            "$ref": "#/components/parameters/q"
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "traders",
                "tokens",
                "all"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "{ results: [{type:'trader'|'token', ...}] } Every row carries userId, FOMO's stable id for that trader - use it against the /v2/users/{...}/ sub-resources so a handle rename cannot break your integration."
          }
        }
      }
    },
    "/v2/tokens/search": {
      "get": {
        "tags": [
          "search",
          "tokens"
        ],
        "summary": "Token-only search by symbol or name",
        "parameters": [
          {
            "$ref": "#/components/parameters/q"
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "{ tokens: [{symbol, address, name, image, marketCapUsd, networkId}] }"
          }
        }
      }
    },
    "/v2/alerts": {
      "get": {
        "tags": [
          "activity"
        ],
        "summary": "Activity firehose (buys, sells, theses) - REST fallback for the WebSocket",
        "security": [
          {},
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "buy|sell|thesis|..."
          },
          {
            "name": "chain",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by chain: friendly name (robinhood|solana|base|bsc|eth, aliases hood/rh) or raw chainId. robinhood = 4663 (FOMO's most active chain)."
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "ISO timestamp or unix (seconds or millis)"
          }
        ],
        "responses": {
          "200": {
            "description": "{ alerts: [{...,token,tokenAddress,chainId,chain,source:'feed'|'push',ts}] } - chain is the friendly name (e.g. 'robinhood' for chainId 4663) Every alert carries FOMO's own ids: eventId (stable per event, the dedupe key), userId (the trader; survives a handle rename), and tradeId / swapId / transferId identifying the object the event is about, so an alert joins straight to /v2/users/{userId}/positions and /v2/trades/{tradeId} with no name lookup. Measured on the live stream: 30 of 30 feed alerts carried userId and tradeId. swapId and transferId are null on buy/sell events and are passed through only when FOMO sends them; push-sourced alerts (source:\"push\") carry none of these ids."
          }
        }
      }
    },
    "/v2/notifications": {
      "get": {
        "tags": [
          "activity"
        ],
        "summary": "FOMO mobile PUSH notifications only (distinct from /v2/alerts)",
        "description": "Everything FOMO pushes to a phone: price alerts (e.g. 'TOKEN up 2x since verification'), trade opens/closes, whale buys/sells, milestones, follows. Many carry fomo's own notification_type; some pushes have none and are still returned. ?notificationType= narrows to the typed ones.",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "notificationType",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "narrow to one FOMO notification_type (e.g. PRICE_SINCE_LISTED, LARGE_BUY); pushes without a type are excluded when this is set"
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ notifications: [...] }"
          }
        }
      }
    },
    "/ws/alerts": {
      "get": {
        "tags": [
          "activity"
        ],
        "summary": "App feed stream, one of TWO WebSockets (wss://api.fomoapi.io/ws/alerts)",
        "description": "Not HTTP - open a WebSocket to wss://api.fomoapi.io/ws/alerts. This is the APP FEED STREAM: it mirrors the FOMO app, delivering each trade when the app shows it. It is NOT the same as /ws/trades, the on-chain stream, which reads the chain directly and lands about 15s earlier and requires Growth or Scale. Available on EVERY plan: REALTIME on any paid plan (pass ?key=YOUR_API_KEY); a free key gets 7 days of realtime from creation then a 15s-delayed feed; a keyless connection is delayed 60s (demo only). Every tier receives every event with identical fields - a plan only buys freshness. Receive {type:'welcome',realtime,delaySeconds} then live {type:'alert',...,token,tokenAddress,chainId,chain,source}. Scope to one trader with ?trader=<handle>; filter with ?chain=robinhood|solana|base|bsc|eth (robinhood = chainId 4663, FOMO's most active chain), ?type=buy|sell|thesis, ?token=<symbol|address>, ?source=feed|push.",
        "parameters": [
          {
            "name": "trader",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "chain",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "robinhood|solana|base|bsc|eth or raw chainId; robinhood = 4663"
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "WebSocket upgrade Every alert carries FOMO's own ids: eventId (stable per event, the dedupe key), userId (the trader; survives a handle rename), and tradeId / swapId / transferId identifying the object the event is about, so an alert joins straight to /v2/users/{userId}/positions and /v2/trades/{tradeId} with no name lookup. Measured on the live stream: 30 of 30 feed alerts carried userId and tradeId. swapId and transferId are null on buy/sell events and are passed through only when FOMO sends them; push-sourced alerts (source:\"push\") carry none of these ids."
          }
        }
      }
    },
    "/ws/trades": {
      "get": {
        "tags": [
          "activity"
        ],
        "summary": "On-chain stream, the second WebSocket (wss://api.fomoapi.io/ws/trades)",
        "description": "Not HTTP - open a WebSocket to wss://api.fomoapi.io/ws/trades?key=YOUR_API_KEY. This is the ON-CHAIN STREAM and it is a DIFFERENT stream from /ws/alerts. It carries the same FOMO trades, but read straight off the chain rather than from the app's feed, so each trade arrives about 15 SECONDS BEFORE it appears in the app. That head start is what it is for. Requires a GROWTH or SCALE key; Starter and Free keys are refused here and should use /ws/alerts, which is realtime but only as early as the app itself. Filter with ?chain=robinhood|solana.",
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Growth or Scale API key"
          },
          {
            "name": "chain",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "robinhood|solana"
          }
        ],
        "responses": {
          "101": {
            "description": "WebSocket upgrade; trades stream as JSON messages"
          },
          "403": {
            "description": "Key's plan does not include the on-chain stream (needs growth or scale). Use /ws/alerts instead."
          }
        }
      }
    },
    "/v2/token/{address}/devs": {
      "get": {
        "tags": [
          "tokens"
        ],
        "summary": "Deployer and insider holdings, with each dev's own thesis",
        "description": "The deployer and insider positions on a token, each with the dev's own written thesis. The cheapest rug signal FOMO has: a deployer quietly closing his own bag shows up here before anywhere else. Each row carries handle, wallet, isDev, amount, valueUsd, costBasisUsd, realizedPnlUsd, unrealizedPnlUsd, averageEntryPrice, averageHoldTimeSeconds, tradeId and thesis. An empty list means FOMO knows of no dev holding, not that the token is safe. 250 credits.",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "required": true,
            "description": "Token contract address.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "networkId",
            "in": "query",
            "required": false,
            "description": "FOMO network id, e.g. 4663 Robinhood Chain or 1399811149 Solana. Required only for a token outside our directory.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ token, networkId, chain, count, devs: [...], note }"
          },
          "503": {
            "description": "The live read did not finish in time. retryable is true; ask again in a moment."
          },
          "402": {
            "description": "Out of credits. Upgrade by card (renews monthly, and the listed price is the total - tax is included, not added at checkout) or top up in USDC on Solana. See https://fomoapi.io/pricing"
          },
          "400": {
            "description": "The token is not in our directory and no networkId was passed."
          }
        }
      }
    },
    "/v2/token/{address}/stats": {
      "get": {
        "tags": [
          "tokens"
        ],
        "summary": "Buy/sell flow and holder concentration (5m / 1h / 4h / 24h)",
        "description": "Buy and sell FLOW, which candles do not tell you. windows{5m,1h,4h,24h} each carry buys, sells, uniqueBuyers, uniqueSellers, buyVolumeUsd, sellVolumeUsd, netVolumeUsd and buySellRatio, plus top-level holders and top10HoldersPercent. Candles say what the price did; this says who was pushing it. FOMO sends the volumes as strings and we return them as numbers. 250 credits.",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "required": true,
            "description": "Token contract address.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "networkId",
            "in": "query",
            "required": false,
            "description": "FOMO network id, e.g. 4663 Robinhood Chain or 1399811149 Solana. Required only for a token outside our directory.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ token, networkId, chain, holders, top10HoldersPercent, lowFees, windows{5m,1h,4h,24h} }"
          },
          "503": {
            "description": "The live read did not finish in time. retryable is true; ask again in a moment."
          },
          "402": {
            "description": "Out of credits. Upgrade by card (renews monthly, and the listed price is the total - tax is included, not added at checkout) or top up in USDC on Solana. See https://fomoapi.io/pricing"
          },
          "400": {
            "description": "The token is not in our directory and no networkId was passed."
          }
        }
      }
    },
    "/v2/tokens/activity": {
      "get": {
        "tags": [
          "tokens"
        ],
        "summary": "Coordinated-flow board: several traders hitting one coin at once",
        "description": "FOMO's coordinated-flow board: several DISTINCT traders hitting the same coin inside a short window, with the names. Each event carries side, token, windowMinutes, uniqueTraders, trades, volumeUsd, priceChangePercent, marketCapUsd and traders[]. This is not the same data as /v2/alerts, which carries large_buy, large_sell and thesis_created and no multi-user events at all. CAVEAT: the board is FOMO's and FOMO stopped publishing to it on 2026-08-23, so every response carries newestEventAt, newestEventAgeHours and, once past six hours, stale:true and a note. For live flow use /v2/alerts or WSS /ws/alerts. 250 credits.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1-100, default 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "side",
            "in": "query",
            "required": false,
            "description": "buy or sell.",
            "schema": {
              "type": "string",
              "enum": [
                "buy",
                "sell"
              ]
            }
          },
          {
            "name": "minTraders",
            "in": "query",
            "required": false,
            "description": "Only events where at least this many distinct traders took part.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ count, events: [...], hasNextPage, newestEventAt, newestEventAgeHours, stale, note }"
          },
          "503": {
            "description": "The live read did not finish in time. retryable is true; ask again in a moment."
          },
          "402": {
            "description": "Out of credits. Upgrade by card (renews monthly, and the listed price is the total - tax is included, not added at checkout) or top up in USDC on Solana. See https://fomoapi.io/pricing"
          }
        }
      }
    },
    "/v2/trades/{tradeId}/comments": {
      "get": {
        "tags": [
          "trades"
        ],
        "summary": "The thesis thread on one trade",
        "description": "Every comment on one trade: text, author userId, likes, createdAt, parentId (set on a reply, null on a top-level thesis) and any links the author embedded, plus FOMO's own olderThesisCount and newerThesisCount for how many theses that trader wrote on the position before and after. We print replies:N in several payloads and until now there was no way to read them. Authors arrive as a bare userId with no handle; resolve one with GET /v2/users/id/{userId}. 250 credits.",
        "parameters": [
          {
            "name": "tradeId",
            "in": "path",
            "required": true,
            "description": "Trade id, from /v2/users/{handle}/trades or /v2/users/{handle}/spotlight.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1-200, default 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ tradeId, count, comments: [...], hasNextPage, note }"
          },
          "503": {
            "description": "The live read did not finish in time. retryable is true; ask again in a moment."
          },
          "402": {
            "description": "Out of credits. Upgrade by card (renews monthly, and the listed price is the total - tax is included, not added at checkout) or top up in USDC on Solana. See https://fomoapi.io/pricing"
          }
        }
      }
    },
    "/v2/users/{handle}/swaps": {
      "get": {
        "summary": "A trader's individual fills (swaps)",
        "description": "Individual fills, newest first. Distinct from /trades, which returns positions with an averaged entry and exit. One call returns 100 fills and a nextCursor; pass it back as ?cursor= to page older fills, and keep going while nextCursor is not null. tradeIdIn/tradeIdOut join each fill back to its position. 250 credits.",
        "parameters": [
          {
            "$ref": "#/components/parameters/handleOrUserId"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Page older results. On /trades and /positions start with cursor=start, then pass each response's nextCursor back here while it is not null (25 closed trades per page). On /swaps the first call already returns a nextCursor (100 fills per page). Billed 250 credits per page."
          }
        ],
        "responses": {
          "200": {
            "description": "Swaps PAGING: one call returns 100 fills plus a nextCursor. Pass it back as ?cursor= and keep going while nextCursor is not null (measured 500 fills over 5 pages, no duplicates, 250 credits per page). ?tokenAddress= scopes a fresh window to one token, and ?deep=1 accumulates across calls."
          }
        }
      }
    },
    "/v2/users/{handle}/positions": {
      "get": {
        "tags": [
          "traders"
        ],
        "summary": "A trader's positions (clearer name for /trades)",
        "description": "Identical to /v2/users/{handle}/trades: positions, a token aggregated into one row with an averaged entry and exit. /trades remains as the legacy alias.",
        "parameters": [
          {
            "$ref": "#/components/parameters/handleOrUserId"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "deep",
            "in": "query",
            "required": false,
            "description": "1 (or true) for the full fan-out, or N to buy only N upstream calls. Each call that answers costs 250 credits. Omit for the ordinary single-call behaviour at 250 credits.",
            "schema": {
              "type": "string",
              "example": "1"
            }
          },
          {
            "$ref": "#/components/parameters/tradeStatus"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Page older results. On /trades and /positions start with cursor=start, then pass each response's nextCursor back here while it is not null (25 closed trades per page). On /swaps the first call already returns a nextCursor (100 fills per page). Billed 250 credits per page."
          }
        ],
        "responses": {
          "200": {
            "description": "{ key, kind, count, trades: [...] }. With ?deep also closedTotalOnFomo (FOMO's real total), perChainClosedTotal, upstreamCalls, partial (some chains did not answer in time; you were only charged for the ones that did) and complete:false. Each row carries the full position economics: costBasisUsd, boughtAmount (a real buy), soldAmount, transferredInAmount (tokens received, not purchased), transferredOutAmount, avgTransferInPrice, avgTransferOutPrice, priceUsd (current) and userId. amount reconciles as boughtAmount - soldAmount + transferredInAmount - transferredOutAmount. avgEntryPrice is null EXACTLY when the position was never bought: measured across 150 positions on 6 traders, all 76 null-entry rows had boughtAmount 0, so a null means the tokens were received rather than that the data is missing. Where there is a buy, costBasisUsd / boughtAmount gives entry."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key as a Bearer token. REQUIRED for all data endpoints (trader resolution, trades, balances, holders, theses, search, notifications) - they return 401 without one. Keyless works only for /v1, /health and /api (discovery only); the leaderboard and /v2/alerts were part of a public showcase that is now closed. The WebSocket is realtime on paid plans (?key=...); a free key gets 7 days of realtime then 15s-delayed; keyless is 60s-delayed. Billing is usage-based credits (a normal call is 250 credits, a thesis 1,250, a wallet resolution 2,500; leaderboard 1, /v2/alerts 0.5); out of credits returns 402. Get a free key (250,000 credits/month) at https://fomoapi.io/dashboard, or buy a plan by card at https://fomoapi.io/pricing - no account needed."
      }
    },
    "parameters": {
      "handle": {
        "name": "handle",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "trader handle, case-insensitive, optional leading @"
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      },
      "q": {
        "name": "q",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "search query"
      },
      "leaderboardLimit": {
        "name": "limit",
        "in": "query",
        "description": "Rows to return. FOMO's own ceiling on this board, measured per window: 150 on 24h/7d/30d, 100 on all. Higher values clamp. Billed 250 credits per call at any limit.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 150,
          "default": 150
        }
      },
      "tradeStatus": {
        "name": "status",
        "in": "query",
        "description": "Narrow to open or closed trades. Opens sort ahead of closed and limit caps at 200, so a trader with 200+ open positions fills the page and closed trades are cut. Use status=closed to read closed history.",
        "schema": {
          "type": "string",
          "enum": [
            "open",
            "closed",
            "all"
          ]
        }
      },
      "handleOrUserId": {
        "name": "handle",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "trader handle (case-insensitive, optional leading @) OR the trader's FOMO userId, a UUID such as 254245a7-575a-51be-9bc3-090a924789eb. Both forms return identical data. Prefer the userId if you store one: a handle can be renamed by its owner, and the id cannot. The id form is also marginally faster, because the handle form spends a lookup arriving at exactly that id. Same credit cost either way. Get a userId from /v2/leaderboard/{window}, /v2/search, /v2/alerts or the /ws/alerts stream."
      }
    }
  }
}