API v1

PairsFun public API

Every PairsFun token, its pools, its fees, $PAIRS supply and burns, and the protocol totals, as JSON over HTTPS. No key, no sign-up, and CORS is open to any site, so a page can call it straight from the browser.

Quick start

Base URL https://pairsfun.xyz/v1 (the same calls also answer under /api/v1). Plain GET requests, nothing to sign up for.

curl "https://pairsfun.xyz/v1/tokens?sort=mcap&limit=10"
curl "https://pairsfun.xyz/v1/tokens/WecNBsywJKQLGfBKZzBSix3ykXJ11DfAGUiXCLypair"
curl "https://pairsfun.xyz/v1/stats"

From a browser or a bot, with fetch only. This reads every token on the bonding curve, page by page:

const API = 'https://pairsfun.xyz/v1'

async function get(path) {
  const res = await fetch(API + path)
  const json = await res.json()
  // Errors always look the same: { error: { code, message, status, requestId, retryAfterSec? } }
  if (!res.ok) throw Object.assign(new Error(json.error.message), json.error)
  return json
}

const tokens = []
let cursor = null
do {
  const page = await get('/tokens?status=curve&sort=progress&limit=100' + (cursor ? '&cursor=' + cursor : ''))
  tokens.push(...page.tokens)
  cursor = page.nextCursor
} while (cursor)

for (const t of tokens) console.log(t.symbol, t.curve.progressPct + '%', t.marketCapUsd)

Endpoints

All of them are GET and need no key. The cache column says how long the same answer is served to everyone; polling faster than that returns the same data.

CallWhat it returnsCache
GET /statusIs the API up, is launching open, the bonding-curve launch model (start market cap, raise target, supply split), the currencies a curve can raise in, the official $PAIRS mint.10 s
GET /tokensEvery listed token, with filters, search, sorting and pages. See Token list.10 s
GET /tokens/{mint}One token in full: the token object plus description, links, supply, launch and migration transactions, every pool, and the on-chain checks shown on its page.10 s
GET /tokens/{mint}/chartPrice history of a bonding-curve token, up to 720 points of [unix seconds, price] in its curve currency.30 s
GET /tokens/{mint}/poolsMultipair pools of a graduated token: one pool per asset its creator picked, opened from the 10% of supply set aside for them.30 s
GET /tokens/{mint}/creator-feesThe creator fee of a graduated token's main pool: collected so far, the creator's part, paid and waiting, the treasury part, with transactions.15 s
GET /statsProtocol totals: launches, raised, volume, fees, liquidity, daily volume, top tokens by volume and market cap, the latest launches and graduations.30 s
GET /feesFee rates on the curve and in the main pool, and how the PairsFun fee from bonding curves is split when the split is on (multipair pools, treasury, $PAIRS bought back and burned by the pools key), with its live totals and whether it is on now.30 s
GET /buyback$PAIRS supply, how much was burned and bought back so far, the treasury, and the transactions.30 s
GET /prices?mints=USD prices for 1 to 50 tokens, comma-separated. A token with no price right now is left out.15 s
GET /pairs, /pairs/{mint}Assets a token can be paired with, and one asset in detail: price, decimals, issuer powers.60 s
GET /launchesInstant multipairs only, the earlier launch model, in their original shape. For every token use /tokens.none
GET /config, /openapi.jsonLaunch settings for partner sites, and the full contract of this API.none

Token list

GET /tokens lists every token on PairsFun. Test coins and tokens taken down by moderation are not in it.

ParameterValues
statusall (default), curve (on the bonding curve, including a token migrating right now), graduated (migrated into its main pool), multipair (instant multipairs, the earlier model).
sortnew (default), mcap, volume (24 hours), progress (bonding-curve progress, graduated count as 100). Tokens without the value go last.
qPart of the name or ticker, a full mint address, or the ticker of a pair asset (TSLAx). Up to 64 characters.
limit1 to 100, default 50.
cursornextCursor from the previous page. It is null on the last page.

The answer is { "tokens": [...], "nextCursor": "50", "total": 214 }, where total counts every token that matches status and q. A new launch can shift a page by a token or two while you page through, so de-duplicate by mint.

The token object

The same object comes in /tokens, in the top lists of /stats, and inside /tokens/{mint} (with more fields). The official $PAIRS, as an example:

