← docs

REST API

Public read endpoints. JSON, no authentication, no API keys. Base URL: https://lumpfun.com. Per-IP rate limits apply — reads are generous, transaction building is tighter, and exceeding either returns 429 with a retry-after header. Requests are same-origin by default: these routes send no CORS headers, so call them from a server rather than a browser on another origin.

All bigint values (lovelace, token quantities, reserves) are returned as strings to preserve precision; parse them with BigInt, never Number.

Two pool generations are live. Current tokens (poolVersion: "v3") trade on a bonding curve that graduates in place into a permanent AMM pool — the same UTxO, flipped by a datum field, tradable forever afterwards on LumpSwap. Older v1/v2 tokens are historical and several endpoints behave differently for them; each notes where it matters.

GET

/api/tokens

List every token registered with LumpFun, ordered newest first. Each entry is the full TokenMetarecord — the same shape stored in our KV registry.

Response (200) — array of:

{
  "policyId":     "ec11a20dc05761a24c415cfc85b42ef5b31caa52dd501082d6744b9c",
  "assetName":    "4d5547",                       // hex of utf-8 ticker
  "ticker":       "MUG",
  "name":         "Mug",
  "creatorAddress":   "addr1q...",                // bech32 of creator wallet
  "creatorFeeBps":    100,                        // 100 = 1%, max 200
  "curveAddress":     "addr1w...",                // bonding-curve script
  "validatorCbor":    "59...",                    // parameterised PlutusV3
  "graduationAdaLovelace": "21000000000",         // optional, default 21k ADA
  "imageUri":         "https://...",
  "description":      "...",
  "website":          "https://...",              // all socials optional
  "twitter":          "@handle",
  "telegram":         "t.me/...",
  "discord":          "discord.gg/...",
  "launchedAt":       "2026-05-05T11:48:29.797Z",

  // Set when graduation has happened:
  "graduatedTxHash":      "f326c7...",
  "minswapPoolTxHash":    "77d4bd...",
  "minswapPoolId":        "ec11a2...4d5547",
  "poolAdaLovelace":      "12000000",
  "poolTokens":           "3970836",

  // Set when creator picked a launch vesting window:
  "vestingAddress":         "addr1w...",
  "vestingValidatorCbor":   "...",
  "vestingUnlockMs":        1799123456000,
  "vestingClaimedTxHash":   "...",
  "extraVestings":          [ /* additional re-vest positions */ ],

  // Set on tokens launched after the fee accumulator shipped:
  "feeAccumulatorAddress":         "addr1w...",
  "feeAccumulatorValidatorCbor":   "...",
  "feeAccumulatorClaimedTxHash":   "..."
}

curl

curl https://lumpfun.com/api/tokens

ts

const tokens = await fetch('https://lumpfun.com/api/tokens').then(r => r.json());
for (const t of tokens) {
  console.log(t.ticker, t.policyId);
}
GET

/api/token/{policyId}

Single token lookup with live curve state attached. Cheaper than fetching /api/tokens and filtering when you already know the policy ID.

Response (200):

{
  ...all TokenMeta fields,
  "live": {
    "adaReserve":    "12345678",       // lovelace currently at the curve
    "tokenReserve":  "987654321",      // tokens still purchasable
    "priceLovelace": "3049999",        // (adaReserve+virtual)*1e6/tokenReserve
                                       // NOTE: SCALED BY 1e6. To get ADA/token
                                       // divide by 1e12, not 1e6 — the 1e6 is a
                                       // fixed-point scale, and the lovelace→ADA
                                       // conversion is a second 1e6.
    "marketCapAda":  3049.999,         // priceLovelace * total_supply / 1e12
    "bondedPct":     0.05,             // 0–100, 100 = at graduation
    "graduated":     false
  }
}

live is null for fully-graduated tokens whose curve UTxO has been drained — read poolAdaLovelace / poolTokens from the meta instead.

curl

curl https://lumpfun.com/api/token/ec11a20dc05761a24c415cfc85b42ef5b31caa52dd501082d6744b9c
GET

/api/curve

Raw on-chain curve state. The lowest-latency way to poll reserves for a trading agent — bypasses any cache layer above Blockfrost.

Query params:

namedescription
addressbech32 curve script address (from token meta)required
assetconcatenation of policyId + assetName (hex)required

Response (200):

{
  "adaReserve":   "5000000",       // lovelace in the curve UTxO
  "tokenReserve": "996679946"      // tokens still in the curve
}

Returns 404 with {"error":"curve UTxO not found"} after the token has graduated and the UTxO is gone.

Side effect:any GET to this endpoint also kicks off a graduation check for that token. If reserves have crossed the threshold and the migration hasn't run yet, the server queues it. Idempotent and safe to poll.

