Coffer Docs
Integration

API Reference

Public read endpoints: pool discovery, swap routing, statistics, charts, and XP

Base URL: https://api.coffer.so. The endpoints on this page are public: no API key, no sign-in. They read indexed data; trades and liquidity operations are signed Solana transactions (see the SDK).

Interactive OpenAPI documentation with exact schemas lives at https://api.coffer.so/docs. Endpoints that back the Coffer app itself (wallet sign-in, portfolio, referrals, pool registration and renaming) are not covered here: create and manage pools through app.coffer.so.

Conventions

  • Addresses are base58; token amounts and LP supply are raw integer strings. Calculate with bigint.
  • swapFee, protocolFeeRate and weight are display percentages: swapFee: 0.3 means 0.3%; protocolFeeRate: 20 means the protocol takes 20% of the base fee.
  • Pagination: limit 1–100 (default 50), offset 0–10,000 (default 0).
  • Standard envelope: { "success": true, "data": ... }; paginated responses add hasMore and totalCount. The exceptions below return a flat object.
  • Rate limits are per IP: 10 requests per second and 200 per minute across the API; chart endpoints have their own per-IP limits. On 429, back off.
  • Indexed state lags the chain by seconds to minutes. Refresh from chain before building a transaction.

Pool and token discovery

GETParameters / result
/api/poolsPagination. Pinned pools first, then newest
/api/pools/:addressOne pool with its tokens; 404 if unknown
/api/pools/by-pair?tokenA=<mint>&tokenB=<mint>Pools holding both mints, TVL descending
/api/pools/tokensEvery token that appears in a pool, with the pools it belongs to
/api/pools/top-tokens?limit=30limit 1–100; tokens ordered by priority, market cap, ticker

Discovery can include disabled pools: check poolEnabled and swapsEnabled.

Pool fields include poolAddress, poolName, baseAssetMint, swapFee, protocolFeeRate, poolEnabled, swapsEnabled, poolAdmin, bptMint, bptTotalSupply, bptPriceUsd, tvlUsd, virtualTvlUsd, volume24h, apy, apy7d, apyUnreliable, the range-manager settings (rangeManager*) and tokens. Each token carries mintAddress, ticker, decimals, weight, leverage (rounded for display), actualBalance, virtualBalance, vaultAddress, tokenProgram, orderIndex, imageUrl, isActive and the sell-off window settings (maxSelloffPct, variableFee*, currentSelloff, windowStartTimestamp).

  • apy is the 24-hour figure shown in the app, apy7d the 7-day one; both are compounded, see How Yield Is Generated. apyUnreliable: true means the pool's TVL is too small for the figure to mean anything and both are reported as 0.
  • actualBalance is already net of protocol fees: never subtract protocolFeesOwed again. protocolFeesOwed is not returned; read it on chain.
  • Metrics and token metadata can be null while a pool is being indexed.

Swap routes

GET /api/pools/swap-route?tokenIn=<mint>&tokenOut=<mint>&amountIn=<raw>&decimalsIn=9
ParameterMeaning
tokenIn, tokenOutDistinct mints
amountInRaw input, positive integer string (at most 20 digits; execution must fit u64)
decimalsIn0–18, default 9. Use the mint's real decimals so USD-based fields are right
poolOptional: quote through this one pool only, no split
slippageBps0–10,000, default 200 (2%). Used to derive minReceived and per-leg minAmountOut

Response, with placeholder addresses:

{
  "success": true,
  "data": {
    "routes": [{
      "poolAddress": "<pool>",
      "poolName": "SOLUSDC",
      "amountIn": "1000000000",
      "expectedOut": "149000000",
      "minAmountOut": "146020000",
      "percentage": 100,
      "swapFee": 0.3,
      "surgeFeeAmount": "0",
      "surgeFeePct": 0,
      "tokenInIndex": 0,
      "tokenOutIndex": 1,
      "tokenProgramIn": "<input-token-program>",
      "tokenProgramOut": "<output-token-program>",
      "vaultIn": "<input-vault>",
      "vaultOut": "<output-vault>"
    }],
    "totalAmountIn": "1000000000",
    "totalExpectedOut": "149000000",
    "minReceived": "146020000",
    "slippageBps": 200,
    "effectivePrice": 0.149,
    "spotPrice": 0.15,
    "priceImpact": 0.36777,
    "feePercent": 0.3,
    "totalSurgeFee": "0",
    "estimatedXp": 0,
    "xpBoostApplied": false
  }
}
FieldSemantics
routesDirect-pair legs across Coffer pools; raw amounts per leg
percentageShare of the input sent through this leg, two decimals
expectedOut, minAmountOutQuoted output per leg (net of surge) and the floor derived from slippageBps
surgeFeeAmount, surgeFeePctOutput-side dynamic fee of this leg, if the pool's sell-off window is above its threshold
totalAmountIn, totalExpectedOut, minReceivedTotals across legs
effectivePriceRaw output / raw input
spotPriceBest candidate pre-fee raw spot price
priceImpactPercent deviation from the best fee-adjusted candidate spot, clamped at zero
feePercentInput-weighted base fee, display percent
estimatedXpExpected XP for this trade; 0 can also mean a missing USD price
xpBoostAppliedtrue when a signed-in app user has a referral boost; always false for anonymous calls

