> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fomoscan.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# List the traders a trader follows

> The traders this trader follows, as trader envelopes, with the platform of each edge and the time a crawl last saw it.

- The list covers every account of the trader on a platform with a follow graph (`followGraph` in `GET /v3/capabilities`).
- One item per edge: a trader on the other side of two platforms appears twice, once per platform.
- An edge between two accounts of the same trader is left out, so a merge never makes a trader follow itself.
- `counts` holds, per platform, the platform's own count (`reported`) and the edges we hold (`stored`). Every page carries it.
- Order: `observedAt` newest first, then the account ids. A page boundary inside a tie neither drops nor repeats an edge.
- Paged with `cursor` and `limit` (default 20, maximum 100). Follow `nextCursor` to read older rows.

## Price

**200 credits per started 20 rows of `limit`**, on every call. A short or empty page costs the same as a full one.

| Answer | Credits | Reveals |
| --- | --- | --- |
| `limit` 1. | 200 | 0 |
| `limit` 20 (the default). | 200 | 0 |
| `limit` 50. | 600 | 0 |
| `limit` 100 (the maximum). | 1,000 | 0 |
| 404: no trader has this id. | 200 | 0 |

## Limits

- 2,000 requests per minute from one address on this operation, beside the per-minute limit of the key.
- A key with less than 200 credits left gets 402 `QUOTA_EXCEEDED` before any work.



## OpenAPI

