Pump.fun bonding curve progress: get it via API (with the math)
Every pump.fun token spends its early life on a bonding curve: a deterministic pricing contract that sells tokens for SOL until its inventory runs out and the token graduates to PumpSwap. If you want to check bonding curve progress on pump.fun programmatically — how far along a token is, what the curve-implied price is, how much SOL the curve actually holds — you need exactly four fields, and the Raiden API returns all four on every single swap. This is the full walkthrough: the math, the fields, and a Python function that turns them into a progress number you can trust.
What the bonding curve actually is
Until graduation there is no order book and no liquidity providers. The curve prices
every trade with a constant-product invariant over virtual reserves:
k = v_sol × v_token. A buy deposits SOL into the
virtual SOL side and withdraws tokens from the virtual token side, keeping
k constant — so each successive buy gets fewer tokens per SOL and the
price rises along a smooth, predetermined path. Sells run the same math in reverse.
The reserves are called virtual because they include a synthetic offset that
sets the starting price; they are pricing variables, not balances. What the curve
actually holds — the SOL paid in, the tokens not yet sold — lives in a separate
pair of real reserves. When the curve's real token inventory is exhausted, the
token graduates: liquidity moves to a PumpSwap pool and curve math stops applying.
The four reserve fields, on every swap
This is where the pump.fun bonding curve API angle gets simple. Raiden indexes
every fill, and each row of /tokens/{mint}/swaps carries the complete
curve state after that fill:
| Field | What it is | What you use it for |
|---|---|---|
v_sol_reserves | virtual SOL reserve after the fill (lamports, string) | price numerator |
v_token_reserves | virtual token reserve after the fill (base units, string) | price denominator |
real_sol_reserves | SOL the curve actually holds | how much has been paid into the curve |
real_token_reserves | tokens actually left in the curve | progress — drains toward zero |
Because the state rides on every swap, you never need a separate "curve state" endpoint or an on-chain account fetch: the latest swap is the current state, and any historical swap is the state at that moment — which makes progress-over-time charts a pagination exercise, not an archive-node problem.
Pump.fun bonding curve calculation: price from virtual reserves
One call gets the current state — the most recent fill:
curl -H "X-API-Key: $RAIDEN_KEY" \ "https://terminal.raiden.wtf/api/tokens/MINT/swaps?order=desc&limit=1"
{
"data": [
{
"time": "2026-07-18T11:02:44Z",
"mint": "9xQm…pump",
"trader": "8psN…VRtf",
"is_buy": true,
"sol_amount": "488888889",
"token_amount": "16528925619834",
"price": "0.00000002958",
"v_sol_reserves": "30488888889",
"v_token_reserves": "1041666666666666",
"real_sol_reserves": "488888889",
"real_token_reserves": "761250000000000",
"slot": 348812994,
"block_index": 1287,
"sig": "4Fr4…ULFK",
"venue": 0
… fee, creator_fee, tip, cu_price, cu_limit, priority_fee …
}
],
"next_cursor": "2026-07-18T11:02:44Z"
}The pump.fun bonding curve calculation for price is one division — the virtual reserves are the curve's spot price:
# curve spot price in SOL per token, adjusted for decimals: # SOL side is in lamports (1 SOL = 1e9), token side in base units (6 decimals) spot_price = (v_sol_reserves / 1e9) / (v_token_reserves / 1e6) # with the row above: # (30488888889 / 1e9) / (1041666666666666 / 1e6) # = 30.488888889 SOL / 1,041,666,666.67 tokens ≈ 0.00000002927 SOL # — the price the NEXT fill will trade around
decimals: 6 for pump.fun mints, hence the
1e6. Parse with int(), never float(), before
doing math. Each row also ships a precomputed price field — that one is
the fill's own execution price, sol_amount / token_amount
(decimal-adjusted), so it sits a hair away from the after-fill reserve ratio: the
ratio is the curve's spot price for the next trade.How to check bonding curve progress
Progress is the token side draining. Every buy removes tokens from
real_token_reserves; graduation happens when the curve's inventory is
gone. So the honest measure is a ratio between two observed states: the curve
now, versus the curve at the start of its observable life. Fetch the token's first
indexed swap with order=asc&limit=1, reconstruct the pre-fill state
(if that first swap was a buy, the curve held its real_token_reserves
plus the token_amount it sold), and divide:
progress = 1 − real_token_now / real_token_start
A token at 0.9 has sold 90% of the inventory it started with; a token
stuck at 0.05 for hours is dead on the curve. The complementary signal is
real_sol_reserves climbing — the SOL actually collected — which is the
number worth charting next to volume when you want to separate genuine accumulation
from wash churn.
status flips to "graduated",
graduated_at timestamps it, amm_pool points at the PumpSwap
pool.A Python progress function
Everything above, in one function you can drop into a monitor loop:
import requests BASE = "https://terminal.raiden.wtf/api" H = {"X-API-Key": "YOUR_KEY"} def curve_price(row): # SOL side: lamports (1e9) · token side: base units (6 decimals) return (int(row["v_sol_reserves"]) / 1e9) / (int(row["v_token_reserves"]) / 1e6) def curve_progress(mint): tok = requests.get(f"{BASE}/tokens/{mint}", headers=H).json() if tok["status"] == "graduated": return {"status": "graduated", "graduated_at": tok["graduated_at"], "progress": 1.0} swaps = lambda **p: requests.get(f"{BASE}/tokens/{mint}/swaps", params=p, headers=H).json()["data"] last, first = swaps(order="desc", limit=1), swaps(order="asc", limit=1) if not last or not first: return {"status": tok["status"], "progress": 0.0} last, first = last[0], first[0] # curve state BEFORE the first observed fill: # a buy took token_amount OUT of the curve, so add it back start = int(first["real_token_reserves"]) if first["is_buy"]: start += int(first["token_amount"]) now = int(last["real_token_reserves"]) return { "status": tok["status"], # "bonding" "progress": 1 - now / start, # share of observed inventory sold "price_sol": curve_price(last), "sol_in_curve": int(last["real_sol_reserves"]) / 1e9, } print(curve_progress("9xQm…pump")) # {'status': 'bonding', 'progress': 0.269, 'price_sol': 2.927e-08, 'sol_in_curve': 0.489}
Three calls per check, all cheap. For a progress-over-time chart, paginate
order=asc with the cursor and plot
real_token_reserves per fill — the full download recipe is in the
historical data with Python guide.
After graduation: PumpSwap, candles and the venue tag
The honest caveat: reserve math describes the curve, and the curve ends at
graduation. From that point the token trades on PumpSwap, and its price comes from
the same places every AMM price comes from — the indexed
/tokens/{mint}/swaps fills and /tokens/{mint}/candles
(tf=1s|30s|1m|1h|1d), with each swap row tagged by its venue
so curve-era and PumpSwap-era fills are cleanly separable in one continuous history.
The token object keeps serving last_price either way. If graduations are
the event you actually care about, two follow-ups: the
graduated tokens guide covers listing
and exporting them via the screener's status filter, and a
WebSocket stream with the
grad kind pushes a graduate event the moment a curve
completes — no polling loop required.
That is the whole system: virtual reserves set the price, real reserves measure the
progress, status and graduated_at confirm the finish line —
all from swap rows you can paginate freely. Full request and response shapes for every
endpoint used here are in the API reference, and the
pump.fun API overview covers access.
Frequently asked questions
What is bonding curve progress on pump.fun?
The share of the curve's token inventory that buyers have already taken out. Every buy drains real_token_reserves toward zero; when the curve is exhausted the token graduates to PumpSwap. The robust way to measure it is a ratio computed from observed reserves — current real_token_reserves against the curve state at the first observed swap — not a comparison against a hardcoded protocol constant.
How is the pump.fun token price calculated on the bonding curve?
From the VIRTUAL reserves, constant-product style: spot price = v_sol_reserves / v_token_reserves, adjusted for decimals. Divide the SOL side by 1e9 (amounts are lamport strings) and the token side by 1e6 (pump.fun tokens have 6 decimals) to get SOL per token. Every indexed swap carries both after-fill virtual reserves, so the curve price is recomputable at any point in a token's history; each row also ships a price field with that fill's own execution price (sol_amount / token_amount, decimal-adjusted).
What is the difference between virtual and real reserves?
Virtual reserves (v_sol_reserves, v_token_reserves) are the pricing variables of the constant-product formula — they include a synthetic offset, so they never reflect actual balances. Real reserves (real_sol_reserves, real_token_reserves) are what the curve actually holds: the SOL collected from buyers and the tokens still available. Price comes from the virtual pair; progress comes from the real pair.
How do I know when a pump.fun token has graduated?
Authoritatively from the token object: status flips to "graduated", graduated_at records when, and amm_pool points at the PumpSwap pool. Do not infer graduation from a hardcoded SOL threshold — curve parameters are protocol internals, not part of the API contract, and can change. For realtime, the grad WebSocket kind pushes a graduate event the moment it happens.
Why shouldn't I hardcode a SOL graduation target in my progress formula?
Because the curve's initial reserve sizes and completion criteria are pump.fun implementation details that can be changed by the protocol at any time. A formula anchored to a magic number silently breaks the day the parameters move. A ratio built from observed reserves keeps working, and tokens.status / graduated_at confirm the actual event.
Can I still compute price from reserves after graduation?
Not with curve math. The constant-product formula describes the bonding curve, and after graduation trading moves to PumpSwap. Post-graduation prices come from the same swap rows and OHLCV candles, tagged with the venue field — the token's price history stays continuous through graduation, it just stops being curve math.