The route is a quote, not a transaction. HTTP 200 with an empty routes array or zero output means no usable route. Swap Routing covers how to turn a route into a protected transaction.

Transactions and aggregate statistics

GETResult / filters
/api/pools/:address/transactionsPagination; optional user=<wallet> and type=swap|add_liquidity|remove_liquidity|zap; newest first
/api/pools/:address/tx-stats{ totalCount, count24h }
/api/pools/statsProtocol totals: totalTvlUsd, totalVirtualTvl, totalVolume24h, poolCount, updatedAt

Transaction rows carry raw amounts (amountIn, amountOut, feeAmount, protocolFeeAmount, surgeFeeAmount), their USD values, the pool state after the transaction (virtualBalances, actualBalances, normalizedWeights, bptTotalSupply) and blockTime/slot. Base fees and their protocol share are in input-token units; surge fees in output-token units. zap is a single-token deposit.

Time series

GET /api/stats/:metric?pool=<address>&window=7d

Metrics: tvl, volume, swap_count, avg_swap, median_swap, fees_lp, fees_protocol, users_total, dau, mau, deposits, removals. pool defaults to all (the whole protocol). Windows: 1d, 7d (default), 30d, all (everything since launch). Points are ten-minute buckets.

Flat response: { "points": [{ "t": 1714003200000, "v": 1234567.89 }] }, t in Unix milliseconds, v in USD or a count.

Charts

The series behind the app's charts, on a uniform time grid and cached for a couple of minutes.

GETResult
/api/pools/:address/chart?metric=apy&range=1m&basis=7dOne pool metric over time. metric: apy (default), tvl, volume, fees. range: 24h, 1w, 1m, 6m, 1y, all. For apy, basis is the rolling window behind each point: 7d (default, smooth) or 24h (matches the pool card headline). Returns granularitySec, current, change and points: [{ t, v }]
/api/pools/:address/token-chart?range=1w&token=<mint>Price of every pool token quoted in the pool's base token, with the live rate and the curve's price bounds (the range slider). token narrows to one mint. range: 1d, 1w, 1m, 1y, all
/api/tokens/pair-chart?a=<mint>&b=<mint>&range=1wPrice of a in b over time for any two priced mints, pool or not: current { ratio, aUsd, bUsd }, change { abs, pct }, series. Same ranges; all is capped at one year. 90 requests per minute per IP

Pair-chart ratios keep six significant digits (BONK/SOL-style prices are tiny); a mint without a known USD price history yields a flat line at its current price.

XP leaderboard

Read-only views of Coffer XP. Flat responses.

GETResult
/api/leaderboard?page=1&limit=20{ total, page, limit, data: [{ address, points, place }] }; limit 1–100
/api/leaderboard/statsAggregate users and XP
/api/leaderboard/epochCurrent epoch, multiplier, base and current rates, epoch history
/api/leaderboard/user/:addressA wallet's points and rank
/api/leaderboard/user/:address/history?page=1&limit=20The wallet's accrual history

swapXpPerUsdLpFee applies to the trade's entire base fee in USD (LP plus protocol share, excluding surge). currentRates.lpXpPerUsd is per three-hour run; lpXpPerUsdPerDay is the daily figure shown in the app.

Errors

Errors use { "success": false, "error": "..." } with the HTTP status: 400 invalid input, 404 unknown pool or wallet, 429 rate limited, 500 server error.

Health and version

GET /health       → { "status": "ok", "timestamp": "..." }
GET /api/version  → { "version": "...", "timestamp": "..." }

Health is HTTP liveness only, not indexing freshness.

On this page