WHYUS? logo

WHYUS? · Developers

WHYUS? API

Everything the screener shows is available as JSON from https://dex.whyus.fun. There is no key, no account and no quota to sign up for: the endpoints are read-only views of the same live market data, and the app's own server is what calls the upstream providers, so a caller never spends their rate limit.

Last updated: 23 October 2026

Base URL

One origin serves every endpoint. It is this screener's own domain, not the host you happen to be reading this on:

https://dex.whyus.fun/api/market

Every response is application/json with access-control-allow-origin: *, so a browser page, a script or a worker can call it directly with no CORS setup, no key and no signing.

Quickstart

There is nothing to install and nothing to sign up for. One request gets the whole live market, and the shape of the answer is the same whether you read it in a shell, in a browser or in a notebook:

  1. Call the snapshot. GET /api/market answers every pair the screener knows about, already merged from both providers.
  2. Read updatedAt before you trust a number. The snapshot is shared and refreshed at most every 30 seconds, so that field is what tells you how old the data is — not the moment you asked for it.
  3. Key a row by chainId + pairAddress. A token can trade in several pools and the same address exists on several chains.
curl -s "https://dex.whyus.fun/api/market" | jq '{updatedAt, count, top: .pairs[0].baseToken.symbol}'

Every sample on this page can be copied with the button in its corner, and the playground below sends a real request so you can see the answer before you wire anything up.

Try it

This panel calls whichever endpoint you pick on the deployment serving this page, and prints the status, the timing and the JSON that came back. Requests are plain GETs: the API has no write endpoint at all, so nothing here can change anything.

The merged snapshot: every pair the screener knows about.

Send a request to see the response this deployment actually answers with.

The same call from a terminal

curl -s "https://dex.whyus.fun/api/market" | head -c 400

A response is trimmed to the first 4,000 characters so the page stays readable — a full /api/market snapshot is far larger. The endpoint links in the table below open the untrimmed JSON.

Endpoints

EndpointReturnsCache-Control
GET /api/marketEvery pair in the current screener snapshot, merged from both providerspublic, max-age=30
GET /api/pinnedThe listings pinned to the top of the screener, in the order they lead itpublic, max-age=30
GET /api/boost/packagesThe boost catalogue: packages, prices, durations and supported chainspublic, max-age=30
GET /api/boost/queueBoost placements, newest first, without buyer contact detailspublic, max-age=0
GET /api/advertise/packagesThe advertising catalogue: packages, prices, durations and the categories an advert is filed underpublic, max-age=30
GET /api/advertise/queueAdvertising placements, newest first, without advertiser contact detailspublic, max-age=0

Each path is relative to the base URL above, and the link opens the live JSON — on the host you are reading this page from, so you can try an endpoint before wiring it up.

Response reference

GET /api/market

FieldTypeNotes
updatedAtstringISO-8601, when the server's snapshot was taken
countnumberLength of pairs
pairsPair[]The merged snapshot — see the pair reference below

GET /api/pinned

FieldTypeNotes
countnumberLength of pairs — zero when nothing is pinned
pairsPair[]The pinned listings in the order they lead the screener, each carrying pinned: true
pairPair | nullThe first of pairs, for callers that only want the lead listing

GET /api/boost/packages

FieldTypeNotes
currencystringAlways USD; the fee is paid in crypto at the live rate
packages[]{ id, name, tagline, priceUsd, durationHours, duration, perks }The catalogue the Boost page sells from
chains[]{ id, label }Chains a placement can be bought for

GET /api/boost/queue

FieldTypeNotes
count / runningnumberEvery placement, and how many are live right now
orders[]object[]id, packageId, chainId, tokenAddress, token ({ symbol, name, pair, image }), status, createdAt, expiresAt, running
orders[].paymentobject | absentPresent once the fee was sent on-chain: rail, symbol, amount and the txSignature to check on the explorer

GET /api/advertise/packages

FieldTypeNotes
packages[]{ id, name, tagline, priceUsd, durationHours, duration, perks }Same shape as the boost catalogue — an advert is bought the same way
categories[]{ id, label, hint }What an advert can be filed under: product, website, launch, service, event

GET /api/advertise/queue