{
  "mint": "WecNBsywJKQLGfBKZzBSix3ykXJ11DfAGUiXCLypair",
  "name": "PairsFun",
  "symbol": "PAIRS",
  "image": "https://pairsfun.xyz/meta/img/WecNBsywJKQLGfBKZzBSix3ykXJ11DfAGUiXCLypair.png",
  "status": "graduated",
  "createdAt": "2026-11-05T04:28:05.000Z",
  "creator": "C478RVVWiAg2eFBr8BTw619qhkKSQFQuudEZUuqNtw5y",
  "decimals": 6,
  "priceUsd": 0.0000496,
  "marketCapUsd": 49601.22,
  "liquidityUsd": 15368.72,
  "volume24hUsd": 338568.45,
  "volumeAllUsd": 338568.45,
  "curve": {
    "pool": "F3vscNPnybdZJG7k3tWG7XdHCGZZBAs8nYoxRd3vt1cT",
    "quoteMint": "So11111111111111111111111111111111111111112",
    "quoteDecimals": 9,
    "progressPct": 100,
    "raisedRaw": "200000000196",
    "targetRaw": "200000000000",
    "priceInQuote": 5.134e-7,
    "migrating": false
  },
  "mainPool": "4qq6m4MMfd84fW5USDMWvzvFoJZVETsuuVuqvLRuuBW3",
  "pairAssets": [
    { "mint": "XsDoVfqeBukxuZHWhdvWHBhgEHjGNst4MLodqsJHzoB", "symbol": "TSLAx", "weightBps": null, "pool": null },
    { "mint": "Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh", "symbol": "NVDAx", "weightBps": null, "pool": null },
    { "mint": "XsoCS1TfEyfFhfvj8EtZ528L3CaKBDBRqRapnBbDF2W", "symbol": "SPYx", "weightBps": null, "pool": null }
  ],
  "fake": false,
  "url": "https://pairsfun.xyz/coin/WecNBsywJKQLGfBKZzBSix3ykXJ11DfAGUiXCLypair"
}
FieldMeaning
statuscurve, graduated or multipair.
priceUsd, marketCapUsdUSD per token and for the whole supply. null when the price of its pool currency is not known right now, never a made-up 0.
liquidityUsdReal liquidity in its pools, in USD.
volume24hUsd, volumeAllUsdEstimates from pool counters. null when not counted yet.
curveBonding-curve state: progress in percent, raised and target in base units of the curve currency, the price in it. null for instant multipairs.
mainPoolThe pool a graduated token migrated into. null until then.
pairAssetsFor a curve token, the assets its creator picked for multipair pools after migration (the pools are in /tokens/{mint}/pools). For an instant multipair, its basket, with weights and pools.
faketrue when the name or ticker copies PairsFun or $PAIRS (with fakeReason), false when checked and clean, null when not checked yet.

GET /tokens/{mint} answers { "token": { ... } } with all of the above plus description, links (the creator's website, X, Telegram), supplyRaw, launch (dev buy, launch and migration transactions), pools (each with its role: curve, main, basket or multipair) and proofs, the on-chain checks shown on the token page, each with a link to check it yourself.

Conventions

Limits

no key

60 requests a minute per IP address (IPv6 by /64), for all the calls on this page together. A token page also counts against the per-visitor limit of pairsfun.xyz itself.

partner key

A key from the partner program reads with its own limit, 600 a minute per key, sent as Authorization: Bearer <key>. A publishable key works only from its registered origins, as for launches.

Every answer, a 304 and an error included, carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds); only /config and /openapi.json have no limit and no such headers. Over the limit you get 429 rate_limited with Retry-After, the same number in error.retryAfterSec. Need more? DM @pairsfun_xyz on X.

Caching

Answers are shared: everyone asking the same thing within the cache time in Endpoints gets the same answer, and it comes with Cache-Control: public, max-age for the time it has left. Each answer also carries an ETag: send it back as If-None-Match and an unchanged answer is 304 Not Modified with no body. A 304 still counts toward the limit.

Errors

HTTP 404
{
  "error": {
    "code": "not_found",
    "message": "No PairsFun token with this mint.",
    "status": 404,
    "requestId": "req_9f2c41d07a3b55e1"
  }
}

Branch on code, show message, quote requestId when you write to us. The text can change, the code does not.

CodeWhat happened
validation_failedA parameter is wrong; details.field names it. HTTP 400.
not_foundNo such token, or it has no such data yet (no multipair pools, no creator fee payouts). HTTP 404.
method_not_allowedNot a GET. The Allow header says what is. HTTP 405.
rate_limitedOver the limit. Wait retryAfterSec. HTTP 429.
warming_upThe server has just restarted and is still reading the pools. Retry after retryAfterSec, a few seconds. HTTP 503.
upstream_unavailableThe network node or a price source did not answer. Try again in a moment. HTTP 502.
unauthorized, forbidden_originOnly when you send a key: an unknown or revoked key, or a publishable key from an origin that is not registered.
internal_errorSomething broke on our side. HTTP 500.

Versioning

This is v1. Within v1 fields and endpoints are only added, never renamed or removed, and an enum can gain new values. A breaking change would come as /v2, with v1 kept alongside it. The OpenAPI spec is the contract; its info.version goes up with every addition.

Launching tokens from your own site instead of reading them? That is the partner API, on the same base URL.