````yaml https://api.fomoscan.sh/v3/openapi.json get /v3/traders/{traderId}/following
openapi: 3.1.0
info:
  title: fomoscan API
  version: 3.0.0
  description: >-
    Coming from v2 (`https://api.fomoscan.sh/v2`)? The section Moving from v2,
    at the end of this introduction, maps each v2 route to its v3 operation and
    price and lists what changed.


    Social graph, post and wallet data for trading products, in one shape for
    every platform. Every price is in credit units.


    ## Objects


    Every object is an envelope:


    ```json

    { "kind": "trader", "id": "tdr_01jbst8pvcfp79y0938nkrkayd", "canonical": { …
    }, "sources": { "<platform>": { "profile": { … } } } }

    ```


    | Field | Meaning |

    | --- | --- |

    | `kind` | `trader`, `wallet`, `post`, `trade`, `token`, `group`. The only
    field to switch on. Skip kinds you do not know. |

    | `id` | Our id of the object. Store it; see Ids. |

    | `canonical` | Fields that mean the same on every platform. |

    | `sources` | Capability blocks keyed by platform. A block has the same
    shape on every platform that provides it and carries its own `observedAt`. |


    What `sources` tells you:


    | On the wire | Meaning |

    | --- | --- |

    | `sources.<platform>` absent | The object has no side on that platform. |

    | `sources.<platform>.<block>` absent | The platform cannot provide the
    block (`GET /v3/capabilities` lists which can), or we hold no reading of it
    yet. |

    | A block field is `null` | The platform left it empty at `observedAt`, or
    never fills it (`unfilled` in the capability table). |

    | `{ "status": "unavailable", "retryable": true }` in place of a block | We
    could not read the block for this call. The rest of the object is current. |


    PnL, boards, rankings, follow lists, the key and the capability table are
    documents about a subject. A document holds envelopes wherever a subject
    appears.


    ## Ids


    | Kind | Id |

    | --- | --- |

    | `trader` | `tdr_` and 26 lowercase base32 characters |

    | `wallet` | `wal_<family>_<address>`, family `svm` or `evm` |

    | `post` | `pst_` and 26 lowercase base32 characters |

    | `trade` | `trd_` and 26 lowercase base32 characters |

    | `token` | `tok_<network>_<address>` |

    | `group` | `grp_` and 26 lowercase base32 characters |


    - A malformed id or a wrong prefix answers 400 `INVALID_ARGUMENT` before any
    read.

    - A platform's own ids never appear in a response. Find a trader once, by
    handle (`GET /v3/traders/lookup`) or by wallet (`GET
    /v3/wallets/{family}/{address}`), then keep our id.

    - An id merged into another trader answers 200 with the trader that absorbed
    it, whose `canonical.mergedFrom` lists the old id.

    - List cursors start with `c_`, stream cursors with `s_`. Neither works in
    the place of the other.


    ## Money and time


    - Money and token quantities are `Amount` objects, never JSON numbers. `{
    "value": "12345678", "decimals": 6, "currency": "USD" }` is 12.345678 USD.

    - `value` is an integer in base units, as a string, and negative below zero.
    Read it with a decimal or big integer type: a float loses digits.

    - `currency` is `USD`, or the token id of a token quantity
    (`tok_solana_<mint>`).

    - `priceAsOf` is present when we converted a value with a price, and holds
    the time of that price.

    - Ratios (`returnPct`, `multiple`) and counts are JSON numbers.

    - Times in bodies are RFC3339 in UTC with milliseconds. A time in a query
    parameter (`at`) is RFC3339 with `Z` or an offset.


    ## Paging


    A list answers one page:


    ```json

    { "items": [ … ], "hasMore": true, "nextCursor": "c_…" }

    ```


    - Send `nextCursor` back as `cursor` for the next page. Stop when `hasMore`
    is `false`.

    - A cursor is opaque and belongs to the query that produced it. Sent with
    other parameters, it answers 400 `INVALID_ARGUMENT`.

    - `limit` defaults to 50, at most 200. Post feeds, follow lists and trade
    lists default to 20, at most 100.

    - A page boundary inside a tie of the order neither drops nor repeats a row.

    - The entries of one board or ranking arrive whole, without paging.


    ## Errors


    Every error answers the same body:


    ```json

    { "error": { "type": "invalid_request", "code": "NOT_FOUND", "message":
    "Trader not found.", "requestId": "req_…" } }

    ```


    | HTTP | `type` | `code` | When |

    | --- | --- | --- | --- |

    | 400 | `invalid_request` | `INVALID_ARGUMENT` | A parameter, id, cursor or
    body is malformed, or the operation does not take it. |

    | 401 | `authentication` | `UNAUTHENTICATED` | No key, or the key is
    unknown, revoked or expired. |

    | 402 | `quota` | `QUOTA_EXCEEDED` | The credit balance cannot pay for the
    call. |

    | 402 | `quota` | `REVEAL_LIMIT_REACHED` | The answer needs a reveal and the
    key has used every reveal this month. |

    | 403 | `permission` | `PERMISSION_DENIED` | The key may not make the call:
    it has no REST scope, its plan is switched off, or a free key asked for a
    reveal. |

    | 403 | `permission` | `PLAN_REQUIRED` | The plan of the key does not
    include the stream. REST calls never answer it. |

    | 404 | `invalid_request` | `NOT_FOUND` | The subject of the call does not
    exist, or no route has this method and path. |

    | 429 | `rate_limit` | `RATE_LIMITED` | Too many requests from the key or
    the address. Wait `Retry-After` seconds. |

    | 500 | `internal` | `INTERNAL` | The call failed on our side. Retry with
    backoff, and quote `requestId` if it repeats. |

    | 503 | `overloaded` | `OVERLOADED` | The API holds too many requests at
    once. Wait `Retry-After` seconds. |


    - Act on `code`. `message` is for people and can change.

    - 429 and 503 carry `Retry-After` in seconds.

    - A query parameter the operation does not list answers 400.


    ## Requests


    - Send the key in `Authorization: Bearer <key>` or in `X-Api-Key: <key>`. A
    key in the query string does not count.

    - Every response carries `X-Request-Id`, the id of the request in our logs.
    Quote it when you write to us.

    - Every response carries `Cache-Control: private, no-store`.

    - A request body is JSON sent with `Content-Type: application/json`. A body
    with another content type, or with none, answers 400.

    - `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` report the
    per-minute window of the key. A key without a limit gets none of them.


    ## Compatibility


    - Inside v3 every change is additive: a new optional field, kind, platform
    key or block. Skip what you do not know.

    - Removing, renaming or retyping a field needs a new major version.


    ## Servers


    REST calls go to `https://api.fomoscan.sh`. The stream lives on its own
    host; see the WebSocket section.


    ## Credits and reveals


    - A call that answers takes the credits of the table below from the key's
    monthly balance; a `free` operation takes none. `GET /v3/me` shows the
    balance.

    - A refusal (400, 401, 402, 403, 429, 503) costs nothing. A 404 costs what
    the operation's Price table says.

    - A reveal is one unit of the key's monthly reveal allowance. Operations
    that show who owns a wallet or what a person earned take them, as the
    Reveals column says.

    - A metered key that cannot pay for an answer gets 402 `QUOTA_EXCEEDED` in
    its place. It is never charged past its balance.

    - Each operation lists its prices in full under Price.


    | Operation | Credits | Reveals |

    | --- | --- | --- |

    | `GET /v3/traders/{traderId}` | 100 hit, 200 miss | none |

    | `GET /v3/traders/lookup` | 100 hit, 200 miss | none |

    | `GET /v3/wallets/{family}/{address}` | 30,000 hit, 200 miss, 10 flat | one
    per hit |

    | `GET /v3/traders/{traderId}/wallets` | 100 hit, 200 miss | one when the
    page lists a wallet |

    | `GET /v3/traders/{traderId}/pnl` | 30,000 hit, 200 miss | one per hit |

    | `POST /v3/traders/pnl` | 30,000 per `ready` item, 200 per other item | one
    per `ready` item |

    | `GET /v3/posts` | 200 per started 20 rows of `limit`; 10 flat with
    `platform=pump` | none |

    | `GET /v3/tokens/{network}/{address}/posts` | 200 per started 20 rows of
    `limit`; 10 flat with `platform=pump` | none |

    | `GET /v3/traders/{traderId}/posts` | 200 per started 20 rows of `limit`;
    10 flat with `platform=pump` | none |

    | `GET /v3/tokens/{network}/{address}` | 100 hit, 100 miss | none |

    | `GET /v3/leaderboards` | free | none |

    | `GET /v3/leaderboards/{platform}/{board}` | 500 hit, 200 miss, 10 flat |
    none |

    | `GET /v3/rankings` | free | none |

    | `GET /v3/rankings/{key}` | 500 hit, 200 miss | none |

    | `GET /v3/me` | free | none |

    | `GET /v3/capabilities` | free | none |

    | `GET /v3` (WebSocket) | 20,000 per started minute per connection | none |

    | `GET /v3/traders/{traderId}/followers` | 200 per started 20 rows of
    `limit` | none |

    | `GET /v3/traders/{traderId}/following` | 200 per started 20 rows of
    `limit` | none |

    | `GET /v3/trades` | 200 per started 20 rows of `limit`, plus 100 per trader
    shown | one per trader shown |

    | `GET /v3/traders/{traderId}/trades` | 200 per started 20 rows of `limit`,
    plus 100 per trader shown | one per trader shown |

    | `GET /v3/wallets/{family}/{address}/trades` | 200 per started 20 rows of
    `limit` | none |

    | `GET /v3/wallets/{family}/{address}/pnl` | 500 hit, 200 miss | none |

    | `POST /v3/wallets/pnl` | 500 per `ready` item, 200 per other item | none |


    ## Rate limits


    | Limit | Value | Past it |

    | --- | --- | --- |

    | Per key | The key's `ratePerMinute` (`GET /v3/me`), reported in the
    `RateLimit-*` headers. | 429 `RATE_LIMITED` with `Retry-After`. |

    | Per address, before the key is checked | 2,400 requests per minute. | 429
    `RATE_LIMITED` with `Retry-After`. |

    | Per address and operation | Requests per minute: 2,000 on most operations;
    120 on `POST /v3/traders/pnl`; 120 on `GET /v3/me`; 120 on `POST
    /v3/wallets/pnl`; 600 on `GET /v3/leaderboards/{platform}/{board}`; 600 on
    `GET /v3/rankings/{key}`. | 429 `RATE_LIMITED` with `Retry-After`. |

    | Requests in flight at once on one API instance, across every key | 30
    requests. | 503 `OVERLOADED` with `Retry-After: 1`, before the key is
    checked. |

    | Requests in flight at once on one API instance, for one key and the keys
    under it | 15 requests. | 429 `RATE_LIMITED` with `Retry-After: 1`, after
    the key is checked. |


    A refused request costs nothing. Retry after the seconds in `Retry-After`.


    ## Live posts


    Live posts arrive over one WebSocket on the stream host. The WebSocket
    section at the end of this page says how to connect, what it costs and how
    to behave on it.


    ## Moving from v2


    v2 (`https://api.fomoscan.sh/v2`) and v3 take the same key and spend from
    the same balance. Most v3 operations cost what the v2 route they replace
    costs; the table under Routes and prices lists both.


    ### At a glance


    |  | v2 | v3 |

    | --- | --- | --- |

    | REST | `https://api.fomoscan.sh/v2/…` | `https://api.fomoscan.sh/v3/…` |

    | Reference | `https://api.fomoscan.sh/docs`, `https://docs.fomoscan.sh` |
    `https://api.fomoscan.sh/v3/docs`, `https://api.fomoscan.sh/v3/openapi.json`
    |

    | Stream | `wss://api.fomoscan.sh/v2/ws`, `wss://api.fomoscan.sh/v2/ws/pump`
    | `wss://stream.fomoscan.sh/v3`, one socket for every platform |

    | Key | `fsk_live_…`, `fsk_test_…` | the same key |

    | Headers | `Authorization: Bearer <key>` or `X-Api-Key: <key>` | the same;
    a key in the query string does not count |

    | Answers | flat JSON records, USD as numbers, epoch milliseconds |
    envelopes and documents, money as integer strings, RFC3339 times |


    ### Keys, plans and credits


    - Your v2 key works on v3. A v3 call spends from the monthly credits, top-up
    and reveal cap a v2 call spends from, so `GET /v2/me` and `GET /v3/me`
    report one balance.

    - Scopes are the v2 scopes: `basic` covers REST and the stream, `http:read`
    REST only and `live:read` the stream only.

    - The stream takes the plans it takes on v2; the WebSocket section lists
    them.

    - A key on a free plan (`pilot` or `explore`) without reveals gets 403
    `PERMISSION_DENIED` for every answer that takes a reveal. v2 let such keys
    reveal a fixed sample of handles and wallets; v3 has no sample set.

    - A refusal (400, 401, 402, 403, 429, 503) costs nothing. A 404 costs the
    operation's miss price, as on v2.


    ### Routes and prices


    Credits per call. A reveal is one unit of the key's monthly reveal cap. Each
    v3 operation lists every answer and its price under Price.


    | v2 | v2 credits | v3 | v3 credits |

    | --- | --- | --- | --- |

    | `GET /v2/user/handle/{handle}` | 100 and a reveal when a wallet is
    returned; 200 not found | `GET /v3/traders/lookup?handle=`, then `GET
    /v3/traders/{traderId}/wallets` | 100, 200 with no match; then 100, and a
    reveal when the page lists a wallet |

    | `GET /v2/user/handle/{handle}/profile` | 100; 200 not found | `GET
    /v3/traders/lookup?handle=` | 100; 200 with no match |

    | `GET /v2/user/id/{id}`, `GET /v2/user/id/{id}/profile` | 100; 200 not
    found | not in v3 yet |  |

    | `GET /v2/user/wallet/{address}`, `GET /v2/pump/user/wallet/{wallet}` |
    30,000 and a reveal, 200 not found; the `/v2/pump/` route 10 | `GET
    /v3/wallets/{family}/{address}` | 30,000 and a reveal; 10 when every owner
    holds tier `account`; 200 without an owner |

    | `POST /v2/user/handle/{handle}/resolve` | 30,000 and a reveal; 200 on a
    202 or 404 | `GET /v3/traders/{traderId}/wallets`, stored wallets only |
    100, and a reveal when the page lists a wallet; 200 for an unknown trader |

    | `GET /v2/user/handle/{handle}/pnl` | 30,000 and a reveal when `ready`; 200
    otherwise | `GET /v3/traders/{traderId}/pnl` | 30,000 and a reveal when
    `ready`; 200 otherwise |

    | `POST /v2/user/handles/pnl` | per handle: 30,000 and a reveal when
    `ready`, 200 otherwise | `POST /v3/traders/pnl` | per id: 30,000 and a
    reveal when `ready`, 200 otherwise |

    | `GET /v2/pump/user/wallet/{wallet}/pnl` | 500 when `ready`; 200 otherwise
    | `GET /v3/wallets/svm/{address}/pnl` | 500 when `ready`; 200 otherwise |

    | `POST /v2/pump/user/wallets/pnl` | per wallet: 500 when `ready`, 200
    otherwise | `POST /v3/wallets/pnl` | per wallet: 500 when `ready`, 200
    otherwise |

    | `GET /v2/user/handle/{handle}/holdings`, `POST /v2/user/handles/holdings`
    | 2,500; 1,250 per handle; allow-listed keys only | allow-listed keys only;
    v3 keeps its own list | 2,500; 1,250 per trader |

    | `GET /v2/pump/user/wallet/{wallet}/holdings`, `POST
    /v2/pump/user/wallets/holdings` | 500 per wallet; allow-listed keys only |
    not in v3 yet |  |

    | `GET /v2/thesis` | 200 per started 20 rows of `limit` | `GET /v3/posts` |
    200 per started 20 rows of `limit` |

    | `GET /v2/thesis/token/{tokenAddress}` | 200 per started 20 rows of `limit`
    | `GET /v3/tokens/{network}/{address}/posts` | 200 per started 20 rows of
    `limit` |

    | `GET /v2/thesis/user/{id}`, `GET
    /v2/thesis/user/{id}/token/{tokenAddress}` | 200 per started 20 rows of
    `limit` | `GET /v3/traders/{traderId}/posts`, with `token=tok_…` for one
    token | 200 per started 20 rows of `limit`; 200 for an unknown trader |

    | `GET /v2/pump/thesis` | 10 for a page of 25 | `GET
    /v3/posts?platform=pump` | 10 for a page of up to 100 |

    | `GET /v2/pump/thesis/token/{mint}` | 10 for a page of 25 | `GET
    /v3/tokens/solana/{address}/posts?platform=pump` | 10 for a page of up to
    100 |

    | `GET /v2/pump/thesis/wallet/{wallet}` | 10 for a page of 25 | `GET
    /v3/wallets/{family}/{address}` once, then `GET
    /v3/traders/{traderId}/posts?platform=pump` | the wallet lookup once, then
    10 for a page of up to 100 |

    | `GET /v2/token/{address}` | 100; 200 not found | `GET
    /v3/tokens/{network}/{address}` | 100; 100 not found |

    | `GET /v2/leaderboard/traders`, `GET /v2/leaderboard/clans` | 500 | `GET
    /v3/leaderboards/fomo/traders`, `GET /v3/leaderboards/fomo/clans` | 500; 200
    without a capture |

    | `GET /v2/leaderboard/tokens/most-held`, `GET
    /v2/leaderboard/tokens/trending`, `GET /v2/leaderboard/tokens/graduated` |
    10 | `GET /v3/leaderboards/fomo/tokens_most_held`, `GET
    /v3/leaderboards/fomo/tokens_trending`, `GET
    /v3/leaderboards/fomo/tokens_graduated` | 10 |

    | `GET /v2/pump/leaderboard/traders` | 10 | `GET
    /v3/leaderboards/pump/traders` | 10 |

    | `GET /v2/leaderboard/traders-fomoscan` | 500 | `GET
    /v3/rankings/traders_net_flow_<window>` | 500; 200 without a snapshot |

    | `GET /v2/me` | free | `GET /v3/me` | free |

    | `GET /v2/ws`, `GET /v2/ws/pump` | 20,000 per minute per socket |
    `wss://stream.fomoscan.sh/v3` | 20,000 per started minute per connection |

    | `POST /v2/user/handle/{handle}/reveal`, `GET /v2/stats/…` | used by the v2
    MCP server | not in v3 yet |  |

    | `GET /feed/trades` | not metered | `GET /v3/trades` | 200 per started 20
    rows of `limit`, plus 100 and a reveal per trader the page shows |

    | `GET /feed/trader/{handle}` | not metered | `GET
    /v3/traders/lookup?handle=`, then `GET /v3/traders/{traderId}`, `GET
    /v3/traders/{traderId}/wallets` and `GET /v3/traders/{traderId}/trades` |
    each at its own price |

    | `GET /get-followers/{handle}`, `GET /get-following/{handle}` | not metered
    | `GET /v3/traders/{traderId}/followers`, `GET
    /v3/traders/{traderId}/following` | 200 per started 20 rows of `limit` |

    | `GET /get-user/{handle}`, `GET /get-user-by-id/{id}`, `GET /search` | not
    metered | `GET /v3/traders/lookup?handle=` for a whole handle; nothing for
    an id or a prefix | 100; 200 with no match |


    New in v3, with no v2 route:


    - `GET /v3/traders/{traderId}`: 100; 200 for an unknown id.

    - `GET /v3/leaderboards`: free.

    - `GET /v3/rankings`: free.

    - `GET /v3/capabilities`: free.

    - `GET /v3/traders/{traderId}/trades`: 200 per started 20 rows of `limit`,
    plus 100 and a reveal per trader the page shows.

    - `GET /v3/wallets/{family}/{address}/trades`: 200 per started 20 rows of
    `limit`.


    ### Ids and lookups


    - v3 never returns a platform's own id, such as v2's `id` and `authorId`.
    Every object carries our id, in the forms the Ids section at the top lists.

    - v3 has no lookup by a v2 `id`. Find each trader once by handle (`GET
    /v3/traders/lookup`) or by wallet (`GET /v3/wallets/{family}/{address}`),
    store the `tdr_` id, and call by it from then on:


    ```bash

    curl -s -H "Authorization: Bearer $KEY"
    "https://api.fomoscan.sh/v3/traders/lookup?handle=tradername"

    curl -s -H "Authorization: Bearer $KEY"
    "https://api.fomoscan.sh/v3/traders/tdr_01jbst8pvcfp79y0938nkrkayd/wallets"

    ```


    ```json

    { "items": [ { "kind": "trader", "id": "tdr_01jbst8pvcfp79y0938nkrkayd",
    "canonical": { … }, "sources": { … } } ], "hasMore": false, "nextCursor":
    null }

    ```


    - A handle can match traders on several platforms. The lookup lists current
    handles first, then handles an account held in the last 30 days; `platform=`
    narrows it to one platform.

    - A trader merged into another answers 200 with the trader that absorbed it,
    and `canonical.mergedFrom` lists the old id. Store the id you get back.

    - Wallet paths name the address family: `/v2/user/wallet/{address}` is
    `/v3/wallets/svm/{address}` or `/v3/wallets/evm/{address}`. EVM addresses
    come back lowercase.

    - Token paths and ids name the network: `/v3/tokens/{network}/{address}`,
    `tok_<network>_<address>`, with `network` one of `solana`, `ethereum`,
    `base`, `bsc`, `arbitrum`, `optimism`, `polygon`, `robinhood`, `hyperevm`,
    `monad`, `arc`, `xlayer`, `ink` or `hyperliquid`. v2 inferred the chain from
    the address.


    ### Answers


    - Every object is an envelope (`kind`, `id`, `canonical`, `sources`); the
    Objects section at the top explains it. PnL, boards, rankings and the key
    are documents that hold envelopes.

    - Money: v2 sent USD as JSON numbers (`"netUsd": 1430.12`). v3 sends an
    `Amount`: `{ "value": "1430120000", "decimals": 6, "currency": "USD" }`.
    Read `value` as a big integer or decimal and shift it by `decimals`; a float
    loses digits. Token prices carry 18 decimals.

    - Time: v2 sent and took epoch milliseconds (`fomoCreatedAt`, `updatedAt`,
    `capturedAt`, `?at=`). v3 sends RFC3339 in UTC with milliseconds
    (`2026-11-02T09:30:00.000Z`) and takes RFC3339 in `at`; epoch milliseconds
    in `at` answer 400.

    - Nulls: v2 used `null` for a missing value. v3 tells four cases apart: no
    `sources.<platform>` key (no side on that platform), no block (the platform
    cannot provide it, or we hold no reading), a `null` field (empty when
    observed), and `{ "status": "unavailable", "retryable": true }` in place of
    a block (we could not read it for this call).

    - Paging: v2 pages took `before=<id of the last item>` and answered `count`,
    `hasMore`, `nextBefore`. v3 lists answer `{ "items", "hasMore", "nextCursor"
    }`; send `nextCursor` back as `cursor`. A cursor works only with the
    parameters that produced it. Post feeds keep `limit` from 1 to 100, 20 by
    default.

    - v2 ignored a query parameter it did not know. v3 answers 400
    `INVALID_ARGUMENT`. `HEAD` answers 404.


    ### Errors


    v2: `{ "error": { "code", "message", "retryAfter" } }`. v3: `{ "error": {
    "type", "code", "message", "requestId" } }`, with `X-Request-Id` on every
    response and `Retry-After` on 429 and 503.


    | v2 | v3 |

    | --- | --- |

    | 400 `VALIDATION_ERROR` | `INVALID_ARGUMENT` |

    | 401 `UNAUTHORIZED` | `UNAUTHENTICATED` |

    | 402 `QUOTA_EXCEEDED`, `REVEAL_LIMIT_REACHED` | the same codes |

    | 403 `FORBIDDEN_SCOPE`, `FREE_TIER_DISABLED`, `SAMPLE_SET_ONLY`,
    `FORBIDDEN` | `PERMISSION_DENIED` |

    | 403 `WEBSOCKET_PLAN_REQUIRED` | `PLAN_REQUIRED` |

    | 404 `NOT_FOUND` | `NOT_FOUND` |

    | 429 `RATE_LIMITED`, `retryAfter` in the body | `RATE_LIMITED`,
    `Retry-After` header |

    | 500 `INTERNAL_ERROR` | `INTERNAL` |

    | 503 `OVERLOADED`, `B2B_DISABLED`, `DATA_UNAVAILABLE`; 504 `TIMEOUT` | 503
    `OVERLOADED`, `Retry-After` header |

    | 202 `{ "pending": true }` from resolve | none |


    ### Fields


    #### User record


    - `id` (the platform user id): none. Use the trader `id` (`tdr_`).

    - `handle`, `name`, `bio`, `profilePicture`: `handle`, `name`, `bio`,
    `avatarUrl` of the `profile` block in `sources.<platform>`.
    `canonical.display` repeats the newest of them.

    - `twitter` (a URL): `profile.xHandle`, the handle without `@`.

    - `followers`, `following`: the `followGraph` block.

    - `solanaAddress`, `evmAddress`: `GET /v3/traders/{traderId}/wallets`,
    wallet envelopes with `canonical.family`, `canonical.address` and the `tier`
    of each owner.

    - Handle suggestions: a missed lookup answers `suggestions`; `match=closest`
    answers the closest trader. Prefixes match through suggestions; there is no
    `/search`.

    - `walletAvailable`: `canonical.walletAvailable` on `GET
    /v3/traders/{traderId}` and `GET /v3/traders/lookup`, at no extra price and
    without a reveal.

    - `banner`, `isPumpUser`, `groupBadges`: none.

    - Wallet lookup: v2 answered one user record with the wallet on the other
    chain. `GET /v3/wallets/{family}/{address}` answers the wallet with every
    owner and a `tier` each (`proven`, `account`, `partial`, `weak`,
    `unverified`); `GET /v3/traders/{traderId}` reads an owner.


    #### PnL


    - The path takes the `tdr_` id in place of the handle. The batch body takes
    `traderIds` in place of `handles` and answers `{ "items": [ { "trader",
    "found", "pnl" } ] }` in request order, in place of `count`, `missing` and
    `entries`.

    - `status` `no-wallet` is `no_wallet`.

    - `wallet`, `evmWallet`: `wallets`, a list of `wal_` ids, filled only when
    `status` is `ready`.

    - `handle`, `displayName`, `avatarUrl`, `twitter`, `followers`: none. Read
    the trader.

    - In each window: `netUsd` is `realized`, `volumeUsd` is `volume`,
    `buyVolumeUsd` and `sellVolumeUsd` are `buyVolume` and `sellVolume`,
    `trades` is `tradeCount`, `updatedAt` is `asOf`, and `rank` is `{
    "position", "of", "ranking" }`. `origin` says where the figures come from:
    `provider` on `24h`, `7d` and `30d`, `computed` on `all`.

    - `unrealized.unrealizedPnlUsd` and `holdingsUsd` are `unrealized.value` and
    `holdingsValue`.

    - Per window, `unrealizedPnlUsd` and `totalPnlUsd` are `unrealized` and
    `total`; `approximate`, `stale` and `nextUpdateAt` keep their v2 names.

    - Wallet PnL (`GET /v3/wallets/{family}/{address}/pnl`) lists the paired
    wallet of the other family in `wallets`; `boards` replaces `pumpBoard`.
    `POST /v3/wallets/pnl` takes up to 100 wallets.

    - `all` holds the net flow and rank we compute from our trades. v2 mixed
    cash flow with whole-wallet figures since 1 January 2025.

    - `window` (new) narrows `windows` to one key at the same price.


    #### Posts


    Theses, callouts and stream items are one kind, `post`:


    - `thesis`: `canonical.body`. `segments`: `canonical.segments`, each `{
    "text", "url" }`.

    - `tokenAddress` and `tokenNetwork`, callout `coinMint` and `chain`:
    `canonical.token.id` (`tok_<network>_<address>`). `tokenSymbol`,
    `coinSymbol`: `canonical.token.symbol`.

    - `authorId` and `authorHandle`, callout `wallet` and `userName`:
    `canonical.author.trader` and `canonical.author.handle`.

    - `fomoCreatedAt`, callout `createdAt`: `canonical.postedAt`. `firstSeenAt`
    (new) is when we first read the post.

    - `likeCount`, `likes`: `canonical.likeCount`. `commentCount`, `replyCount`,
    `viewCount`: the `engagement` block.

    - `holdingsUsd`, `tokenAmount`, `authorTradeUsd`, `realizedPnlUsd`,
    `unrealizedPnlUsd`, `pnl`, `closedAt`, callout `position`: the `position`
    block (`value`, `quantity`, `invested`, `realizedPnl`, `unrealizedPnl`,
    `totalPnl`, `closed`, `closedAt`).

    - `calledOutAtMcap`, `multiple`, `maxMultiplier`, `mediaUrl`: the
    `callPerformance` block (`marketCapAtCall`, `multiple`, `maxMultiple`,
    `mediaUrl`).

    - Thesis `priceUsdAtPost` and `marketCapAtPost`:
    `callPerformance.priceAtCall` and `callPerformance.marketCapAtCall`.

    - `thesisHash`: none. Key on `id`.


    #### Tokens


    - `GET /v2/token/{address}`: `GET /v3/tokens/{network}/{address}`. The
    `market` block per platform carries `price`, `marketCap`, `liquidity`,
    `volume24h`, `holders` and `priceChange24h` (percent). A token without
    platform market data is read live, as on v2, into `canonical.market`.


    #### Boards and rankings


    - `/v2/leaderboard/<board>` and `/v2/pump/leaderboard/traders` are
    `/v3/leaderboards/{platform}/{board}`; `GET /v3/leaderboards` lists every
    board with its windows.

    - `window` stays. `period` `daily`, `weekly`, `monthly` is `window` `24h`,
    `7d`, `30d`. `at` takes RFC3339 in place of epoch milliseconds.

    - `count` is `entryCount`. An entry is `{ "rank", "subject", "measures" }`:
    `subject` is the trader, token or group envelope in place of `id`, `handle`,
    `label` and `avatarUrl`. `measures` holds the figures under snake-case keys,
    money as amounts: `marketCap` is `market_cap`, `numTrades` is `trade_count`,
    `memberCount` is `members`.

    - `/v2/leaderboard/traders-fomoscan?window=&order=` is
    `/v3/rankings/traders_net_flow_<window>?order=`. Tied traders share a rank,
    and `order=worst` answers the bottom entries with their own ranks, where v2
    numbered them from 1.


    #### Account


    - `GET /v3/me` moves `entitlement` and `usage` into `api`: `monthlyUnits`,
    `additionalUnits`, `unitsUsed`, `unitsRemaining`, `ratePerMinute`,
    `monthlyReveals`, `revealsUsed`, `revealsRemaining`, `unmetered`. Stream
    minutes count in `api.unitsUsed`.


    ### Limits and refusals


    |  | v2 | v3 |

    | --- | --- | --- |

    | Per key | `ratePerMinute`, a fixed minute | the same `ratePerMinute` |

    | Per address, before the key | 2,400 per minute | 2,400 per minute |

    | Per address and route, per minute | 2,000 by default; 600 on boards; 120
    on `/v2/me` and batches; 10 on resolve | 2,000 by default; 120 on `POST
    /v3/traders/pnl`, `GET /v3/me` and `POST /v3/wallets/pnl`; 600 on `GET
    /v3/leaderboards/{platform}/{board}` and `GET /v3/rankings/{key}` |

    | Headers | `x-ratelimit-*` for the route limit | `RateLimit-Limit`,
    `RateLimit-Remaining`, `RateLimit-Reset` for the key |

    | 429 | `retryAfter` in the body | `Retry-After` header |

    | Load | 503 `OVERLOADED` | 503 `OVERLOADED` with `Retry-After: 1` past 30
    requests in flight on one API instance, before the key is checked |

    | Requests in flight per key | none | 429 `RATE_LIMITED` with `Retry-After:
    1` past 15 requests in flight for one key and its child keys on one API
    instance |


    - A 429 or a 503 costs nothing. Retry after the seconds in `Retry-After`.

    - A reveal that waits more than 2 seconds on the reveal lock of the key
    answers 429 with `Retry-After: 1`, as on v2.


    ### Stream


    |  | v2 | v3 |

    | --- | --- | --- |

    | URL | `wss://api.fomoscan.sh/v2/ws` (theses),
    `wss://api.fomoscan.sh/v2/ws/pump` (callouts) |
    `wss://stream.fomoscan.sh/v3`, every platform on one socket |

    | Key | `Authorization: Bearer` or `X-Api-Key` on the upgrade | the same; a
    refusal is an HTTP status: 401, 402, 403 `PERMISSION_DENIED` (scope), 403
    `PLAN_REQUIRED`, 429 |

    | Subscribe | `{ "type": "subscribe", "subscription": { "type": "all" } }` |
    `{ "type": "subscribe", "topic": "posts" }` |

    | Tokens | `"subscription": { "type": "tokens", "tokens": ["<address>"] }`,
    up to 50 | `"tokens": ["tok_solana_<mint>"]`, up to 50 |

    | Platforms | one socket per feed | `"platforms": [ … ]`, every platform
    when left out |

    | Acknowledgement | `replaced`, then `subscription` | one `subscribed` frame
    that carries `subscription` and `replaced` |

    | Items | `thesis` with `data`, `pump_callout` with `callout` | `post` with
    `cursor` and `item`, the envelope `GET /v3/posts` returns |

    | Errors | `error` with a string | `error` with `{ "type", "code",
    "message", "requestId" }` |

    | Replay | none | `"since": "s_…"` replays the last 5 minutes, then
    `replay_done` |

    | Heartbeat | ping every 30 s, close 1011 after 90 s of silence | the same |

    | Subscribe rate | no limit | 10 `subscribe` or `unsubscribe` frames per 60
    s |

    | Price | 20,000 credits per minute, per socket | 20,000 credits per started
    minute per connection, charged on connect and every 60 s, subscribed or not
    |


    ```json

    {"type":"subscribe","topic":"posts","platforms":["fomo","pump"],"tokens":["tok_solana_<mint>"],"since":"s_…"}

    ```


    - Close codes: 1001 we restart; 1008 no credit for the next minute, or the
    key lost access; 1009 a frame over 64 KB; 1011 no pong, or billing failed
    twice in a row; 1013 8 MB of frames wait unread, or frames arrive faster
    than we handle them.

    - One socket carries every platform, so a client of both v2 sockets pays one
    minute where v2 charged two.

    - A v2 frame sent to v3 answers an `INVALID_ARGUMENT` error frame and
    changes nothing.

    - v3 sends each post once, when it first becomes visible to us. `postedAt`
    gives its age.

    - Reconnect with `since` set to the last `cursor` you handled. The WebSocket
    section at the end of this page holds every frame, error code and a
    reconnect recipe.


    ### Not in v3 yet


    - Lookup by a v2 `id`: `GET /v2/user/id/{id}`, `GET
    /v2/user/id/{id}/profile`, and author feeds keyed on that id.

    - Reads on demand: v2 fetched the theses of a token it had not read during
    the call. `GET /v3/tokens/{network}/{address}/posts` answers an empty page.

    - Author name, author avatar and token image on each post (`authorName`,
    `authorAvatarUrl`, `tokenImageUrl`, callout `profileImage`). Read the author
    and the token by id.

    - Percentages and cost basis of the author's position:
    `percentageRealizedPnl`, `percentageUnrealizedPnl`, callout `pnlPercentage`,
    `avgEntryPriceUsd`, `avgEntryMcapUsd`, `costBasisAmount`, `amountBought`.

    - Wallet holdings with cost basis from the buy history: `GET
    /v2/pump/user/wallet/{wallet}/holdings`, `POST
    /v2/pump/user/wallets/holdings`.

    - Live resolve: `POST /v2/user/handle/{handle}/resolve` and its 202. v3
    answers from stored data.

    - The stats and open holdings of the `GET /feed/trader/{handle}` card.

    - MCP: the v2 MCP server, its `GET /v2/stats/…` reads and `POST
    /v2/user/handle/{handle}/reveal`.
