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

# Discovery boards

> Rank launchpad tokens by launch stage, across one or more chains, in a single request.

```http theme={null}
POST /v1/discovery
```

One request names the chains and the columns it wants. Each column is ranked and filtered on its own, and answers with its whole ranked set. [Filtering and sorting](/concepts/filtering#discovery-boards) covers the ranking fields, the column defaults and every filter.

## Body

<ParamField body="chains" type="string[]" required>
  One or more [chain slugs](/concepts/chains). Each column ranks tokens from all of them as one set. A repeated slug is read once.
</ParamField>

<ParamField body="newPairs" type="object">
  Launchpad tokens below 40% of their bonding curve. Send `{}` for the defaults.
</ParamField>

<ParamField body="graduating" type="object">
  Launchpad tokens at 40% of their curve or more, not yet graduated.
</ParamField>

<ParamField body="graduated" type="object">
  Launchpad tokens that have graduated, or that have no curve reading.
</ParamField>

Leave a column out to skip it, and name at least one.

### Column config

<ParamField body="sortBy" type="string">
  The [field to rank on](/concepts/filtering#sort-fields). The column's default when absent.
</ParamField>

<ParamField body="order" type="string" default="desc">
  `desc` or `asc`.
</ParamField>

<ParamField body="limit" type="integer" default="20">
  Tokens in the column. A value outside 1–100 is clamped, not refused.
</ParamField>

<ParamField body="filters" type="object">
  Conditions every token in the column must meet, on top of the [column defaults](/concepts/filtering#column-defaults). An unknown filter name is ignored rather than refused.
</ParamField>

## Response

<ResponseField name="columns" type="object[]" required>
  One entry per requested column, in the order `newPairs`, `graduating`, `graduated`.

  <Expandable title="column">
    <ResponseField name="column" type="string" required>
      Which column these entries rank: `newPairs`, `graduating` or `graduated`.
    </ResponseField>

    <ResponseField name="deltas" type="object[]" required>
      The whole ranked column, one entry per rank.

      <Expandable title="entry">
        <ResponseField name="chain" type="string" required>
          The [chain slug](/concepts/chains). With `address`, it keys the row.
        </ResponseField>

        <ResponseField name="address" type="string" required>
          The token's address.
        </ResponseField>

        <ResponseField name="rank" type="integer" required>
          The token's place in the column, from 1.
        </ResponseField>

        <ResponseField name="data" type="object" required>
          The token object.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

### Token object

Each entry's `data` is the same object [Token details](/reference/rest/token-details) returns.

<ResponseField name="chain" type="string" required>
  The [chain slug](/concepts/chains) the token trades on.
</ResponseField>

<ResponseField name="address" type="string" required>
  The token's address.
</ResponseField>

<ResponseField name="name" type="string" required>
  The token's name.
</ResponseField>

<ResponseField name="symbol" type="string" required>
  The token's symbol.
</ResponseField>

<ResponseField name="decimals" type="integer" required>
  The token's decimals.
</ResponseField>

<ResponseField name="supply" type="decimal string" required>
  Total supply.
</ResponseField>

<ResponseField name="creator" type="string" required>
  The creator's address.
</ResponseField>

<ResponseField name="devFunding" type="object" required>
  The creator wallet's first inbound funding.

  <Expandable title="devFunding">
    <ResponseField name="status" type="string" required>
      `unresolved` while the lookup can still resolve, or `resolved` once it has settled. The other fields are `null` in both states when no funder is known.
    </ResponseField>

    <ResponseField name="address" type="string | null">
      The funder's address.
    </ResponseField>

    <ResponseField name="sourceKind" type="string | null">
      `cex` for a known exchange, or `wallet` for another wallet.
    </ResponseField>

    <ResponseField name="sourceLabel" type="string | null">
      A display label for the funder, such as an exchange name.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="createdAt" type="integer" required>
  When the token was created, in milliseconds.
</ResponseField>

<ResponseField name="launchpad" type="string | null" required>
  The launchpad the token launched on. `null` for tokens that did not launch on one.
</ResponseField>

<ResponseField name="bondingCurvePct" type="decimal string | null" required>
  Bonding-curve completion, 0–100. `null` for tokens not on a bonding curve.
</ResponseField>

<ResponseField name="graduatedAt" type="integer | null" required>
  When the token left its launchpad, in milliseconds. `null` until then.
</ResponseField>

<ResponseField name="graduatedToPool" type="string | null" required>
  The pool the token moved to on graduation.
</ResponseField>

<ResponseField name="graduatedToDex" type="string | null" required>
  The venue that pool trades on.
</ResponseField>

<ResponseField name="dexes" type="string[]" required>
  The venues the token's top pools trade on, deepest pool first.
</ResponseField>

<ResponseField name="anchorAddress" type="string | null" required>
  The address of the quote asset in the token's deepest pool.
</ResponseField>

<ResponseField name="anchorSymbol" type="string | null" required>
  The symbol of that quote asset.
</ResponseField>

<ResponseField name="price" type="money object" required>
  The current price.
</ResponseField>

<ResponseField name="marketCap" type="money object" required>
  The current market cap.
</ResponseField>

<ResponseField name="liquidity" type="money object" required>
  The current liquidity.
</ResponseField>

<ResponseField name="ath" type="object" required>
  All-time highs.

  <Expandable title="ath">
    <ResponseField name="price" type="money object" required>
      The all-time-high price.
    </ResponseField>

    <ResponseField name="priceAt" type="integer" required>
      When that price was set, in milliseconds.
    </ResponseField>

    <ResponseField name="marketCap" type="money object" required>
      The all-time-high market cap.
    </ResponseField>

    <ResponseField name="marketCapAt" type="integer" required>
      When that market cap was set, in milliseconds.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="stats" type="object" required>
  Trade activity over trailing windows that end now, keyed `5m`, `30m`, `1h`, `6h` and `24h`.

  <Expandable title="stats window">
    <ResponseField name="priceChangePct" type="decimal string" required>
      Price change over the window, as a signed percentage.
    </ResponseField>

    <ResponseField name="volume" type="money object" required>
      Trade volume over the window.
    </ResponseField>

    <ResponseField name="buyCount" type="integer" required>
      Buy trades over the window.
    </ResponseField>

    <ResponseField name="sellCount" type="integer" required>
      Sell trades over the window.
    </ResponseField>

    <ResponseField name="buyVolume" type="money object" required>
      Buy-side volume over the window.
    </ResponseField>

    <ResponseField name="sellVolume" type="money object" required>
      Sell-side volume over the window.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="totals" type="object" required>
  All-time totals.

  <Expandable title="totals">
    <ResponseField name="volume" type="money object" required>
      All-time trade volume.
    </ResponseField>

    <ResponseField name="fees" type="object" required>
      Fee totals by kind: `transaction`, `validator`, `platform` and `dex`, each a money object. The kinds are measured on different bases, so do not add them together.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="holders" type="object" required>
  Holder concentration and wallet risk groups. Each `Pct` field is the share of supply a group holds now, 0–100. Each `Count` field is the number of wallets that ever carried the group's flag on this token, so it never decreases.

  <Expandable title="holders">
    <ResponseField name="count" type="integer" required>
      Current holders.
    </ResponseField>

    <ResponseField name="top10Pct" type="decimal string" required>
      Share held by the 10 largest holders. It can read above 100 when supply data drifts.
    </ResponseField>

    <ResponseField name="devPct" type="decimal string" required>
      Share held by the creator.
    </ResponseField>

    <ResponseField name="sniperPct, sniperCount" type="decimal string, integer" required>
      Share held by, and count of, wallets that bought within two blocks of the token's first pool (snipers).
    </ResponseField>

    <ResponseField name="bundlerPct, bundlerCount" type="decimal string, integer" required>
      Share held by, and count of, wallets that made the fourth or later buy in a single block (bundlers).
    </ResponseField>

    <ResponseField name="insiderPct, insiderCount" type="decimal string, integer" required>
      Share held by, and count of, wallets that bought within three blocks of the token's first pool (insiders). These overlap with snipers.
    </ResponseField>

    <ResponseField name="freshWalletPct, freshWalletCount" type="decimal string, integer" required>
      Share held by, and count of, wallets funded less than seven days before they traded.
    </ResponseField>

    <ResponseField name="proTraderPct, proTraderCount" type="decimal string, integer" required>
      Share held by, and count of, wallets that have traded through a known trading terminal.
    </ResponseField>

    <ResponseField name="fomoPct, fomoCount" type="decimal string, integer" required>
      Share held by, and count of, wallets that have traded through the fomo.family terminal.
    </ResponseField>

    <ResponseField name="phishingPct" type="decimal string" required>
      Share held by wallets a security source labels malicious.
    </ResponseField>

    <ResponseField name="phishingCount" type="integer" required>
      Wallets that ever traded the token while labelled malicious.
    </ResponseField>

    <ResponseField name="fundedByCexPct" type="decimal string" required>
      Share of current holders that a known exchange funded, 0–100.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="security" type="object" required>
  Contract security signals.

  <Expandable title="security">
    <ResponseField name="mintDisabled" type="boolean" required>
      `true` when no more of the token can be minted: the mint authority is revoked on Solana, or the contract holds no mint power on EVM chains.
    </ResponseField>

    <ResponseField name="transferBlockable" type="boolean" required>
      `true` when transfers can be blocked: by a freeze authority on Solana, or by a blacklist or pause on EVM chains.
    </ResponseField>

    <ResponseField name="lpBurntPct" type="decimal string | null" required>
      Share of liquidity burnt or locked, 0–100. `null` until observed.
    </ResponseField>

    <ResponseField name="transferFeeBps" type="integer | null" required>
      The contract's transfer fee in basis points; `250` is 2.50%. `null` when the contract has not been scored.
    </ResponseField>

    <ResponseField name="rugPct" type="decimal string" required>
      Not yet populated; always `"0"`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="metadata" type="object" required>
  Project links and description. Every field except `twitterReuseCount` can be `null`.

  <Expandable title="metadata">
    <ResponseField name="description" type="string | null" required />

    <ResponseField name="website" type="string | null" required />

    <ResponseField name="twitter" type="string | null" required />

    <ResponseField name="telegram" type="string | null" required />

    <ResponseField name="discord" type="string | null" required />

    <ResponseField name="twitterReuseCount" type="integer" required>
      Not yet populated; always `0`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="platforms" type="object[]">
  External listings. Left out when empty. Today the only listing is a paid DexScreener profile: `{"platform": "dexScreener", "isPaid": true, "paidAt": 1789000000000}`. `paidAt` can be `null`.
</ResponseField>

<ResponseField name="annotations" type="object[]">
  Flags the launch carried, each tagged by `kind`, such as `{"kind": "mayhem_mode"}`. Left out when empty. New kinds can appear.
</ResponseField>

<ResponseField name="updatedAt" type="integer" required>
  When the token object last changed, in milliseconds.
</ResponseField>

## Errors

| Code | When |
| - | - |
| `UNSUPPORTED_CHAIN` | A slug in `chains` is not supported |
| `VALIDATION_ERROR` | No chain or no column requested, a range whose `min` is above its `max`, or a malformed body |

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://{{API_HOST}}/v1/discovery" \
    -H "Authorization: Bearer $API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "chains": ["solana", "base"],
      "graduating": {
        "sortBy": "volume24hUsd",
        "limit": 20,
        "filters": { "liquidityUsd": { "min": "20000" }, "holderCount": { "min": 500 } }
      }
    }'
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch("https://{{API_HOST}}/v1/discovery", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      chains: ["solana", "base"],
      graduating: { sortBy: "volume24hUsd", limit: 20 },
    }),
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  const { columns } = await response.json();
  ```

  ```rust Rust theme={null}
  use serde_json::{Value, json};

  let boards: Value = reqwest::Client::new()
      .post("https://{{API_HOST}}/v1/discovery")
      .bearer_auth(std::env::var("API_KEY")?)
      .json(&json!({
          "chains": ["solana", "base"],
          "graduating": { "sortBy": "volume24hUsd", "limit": 20 },
      }))
      .send()
      .await?
      .error_for_status()?
      .json()
      .await?;

  for column in boards["columns"].as_array().into_iter().flatten() {
      println!("{} {}", column["column"], column["deltas"].as_array().map_or(0, Vec::len));
  }
  ```
</RequestExample>

<ResponseExample>
  ```json Shortened theme={null}
  {
    "columns": [
      {
        "column": "graduating",
        "deltas": [
          {
            "chain": "solana",
            "address": "TokenMint1111111111111111111111111111111pump",
            "rank": 1,
            "data": {
              "chain": "solana",
              "address": "TokenMint1111111111111111111111111111111pump",
              "name": "Example",
              "symbol": "EXAMPLE",
              "decimals": 6,
              "price": { "native": "0.00000042", "usd": "0.000075" },
              "marketCap": { "native": "420.5", "usd": "75000" },
              "liquidity": { "native": "128.4", "usd": "22900" },
              "bondingCurvePct": "91.4",
              "updatedAt": 1789632012345
            }
          }
        ]
      }
    ]
  }
  ```
</ResponseExample>


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