curl

curl "https://lumpfun.com/api/curve?address=addr1wxp8497v67f4vzpsrqmc97rv2vyq6vntrzkz4pc2ce92sncmzz5z5&asset=ec11a20dc05761a24c415cfc85b42ef5b31caa52dd501082d6744b9c4d5547"
GET

/api/v3/pool

Live pool state for a current-cohort token, read from the pool UTxO's own inline datum. This is the authoritative source for a v3 token's reserves, price and phase.

Query params:

namedescription
policyIdtoken policy IDrequired

Response (200):

{
  "adaReserve":      "2000000",
  "tokenReserve":    "1142607197",
  "mode":            "Bootstrap",   // "Bootstrap" = bonding curve
                                    // "Amm"       = graduated permanent pool
  "bondedBps":       3,             // curve progress, 10000 = ready to graduate
  "creatorFeeBps":   100,
  "platformFeeBps":  0,
  "platformFeeFlat": "2000000",     // flat 2 ADA per trade
  "txHash":          "...",         // the pool UTxO being read
  "outputIndex":     0
}

Always take mode from here, never from the registry. Graduation is permissionless — anyone may submit it once the curve is bought out — so it can land between two polls, and the two phases price with different arithmetic. In Bootstrap the reserves carry virtual offsets; in Amm they are the real balances.

Returns 502 when the chain backend cannot be read, and 404 for a token that is not v3.

curl

curl "https://lumpfun.com/api/v3/pool?policyId=POLICY"
GET

/api/quote

Pre-trade quote computed against live reserves with the on-chain curve math (quoteBuy / quoteSellGross from web/src/lib/curve-math.ts). Same numbers the validator will check at submit time — use this so your agent and the chain agree.

Query params:

namedescription
policyIdtoken policy ID (from registry)required
side"buy" or "sell"required
amountbigint string. Buy: lovelace in. Sell: tokens in.required
slippageBps0–500. Default 50 (0.5%).optional

Response (200):

{
  "side":             "buy" | "sell",
  "policyId":         "...",
  "assetUnit":        "<policyId><assetName>",
  "amountIn":         "5000000",
  "expectedOut":      "166112956",     // tokens (buy) or lovelace gross (sell)
  "minOut":           "165280391",     // expectedOut after slippage applied
  "creatorFeeBps":    100,
  "creatorFeeLovelace":  "50000",      // bps × ada_in (buy) or × ada_gross (sell)
  "platformFeeLovelace": "1000000",    // flat, per trade — see note below
  "adaNetLovelace":   "...",           // sell only — gross − creator − platform
  "reserves":  { "adaReserve": "...", "tokenReserve": "..." },
  "graduated": false,
  "slippageBps": 50
}

This endpoint serves v1/v2 tokens only. For a current (v3) token it returns 409 wrong_cohort: v3 uses different curve constants and a mode-selected arithmetic, so quoting it with these formulas would produce numbers the validator rejects. Read /api/v3/pool and quote from its reserves instead. The flat platform fee is 2 ADA on v3 and 1 ADA on the legacy cohorts; the creator fee is 1% of the ADA leg on both, paid on top of a buy and deducted from a sell.

A 409 also means a legacy curve has already graduated. v1 graduation drained the curve to a Minswap pool; v2 and v3 flip in place and keep trading.

curl (buy 5 ADA)

curl "https://lumpfun.com/api/quote?policyId=POLICY&side=buy&amount=5000000"

curl (sell 1M tokens)

curl "https://lumpfun.com/api/quote?policyId=POLICY&side=sell&amount=1000000"
GET

/api/holders

Top holders of an asset, derived from Blockfrost's asset-addresses index. Includes the bonding curve and any vesting script addresses — filter client-side if you only want wallets.

Query params:

namedescription
assetconcatenation of policyId + assetName (hex)required

Response (200) — array of:

{
  "address":  "addr1q...",
  "quantity": "996679946"   // bigint string
}

curl

curl "https://lumpfun.com/api/holders?asset=ec11a20dc05761a24c415cfc85b42ef5b31caa52dd501082d6744b9c4d5547"
GET

/api/trades

Recent trades on a curve, derived from on-chain tx deltas. Sorted newest first, capped at 25 entries. Each row reports the actual buyer/seller (the wallet that signed the tx, identified via inputs — not the treasury or creator-fee output addresses).

Query params:

namedescription
addressbech32 curve script addressrequired
assetconcatenation of policyId + assetNamerequired

Response (200) — array of:

{
  "txHash":     "9a1b2c...",
  "type":       "buy" | "sell",
  "adaDelta":   "10200000",        // |Δ pool lovelace| (a buy includes its fees)
  "adaAmount":  "10000000",        // ADA without fees: buy = into the curve, sell = received
  "tokenDelta": "32156789",        // |Δ token_reserve| in token units
  "trader":     "addr1q...",
  "blockTime":  1777991234         // unix seconds
}

Cached for 24h per tx-hash via Next.js ISR — recent trades come live, deeper history is stable so we don't re-query Blockfrost on every page load.

GET

/api/price-history

OHLC bars for chart rendering. Server buckets trades into the requested timeframe. Same query params as /api/trades plus the bucket size.

Query params:

namedescription
addressbech32 curve script addressrequired
assetconcatenation of policyId + assetNamerequired
timeframe"5m" | "15m" | "1h" | "all". Default "15m".optional

Response (200) — array of bars:

{
  "t":         "5/5 13:00",         // formatted display label
  "timestamp": 1777991100,          // unix seconds (bucket start)
  "open":      0.04,
  "high":      0.045,
  "low":       0.04,
  "close":     0.043
}

Prices are floating-point ADA-per-token, derived from the curve datum at each tx (not lovelace). For the precise on-chain price use /api/curve → spotPrice from curve-math.

GET

/api/wallet-assets

Non-ADA assets held by a Cardano address, with LumpFun registry metadata joined on for any asset that was launched via this protocol.

Query params:

namedescription
addressbech32 (mainnet addr1...)required

Response (200):

{
  "assets": [
    {
      "unit":     "ec11a20...4d5547",
      "quantity": "3298081",
      "registry": {                     // present only for LumpFun-launched tokens
        "policyId":  "ec11a20...",
        "assetName": "4d5547",
        "ticker":    "MUG",
        "name":      "Mug",
        "imageUri":  "https://..."
      }
    }
  ]
}

Returns {"assets": []} (status 200) for an address with no assets. Resolution runs against our own node's chain index, which covers all history, so there is no "not indexed yet" case.

POST

/api/tx/build

Builds an unsigned transaction against our own Cardano node and returns its CBOR. Your wallet signs it; the server cannot. It holds no key for your address and operates a read-only wallet, so the only thing this endpoint can hand you is a transaction you must sign yourself.

Contract parameters are deliberately not taken from the request. Pool address, validator script, fee rate and fee routing are read from the registry, which is re-derived and verified at registration time. You supply only who is paying and how much.

Body:

namedescription
action'buy' | 'sell' | 'launch' | 'order-buy' | 'order-sell' | 'order-cancel' | 'claim-fees' | 'claim-vesting' | 'revest'required
addressbech32 — whose UTxOs fund the tx and where change goesrequired
policyIdtoken being acted on (all actions except launch)conditional
amountbigint string — lovelace for buys, token units for sellsconditional
slippageBpsinteger 0..5000 (default 100)optional

Response (200):

{
  "cbor":      "84a300d90102...",   // unsigned tx — sign this
  "tokensOut": "1662787"            // buys; sells return adaNet instead
}

Flow:

const built   = await post('/api/tx/build',  { action: 'buy', policyId, address, amount });
const witness = await walletApi.signTx(built.cbor, true);   // partial sign
const { txHash } = await post('/api/tx/submit', { cbor: built.cbor, witness });

Returns 409 for actionable conditions — chain index behind tip, no UTxOs at the address, sell too large for the pool to honour, pool UTxO missing.

POST

/api/tx/submit

Attaches a CIP-30 witness set to a transaction this server built and submits it. The transaction is re-hydrated from the exact bytes you signed, so the signature cannot be invalidated by a rebuild.

Body:

namedescription
cborthe unsigned tx returned by /api/tx/buildrequired
witnesshex witness set from walletApi.signTx(cbor, true)required

Response (200):

{ "txHash": "288ab0f22c5bacfa58f76b8a1c7bc1081d3af732624adb0b471624b7d0f2a926" }

An invalid or missing signature is rejected by the ledger. The server cannot substitute one.

Errors

Errors are JSON with a single error field. Common shapes:

  • 400 — missing/invalid query params (e.g. non-bigint amount).
  • 404 — token not in registry, or the pool UTxO is genuinely not on chain.
  • 409 — real token, operation not applicable to its current state: a v3 token on a v2-only endpoint, a graduated legacy curve, or an amount the pool cannot fill.
  • 429 — rate limited. Honour retry-after.
  • 500 — internal error.
  • 502 / 503 — a data source we depend on could not be read. The answer is unknown, not empty.

That last distinction is load-bearing and these routes hold to it: a failed read never returns 200 []. An empty array means the chain really is empty; a backend failure is a 502/503 with a reason. Code that treats them alike will eventually report a healthy zero for an outage — retry on 5xx rather than caching the emptiness.