servers:
  - url: https://api.fomoscan.sh
    description: The API host
security:
  - bearerAuth: []
  - apiKeyHeader: []
tags:
  - name: Traders
  - name: Wallets
  - name: Posts
  - name: Tokens
  - name: Leaderboards
  - name: Rankings
  - name: Account
  - name: Trades
  - name: WebSocket
    description: >-
      The live stream: one socket for every platform, platform and token
      filters, and a replay of the last 5 minutes.
paths:
  /v3/traders/{traderId}/following:
    get:
      tags:
        - Traders
      summary: List the traders a trader follows
      description: >-
        The traders this trader follows, as trader envelopes, with the platform
        of each edge and the time a crawl last saw it.


        - The list covers every account of the trader on a platform with a
        follow graph (`followGraph` in `GET /v3/capabilities`).

        - One item per edge: a trader on the other side of two platforms appears
        twice, once per platform.

        - An edge between two accounts of the same trader is left out, so a
        merge never makes a trader follow itself.

        - `counts` holds, per platform, the platform's own count (`reported`)
        and the edges we hold (`stored`). Every page carries it.

        - Order: `observedAt` newest first, then the account ids. A page
        boundary inside a tie neither drops nor repeats an edge.

        - Paged with `cursor` and `limit` (default 20, maximum 100). Follow
        `nextCursor` to read older rows.


        ## Price


        **200 credits per started 20 rows of `limit`**, on every call. A short
        or empty page costs the same as a full one.


        | Answer | Credits | Reveals |

        | --- | --- | --- |

        | `limit` 1. | 200 | 0 |

        | `limit` 20 (the default). | 200 | 0 |

        | `limit` 50. | 600 | 0 |

        | `limit` 100 (the maximum). | 1,000 | 0 |

        | 404: no trader has this id. | 200 | 0 |


        ## Limits


        - 2,000 requests per minute from one address on this operation, beside
        the per-minute limit of the key.

        - A key with less than 200 credits left gets 402 `QUOTA_EXCEEDED` before
        any work.
      operationId: listTraderFollowing
      parameters:
        - name: traderId
          in: path
          description: >-
            A trader id: `tdr_` and 26 characters. `GET /v3/traders/lookup`
            finds it by handle.
          required: true
          schema:
            type: string
            pattern: ^tdr_[0-7][0-9a-hjkmnp-tv-z]{25}$
        - name: platform
          in: query
          description: >-
            Only edges on this platform. A platform without a follow graph
            answers an empty page.
          required: false
          schema:
            type: string
            enum:
              - fomo
              - pump
              - hyperliquid
              - markets
              - polymarket
              - legend
              - risex
        - name: cursor
          in: query
          description: >-
            The `nextCursor` of the previous page, sent back as it came. Leave
            it out for the first page. A cursor belongs to the query that
            produced it: sent with other parameters it answers 400
            `INVALID_ARGUMENT`.
          required: false
          schema:
            type: string
            minLength: 3
            maxLength: 1024
        - name: limit
          in: query
          description: Rows per page, 1 to 100. 20 when left out.
          required: false
          schema:
            default: 20
            type: integer
            minimum: 1
            maximum: 100
      responses:
        '200':
          description: OK
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FollowListDocument'
              example:
                trader: tdr_01jbst8pvcfp79y0938nkrkayd
                direction: following
                counts:
                  fomo:
                    reported: 312
                    stored: 312
                    observedAt: '2026-11-01T08:42:10.250Z'
                  pump:
                    reported: null
                    stored: 0
                    observedAt: null
                items:
                  - subject:
                      kind: trader
                      id: tdr_01jbst8v3xft7t04hmasw9nf6y
                      canonical:
                        display:
                          handle: tidecaller
                          name: null
                          avatarUrl: null
                        platforms:
                          - fomo
                        mergedFrom: []
                        firstSeenAt: '2025-08-19T12:03:44.090Z'
                      sources:
                        fomo:
                          profile:
                            handle: tidecaller
                            name: null
                            bio: null
                            avatarUrl: null
                            xHandle: null
                            verified: null
                            observedAt: '2026-10-30T22:10:09.533Z'
                    platform: fomo
                    observedAt: '2026-10-31T23:12:40.071Z'
                hasMore: true
                nextCursor: >-
                  c_eyJxIjoiazNKOXhRMm1aN2FCIiwidiI6eyJzIjoiMjAyNi0xMS0wMVQwODo0MTowMi45MTdaIiwiaSI6IjAxOTJmM2E1LTIyMjItNzMzMy04NDQ0LTU1NTU2NjY2Nzc3NyJ9fQ
        '400':
          description: >-
            `INVALID_ARGUMENT`: a parameter, an id, a cursor or the body is
            malformed, or the operation does not take it.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '401':
          description: >-
            `UNAUTHENTICATED`: no key, or the key is unknown, revoked or
            expired.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '402':
          description: '`QUOTA_EXCEEDED`: the credit balance cannot pay for the call.'
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: '`PERMISSION_DENIED`: the key may not make this call.'
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: '`NOT_FOUND`. No trader has this id, merges included.'
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '429':
          description: >-
            `RATE_LIMITED`: too many requests. Retry after the seconds in
            `Retry-After`.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '500':
          description: '`INTERNAL`: the call failed on our side.'
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '503':
          description: >-
            `OVERLOADED`: the API holds too many requests at once. Retry after
            the seconds in `Retry-After`.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