FieldTypeNotes
count / runningnumberEvery request, and how many adverts are running right now
orders[]object[]id, packageId, chainId, title, url, tagline, image, category, status, createdAt, expiresAt, running
orders[].paymentobject | absentThe same payment shape the boost queue carries

An advertising request is filed under the chain its fee was paid on, so chainId here is a payment rail rather than a subject — the advert itself belongs to no chain.

The pair object

FieldTypeNotes
source"dexscreener" | "geckoterminal"Which provider supplied the row
chainIdstringProvider's chain id — solana, base, pulsechain, …
dexIdstringThe exchange the pool trades on: pumpswap, orca, raydium, uniswap-v3, …
pairAddressstringThe pool address. With chainId it identifies the row, and opens the same pair page the screener links to
urlstringThe pool on the provider that supplied it
baseToken / quoteToken{ address, name, symbol }baseToken.address is the token's contract
priceUsd / priceNativestringDecimal strings, because a token can trade at 0.0000000174 USD and a JSON number would lose precision
txns{ m5, h1, h6, h24 } of { buys, sells }Trade counts per window
volume{ m5, h1, h6, h24 } of numberUSD volume per window
priceChange{ m5, h1, h6, h24 } of numberPercent change per window. Those four are the only windows either provider publishes — nothing finer or longer
liquidity{ usd, base, quote }The pool's reserve
fdv / marketCapnumberPresent only when the provider publishes them
pairCreatedAtnumberPool creation time, epoch milliseconds
info{ imageUrl, header, websites[], socials[] }Token imagery and links, when the provider has them
boosts{ active }Paid boosts the provider currently shows for the token
pinnedbooleanOnly on a listing WHYUS? has pinned to the top of the screener
pinLabelstringOptional chip text on a pinned row, when it was labelled
  • Treat every optional field as optional. A window a provider does not publish for a pair is simply absent, so priceChange?.h1 can be undefined while priceChange.h24 is there.
  • A token can have many pools. The screener keeps the most liquid one per token, so use /api/market for a leaderboard view and the providers directly when you need every pool.
  • A pinned row is a real row. Its numbers come from the same provider data as everything else; pinned: true only says WHYUS? leads the list with it. Pinned listings sit ahead of the ranking on every tab, in the panel's own order, and they step aside while a caller searches or filters by another chain.

Calling the API

A snapshot of the market, trimmed to a single pair:

curl

curl -s "https://dex.whyus.fun/api/market" | jq '.count, .pairs[0]'

Response, trimmed

{
  "updatedAt": "2026-10-23T22:14:05.000Z",
  "count": 640,
  "pairs": [
    {
      "chainId": "solana",
      "dexId": "pumpswap",
      "pairAddress": "EKhCvd4QUEydYrsJQ5euYTQAqCia8NmrF2Vt7UDhErXi",
      "baseToken": {
        "address": "AdoGKHb4hLJVqfL8NJYFamJKNtmLGXpZWxZDpo3nVgCh",
        "symbol": "WHY",
        "name": "Whyus.fun"
      },
      "priceUsd": "0.00000001744702422",
      "liquidity": { "usd": 20.7656 },
      "volume": { "h24": 0 },
      "priceChange": { "h24": 0 },
      "source": "geckoterminal"
    }
  ]
}

The same call from JavaScript, in a page or a worker — no CORS setup needed:

const res = await fetch("https://dex.whyus.fun/api/market");
const { updatedAt, count, pairs } = await res.json();

// The ten biggest movers over the last hour.
const movers = pairs
  .filter((p) => p.priceChange?.h1 != null)
  .sort((a, b) => b.priceChange.h1 - a.priceChange.h1)
  .slice(0, 10);

console.log(count, "pairs as of", updatedAt, movers.map((p) => p.baseToken.symbol));

The catalogues and the queues:

curl -s https://dex.whyus.fun/api/boost/packages
curl -s https://dex.whyus.fun/api/boost/queue
curl -s https://dex.whyus.fun/api/advertise/packages
curl -s https://dex.whyus.fun/api/advertise/queue

Recipes

The handful of things this API is most often used for:

Gainers worth looking at

Rank by one window, but drop the pools too thin to trade — a 400% move on $200 of liquidity is noise:

const { pairs } = await fetch("https://dex.whyus.fun/api/market").then((r) => r.json());

const gainers = pairs
  .filter((p) => (p.liquidity?.usd ?? 0) >= 25_000)
  .filter((p) => (p.volume?.h24 ?? 0) >= 10_000)
  .filter((p) => p.priceChange?.h24 != null)
  .sort((a, b) => b.priceChange.h24 - a.priceChange.h24)
  .slice(0, 20)
  .map((p) => ({
    chain: p.chainId,
    token: p.baseToken.symbol,
    price: p.priceUsd,
    h24: p.priceChange.h24,
    liquidity: p.liquidity.usd,
  }));

Just-launched pairs

pairCreatedAt is epoch milliseconds, so "new" is arithmetic rather than a provider flag:

const hourAgo = Date.now() - 60 * 60 * 1000;
const { pairs } = await fetch("https://dex.whyus.fun/api/market").then((r) => r.json());

const fresh = pairs
  .filter((p) => (p.pairCreatedAt ?? 0) > hourAgo)
  .sort((a, b) => b.pairCreatedAt - a.pairCreatedAt);

Who is placed right now

The queues tell you which placements visitors are actually being shown: running is the count, and each row carries its own term. Buyer details are never in this response.

const queue = await fetch("https://dex.whyus.fun/api/boost/queue").then((r) => r.json());

console.log(queue.running, "of", queue.count, "boosts are live");

for (const order of queue.orders.filter((o) => o.running)) {
  const hoursLeft = Math.round((order.expiresAt - Date.now()) / 3_600_000);
  console.log(order.token.symbol, order.chainId, "--", hoursLeft, "h left");
}

Poll without wasting requests

Every response carries updatedAt from one shared snapshot. Read it, and let it decide when to ask again — a client that respects it sees every update exactly once:

let seen = null;

async function tick() {
  const snap = await fetch("https://dex.whyus.fun/api/market").then((r) => r.json());
  if (snap.updatedAt !== seen) {
    seen = snap.updatedAt;
    console.log("new snapshot:", snap.updatedAt, snap.count, "pairs");
    // ... your work here
  }
}

setInterval(tick, 60_000);

Status codes, gaps and caching

  • 200 on success. No endpoint takes a parameter, so there is no 400 to handle; a path that does not exist answers the app's 404 page, not JSON.
  • A provider outage is not an error. The snapshot is merged from two sources, so when one fails the other still fills it and count simply drops. A pinned listing whose provider cannot answer is served from the app's last snapshot of it, so /api/pinned answers an empty pairs only when the operator has pinned nothing.
  • Cache-Control is on every response (see the endpoint table). /api/market is one shared server-side snapshot refreshed at most every 30 seconds, so polling faster returns the same updatedAt — build your loop around that field rather than a fixed timer.
  • Nothing here accepts a write. A placement is bought on the Boost page, because it carries the buyer's contact handle.
  • There is no key and therefore no per-client quota: this deployment answers from its own cache to protect the upstream providers. A heavy crawler slows the site down for everyone, so please keep polling to the interval you actually need.

Rate limits and etiquette

There is no key, so there is no per-client quota to watch — but there is a shared obligation, because this deployment is what talks to the upstream providers. Its caches are what keep them inside their own free-tier limits:

EndpointRefreshWhat that means for you
/api/marketat most every 30sPolling faster returns the same updatedAt — key your loop off that field
/api/pinnedat most every 30sCheap to read; it is the same kind of row, filtered to the pinned block
the two cataloguesat most every 30sPrices change when the operator changes them — fetch once and trust it
the two queuesevery requestUncacheable by design, so it is the only cheap thing to poll when you are watching a placement
  • Ask for what you need. One request every 30–60 seconds is plenty for a dashboard; nothing here changes faster than that.
  • Back off on failures. A 5xx means an upstream provider was slow, not that you did anything wrong — wait, then retry. There is no documented limit to exceed, and no ban to earn, but a tight retry loop only lengthens the outage.
  • No credentials, ever. Nothing here reads a key, a cookie or a session, and nothing here accepts a write, so there is no token to leak and no rate-limit header to parse.
  • Heavy use is welcome, silent abuse is not. If you are building something substantial — a bot, an indexer, a public dashboard — say so on X or Telegram (see below) rather than hammering the endpoints; the operator can raise the caches for you.