components:
  headers:
    X-Request-Id:
      description: The id of this request, as in our logs.
      schema:
        type: string
    RateLimit-Limit:
      description: Requests per minute allowed for the key.
      schema:
        type: integer
        minimum: 0
    RateLimit-Remaining:
      description: Requests left in the current minute.
      schema:
        type: integer
        minimum: 0
    RateLimit-Reset:
      description: Seconds until the current minute ends.
      schema:
        type: integer
        minimum: 0
  schemas:
    FollowListDocument:
      type: object
      properties:
        trader:
          type: string
          description: The `tdr_` id of the trader, after merges.
        direction:
          type: string
          enum:
            - followers
            - following
          description: >-
            `followers`: who follows the trader. `following`: whom the trader
            follows.
        counts:
          type: object
          propertyNames:
            $ref: '#/components/schemas/Platform'
          additionalProperties:
            type: object
            properties:
              reported:
                anyOf:
                  - type: integer
                    minimum: 0
                    maximum: 9007199254740991
                  - type: 'null'
                description: >-
                  The platform's own count, as `followGraph` shows it. `null`
                  without a reading.
              stored:
                type: integer
                minimum: 0
                maximum: 9007199254740991
                description: >-
                  Edges we hold on this platform: the rows this list pages
                  through.
              observedAt:
                anyOf:
                  - $ref: '#/components/schemas/Time'
                  - type: 'null'
                description: When we read `reported`. `null` without a reading.
            required:
              - reported
              - stored
              - observedAt
          description: >-
            One entry per platform the list covers. `stored` against `reported`
            says how much of the platform list we hold.
        items:
          type: array
          items:
            type: object
            properties:
              subject:
                $ref: '#/components/schemas/TraderEnvelope'
                description: The other trader of the edge, as its envelope.
              platform:
                $ref: '#/components/schemas/Platform'
                description: The platform the edge is on.
              observedAt:
                $ref: '#/components/schemas/Time'
                description: When a crawl last saw the edge.
            required:
              - subject
              - platform
              - observedAt
          description: The edges of this page, newest `observedAt` first.
        hasMore:
          type: boolean
          description: '`true` when another page follows this one.'
        nextCursor:
          description: >-
            The cursor of the next page (`c_…`), or `null` on the last page.
            Treat it as opaque: send it back as `cursor`, never parse or build
            it.
          type:
            - string
            - 'null'
      required:
        - trader
        - direction
        - counts
        - items
        - hasMore
        - nextCursor
      description: >-
        One page of a trader's followers or following, with the counts per
        platform.
    ErrorBody:
      type: object
      properties:
        error:
          type: object
          properties:
            type:
              type: string
              enum:
                - invalid_request
                - authentication
                - quota
                - permission
                - rate_limit
                - internal
                - overloaded
              description: The group of the error. Each `code` belongs to one type.
            code:
              type: string
              enum:
                - INVALID_ARGUMENT
                - UNAUTHENTICATED
                - QUOTA_EXCEEDED
                - REVEAL_LIMIT_REACHED
                - PERMISSION_DENIED
                - PLAN_REQUIRED
                - NOT_FOUND
                - RATE_LIMITED
                - INTERNAL
                - OVERLOADED
              description: What went wrong, from a closed list. Act on this field.
            message:
              type: string
              description: A sentence for people. It can change; never parse it.
            requestId:
              type: string
              description: >-
                The id of the request, equal to `X-Request-Id`. Quote it when
                you write to us.
          required:
            - type
            - code
            - message
            - requestId
          description: The error.
      required:
        - error
      description: The body of every error answer.
    Time:
      type: string
      format: date-time
      pattern: >-
        ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d\.\d{3}(?:Z))$
      description: 'A time in RFC3339, UTC, with milliseconds: `2026-11-01T09:12:44.120Z`.'
    TraderEnvelope:
      type: object
      properties:
        kind:
          type: string
          const: trader
          description: >-
            The kind of object, `trader` here, and the only field to switch on.
            Skip objects whose kind you do not know.
        id:
          type: string
          description: >-
            Our id of the object, `tdr_` and 26 lowercase base32 characters.
            Store it: it stays the same across calls and never holds a
            platform's own id.
        canonical:
          type: object
          properties:
            display:
              anyOf:
                - type: object
                  properties:
                    handle:
                      description: >-
                        `handle` of the newest `profile` block that has one,
                        `null` when none has.
                      type:
                        - string
                        - 'null'
                    name:
                      description: >-
                        `name` of the newest `profile` block that has one,
                        `null` when none has.
                      type:
                        - string
                        - 'null'
                    avatarUrl:
                      description: >-
                        `avatarUrl` of the newest `profile` block that has one,
                        `null` when none has.
                      type:
                        - string
                        - 'null'
                  required:
                    - handle
                    - name
                    - avatarUrl
                - type: 'null'
              description: >-
                What to show for the trader. Each field comes from the `profile`
                block with the newest `observedAt` among the blocks in `sources`
                whose field is not `null`; a tie goes to the platform key that
                sorts first. One field can come from another platform than the
                next. `null` when no profile exists.
            platforms:
              type: array
              items:
                $ref: '#/components/schemas/Platform'
              description: The platforms the trader holds an account on, sorted.
            mergedFrom:
              type: array
              items:
                type: string
              description: >-
                The `tdr_` ids of traders merged into this one, sorted. A
                request for one of them answers with this trader.
            firstSeenAt:
              $ref: '#/components/schemas/Time'
              description: When we first saw the trader.
            walletAvailable:
              description: >-
                `true` when `GET /v3/traders/{traderId}/wallets` lists at least
                one wallet for the trader, so its reveal returns an address;
                `false` when it lists none. Never the address. Sent by `GET
                /v3/traders/{traderId}` and `GET /v3/traders/lookup` at no extra
                charge and without a reveal, and absent from the trader
                envelopes of every other operation.
              type: boolean
          required:
            - display
            - platforms
            - mergedFrom
            - firstSeenAt
          description: Fields that mean the same on every platform.
        sources:
          type: object
          propertyNames:
            type: string
            enum:
              - fomo
              - pump
              - hyperliquid
              - markets
              - polymarket
              - legend
              - risex
          additionalProperties:
            type: object
            properties:
              profile:
                description: The public profile of the account on this platform.
                anyOf:
                  - type: object
                    properties:
                      handle:
                        description: The handle on this platform, without `@`.
                        type:
                          - string
                          - 'null'
                      name:
                        description: The display name on this platform.
                        type:
                          - string
                          - 'null'
                      bio:
                        description: The profile text on this platform.
                        type:
                          - string
                          - 'null'
                      avatarUrl:
                        description: >-
                          Our mirrored copy of the avatar (`/a/<key>.webp`),
                          never the platform link.
                        type:
                          - string
                          - 'null'
                      xHandle:
                        description: The X handle the platform shows, without `@`.
                        type:
                          - string
                          - 'null'
                      verified:
                        description: The platform's own verified badge. We do not check it.
                        type:
                          - boolean
                          - 'null'
                      observedAt:
                        $ref: '#/components/schemas/Time'
                        description: >-
                          When we read this block from the platform. Every value
                          in the block is as of this time.
                    required:
                      - handle
                      - name
                      - bio
                      - avatarUrl
                      - xHandle
                      - verified
                      - observedAt
                  - $ref: '#/components/schemas/Unavailable'
              followGraph:
                description: Follower and following counts on this platform.
                anyOf:
                  - type: object
                    properties:
                      followers:
                        anyOf:
                          - type: integer
                            minimum: -9007199254740991
                            maximum: 9007199254740991
                          - type: 'null'
                        description: >-
                          Followers on this platform, as the platform counts
                          them.
                      following:
                        anyOf:
                          - type: integer
                            minimum: -9007199254740991
                            maximum: 9007199254740991
                          - type: 'null'
                        description: >-
                          Accounts this account follows, as the platform counts
                          them.
                      observedAt:
                        $ref: '#/components/schemas/Time'
                        description: >-
                          When we read this block from the platform. Every value
                          in the block is as of this time.
                    required:
                      - followers
                      - following
                      - observedAt
                  - $ref: '#/components/schemas/Unavailable'
              tradingStats:
                description: >-
                  Trading figures the platform publishes. The platform's
                  numbers, never ours.
                anyOf:
                  - type: object
                    properties:
                      swapCount:
                        anyOf:
                          - type: integer
                            minimum: -9007199254740991
                            maximum: 9007199254740991
                          - type: 'null'
                        description: Swaps the platform reports for the account.
                      tradeCount:
                        anyOf:
                          - type: integer
                            minimum: -9007199254740991
                            maximum: 9007199254740991
                          - type: 'null'
                        description: Trades the platform reports for the account.
                      volume:
                        anyOf:
                          - $ref: '#/components/schemas/Amount'
                          - type: 'null'
                        description: Traded volume the platform reports, in USD.
                      avgHoldSeconds:
                        description: >-
                          The average time the account held a position, in
                          seconds, as the platform reports.
                        type:
                          - number
                          - 'null'
                      observedAt:
                        $ref: '#/components/schemas/Time'
                        description: >-
                          When we read this block from the platform. Every value
                          in the block is as of this time.
                    required:
                      - swapCount
                      - tradeCount
                      - volume
                      - avgHoldSeconds
                      - observedAt
                  - $ref: '#/components/schemas/Unavailable'
          description: >-
            Capability blocks keyed by platform. A platform key is absent when
            the object has no side on that platform. A block is absent when the
            platform cannot provide it (`GET /v3/capabilities` lists which can)
            or we hold no reading of it yet. A field is `null` when the platform
            left it empty at `observedAt` or never fills it (`unfilled` in `GET
            /v3/capabilities`). Skip platform keys and blocks you do not know.
      required:
        - kind
        - id
        - canonical
        - sources
      description: >-
        A trader: one person behind accounts on one or more platforms. The
        envelope carries no wallets; `GET /v3/traders/{traderId}/wallets` lists
        them.
    Platform:
      type: string
      enum:
        - fomo
        - pump
        - hyperliquid
        - markets
        - polymarket
        - legend
        - risex
    Unavailable:
      type: object
      properties:
        status:
          type: string
          const: unavailable
          description: Always `unavailable`.
        retryable:
          type: boolean
          description: '`true` when a later call can return the block.'
      required:
        - status
        - retryable
      description: >-
        Stands in for a capability block we could not read at the time of the
        call. The other blocks of the object are current.
    Amount:
      type: object
      properties:
        value:
          type: string
          pattern: ^(0|-?[1-9]\d*)$
          description: >-
            An integer in base units, as a string, and negative below zero. Read
            it with a decimal or big integer type; a float loses digits.
        decimals:
          type: integer
          minimum: 0
          maximum: 36
          description: >-
            The power of ten between base units and one whole unit: `value` ÷
            10^`decimals`.
        currency:
          type: string
          minLength: 1
          description: >-
            `USD`, or the token id (`tok_<network>_<address>`) of a token
            quantity.
        priceAsOf:
          $ref: '#/components/schemas/Time'
          description: >-
            The time of the price we converted the value with. Absent when no
            price was used.
      required:
        - value
        - decimals
        - currency
      description: >-
        Money or a token quantity, never a JSON number. `{ "value": "12345678",
        "decimals": 6, "currency": "USD" }` is 12.345678 USD.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
    apiKeyHeader:
      type: apiKey
      in: header
      name: X-Api-Key

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.