The boost API

A boost is a paid placement: it buys visibility in the strip above the screener, never a change to the data. The catalogue is public, and the queue is readable, but a placement itself is requested through the Boost page — it carries the buyer's contact handle, so it is not an anonymous write endpoint.

PackagePricePlacement
1 Hour$91 hour
6 Hours$256 hours
12 Hours$3912 hours
24 Hours$691 day
7 Days$1797 days
14 Days$29914 days
30 Days$49930 days
60 Days$84960 days
90 Days$1,14990 days

Placements can be bought for Solana, Ethereum, Base, BNB Chain, Arbitrum, Optimism, Polygon, Avalanche, Linea, Blast, Sonic, Cronos, Unichain, Ink, Scroll, ZKsync, Mantle, Celo, HyperEVM, Abstract, Monad, Berachain, Plasma, ApeChain, World Chain, opBNB, Katana, MegaETH, Stable, Robinhood Chain, Sui, Tron, TON, PulseChain, Fantom, Metis.

The fee is paid in crypto, from the buyer's own wallet, straight into one of two treasury wallets — there is no card, no custodian and no key held by the app:

RailTreasury wallet
Solana (SOL)EeGxCzMNh1YDNKuhDdApPDLEBQwYtfk3656AXbdJ1Ner
EVM networks (native coin)0xaaD8D32Cf2D3CDd1E4A4aF57896A9F1cAf65954F

A boost bought on one of these networks is paid in that network's own coin, at the live DEX Screener rate, in one transfer:

  • solana — SOL
  • ethereum — ETH (chain id 0x1)
  • base — ETH (chain id 0x2105)
  • bsc — BNB (chain id 0x38)
  • arbitrum — ETH (chain id 0xa4b1)
  • optimism — ETH (chain id 0xa)
  • polygon — POL (chain id 0x89)
  • avalanche — AVAX (chain id 0xa86a)
  • linea — ETH (chain id 0xe708)
  • blast — ETH (chain id 0x13e31)
  • sonic — S (chain id 0x92)
  • cronos — CRO (chain id 0x19)
  • unichain — ETH (chain id 0x82)
  • ink — ETH (chain id 0x570)
  • scroll — ETH (chain id 0x82750)
  • mantle — MNT (chain id 0x1388)
  • celo — CELO (chain id 0xa4ec)

A placement for any other chain is paid on Solana in SOL, so every package is payable without bridging. Once the transaction is in, the placement appears in /api/boost/queue with its payment.txSignature, the amount and the rail — check it on the chain's explorer before treating a placement as settled.

Versioning and compatibility

The endpoints are unversioned and additive: a field is never renamed, never changes type and never disappears, so a client written today keeps working. New fields are added when a provider publishes something new — read what you need and ignore the rest, and treat every field the pair reference marks as optional as exactly that.

This page documents the shape as of 23 October 2026. If a change would ever have to break the shape above, it arrives at a new path instead, so the paths in the endpoint table are the fixed part of the contract.

Where the data really comes from

The third-party sources' terms, limits and accuracy govern the numbers above; WHYUS? only merges and formats what they publish. GeckoTerminal and Uniswap are both called by this app's server, never by a visitor's browser — GeckoTerminal for trending pools, Uniswap's subgraph for a pair chart's candles — and DEX Screener is called from the browser by the screener itself. Either way the numbers are third-party: verify on-chain before you act on them.

Related pages

  • Docs — how the screener, wallets and boosts work.
  • Boost a token — packages, payment and the public queue.
  • Advertise — sponsored placements for a product, site or launch, bought on the same rails.
  • Guidelines — how to use the data responsibly.
  • Terms of service — the terms you accept by using the API.

This document describes how the WHYUS? app currently behaves. WHYUS? is owned and operated by whyus.fun, reachable on X (@whyusdotfun) and Telegram (@whyusdotfun). Operational details only the operator can supply — the legal entity, its registered address and the governing jurisdiction — are not published in the app yet and must be added before this document is relied on commercially.