Developers · API v1
Build on satpad
Read every satpad coin and build its transactions over HTTP. Put them in a bot, a wallet, a dashboard or your own trading front end, without learning Meteora's SDKs. Each coin has 21,000,000 supply, trades against BTC on a Meteora bonding curve that halves across four eras, and graduates to a Meteora DAMM v2 pool.
- No API key. JSON over HTTPS, open to every origin (CORS).
- Your users keep their keys. Trades and launches come back as unsigned transactions; the user's wallet signs and sends them.
- Curve or pool, handled. Buys and sells route to the bonding curve or, after graduation, the DAMM v2 pool. Buys can pay in SOL.
- Live events. One stream for new coins, halvings, graduations and burns.
| Base URL | https://satpad.fun/api/v1 |
| Network | Solana: real BTC (cbBTC) is at stake |
| OpenAPI | /api/v1/openapi.json |
| Status | A hackathon submission (Colosseum Crypto World's Fair, Meteora DBC side track). The v1 contract is stable; the service has no uptime guarantee. |
Start in three calls
Find a coin, quote it, sign the buy.
1. Find a coin
curl "https://satpad.fun/api/v1/coins?sort=progress&graduated=false&limit=5"2. Quote it
curl "https://satpad.fun/api/v1/quote?mint=<mint>&side=buy&amount=0.001"3. Build the transaction, sign it in the wallet, send it
import { Transaction, VersionedTransaction } from "@solana/web3.js";
const res = await fetch("https://satpad.fun/api/v1/tx/buy", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ wallet: wallet.publicKey.toBase58(), mint, btc: 0.001, slippageBps: 200 }),
}).then((r) => r.json());
if (res.error) throw new Error(res.error);
const bytes = Uint8Array.from(atob(res.transaction), (c) => c.charCodeAt(0));
const tx = res.version === 0 ? VersionedTransaction.deserialize(bytes) : Transaction.from(bytes);
const signature = await wallet.sendTransaction(tx, connection);Learn the concepts
Units
| Quantity | Unit in the API | On chain |
|---|---|---|
| BTC amounts | BTC, a decimal number | 8 decimals (1 BTC = 10^8 atoms = 100,000,000 sats) |
| Coin amounts | Whole coins, a decimal number | 6 decimals, 21,000,000 supply per coin |
| Prices | Sats per coin (priceSats) | Read from the pool's sqrt price |
| Fully diluted value | BTC (fdvBtc) | priceSats × 21,000,000 ÷ 10^8 |
| Times | Unix seconds (event timestamps at: Unix ms) | — |
Numbers are JSON floats, fine for display and quotes. The transactions themselves use exact integer amounts, and out.min is what the chain enforces.
A coin's life
- Launch. A creator picks a curve tier:
light(0.125 BTC),standard(0.25 BTC),deep(0.5 BTC),max(1 BTC). The tier sets how much BTC the curve raises before graduation, and with it every price on the curve. Supply, eras and fees are the same in every tier. The creator also picks amode:creator(the creator share of fees goes to the creator),holders(it goes to the coin's holders: 60% of every fee, credited pro rata at snapshots and claimed through/holders/claim) orbtc(it is paid in BTC to a Bitcoin address bound at launch, after bridge fees, in payouts worth $10 or more). Coins on the first ladder carry legacy tier ids (satoshi, hal, whitepaper, bitcoin) andlegacy: true; new launches can't use them. - Binding a BTC payout address. A
btclaunch carries exactly one Memo v2 instruction with the textsatpad btc payout: v1 <address>(bech32 addresses lowercase), listing the creator (the pool creator and fee payer) as a signer key, in the pool-creation transaction.tx/launchwithbtcAddressadds it for you. The keeper reads it back from the creation transaction; no memo, two memos, an address for another network or a memo the creator didn't sign leave the coin unbound, and its creator share goes to the platform. The address can never be changed. - Anti-snipe. On the current configs the fee starts high and drops each second after launch (49.99% / 33.66% / 17.33% / 1%);
feeBpsNowon a coin andfeeBpson a quote show the live value. The bundledfirstBuyBtcintx/launchpays 1%. - Bonding curve, four eras. Genesis, Halving I, Halving II, Final Era. In each era the price doubles and the coins sold halve, so each era raises the same BTC. Entering the next era is a halving:
era.indexgoes up and you get acoin.halvingevent. Each trade pays a 1% fee in BTC. - Graduation. When
btcRaisedreachesgraduationBtc, the coin moves to a Meteora DAMM v2 pool (migrated: true,dammPoolset, acoin.graduatedevent). Trading continues there; the API routes to it automatically and quotes showvenue: "dammV2".
Which network
This deployment serves Solana. Every response carries an X-Cluster header (mainnet-beta), and GET /api/v1 returns cluster. Check it before asking a user to sign, and send the transaction to an RPC on the same network.
Sign transactions
transactionis base64. Decode it, thenTransaction.from(bytes)whenversionis"legacy", orVersionedTransaction.deserialize(bytes)when it is0(buys paid in SOL).- The fee payer is
wallet. Nothing is signed on our side. Launches also need the new mint keypair's signature (see the launch recipe). - The transaction has a recent blockhash: sign and send within about 60 seconds, or build a new one.
- Slippage protection is on chain: if the price moves past
slippageBps, the transaction fails and nothing is spent but the network fee. - Simulate before sending if you want an early error (insufficient BTC, for example); wallets usually do this for you.
Handle errors and limits
Errors are JSON: { "error": "slippageBps: an integer from 0 to 5000" }. The message says what to fix.
| Status | Meaning |
|---|---|
400 | Bad input: a parameter is missing or invalid. |
404 | Not a satpad coin on this network. |
409 | Launch: that mint already exists. |
413 / 415 | Metadata: the image is too big, or not PNG, JPG or GIF. |
429 | Rate limited. retryAfterSecs says when to retry. |
502 | A chain read or route failed upstream. Safe to retry. |
503 | Not deployed on this network yet, or the event stream is full. |
Limits per IP address:
| Bucket | Limit | Endpoints |
|---|---|---|
read | 120 per 1 min | /, /coins, /coins/{mint}, /curves, /burns, /btc-usd, /coins/{mint}/payouts, /coins/{mint}/holders, /coins/{mint}/holders/{wallet}, /holders/claim/status |
quote | 60 per 1 min | /quote, /btc-address/check |
tx | 30 per 1 min | /tx/buy, /tx/sell |
claimPrepare | 10 per 1 min | /holders/claim/prepare |
claimSubmit | 5 per 1 min | /holders/claim/submit |
launch | 5 per 10 min | /tx/launch |
metadata | 10 per 10 min | /metadata |
stream | 10 per 1 min | /events |
Need more? Cache the read endpoints (their Cache-Control headers say for how long) and use the event stream instead of polling.
API reference
Market
Get the API index
GET/api/v1
The network this deployment serves, every curve tier with its DBC config per launch mode (and the legacy configs), the anti-snipe schedule, the holders-mode and BTC payout constants, the BTC quote mint, token rules, fees, program IDs, the keeper and treasury, and the endpoint list. Read it once at startup and check cluster.
Limit 120 reads per 1 min per IP · cached 1 min
curl https://satpad.fun/api/v1{
"name": "satpad API",
"version": "1.1",
"cluster": "mainnet-beta",
"deployed": true,
"token": {
"supply": 21000000,
"decimals": 6,
"mintAuthority": null,
"freezeAuthority": null
},
"quote": {
"mint": "<btc mint>",
"decimals": 8
},
"tiers": [
{
"id": "light",
"name": "Light",
"graduationBtc": 0.125,
"configs": {
"creator": "<config>",
"holders": "<config>",
"btc": "<config>"
}
},
"…"
],
"legacyConfigs": [
{
"id": "satoshi",
"name": "Satoshi",
"graduationBtc": 0.1,
"mode": "creator",
"config": "<config>"
},
"…"
],
"modes": [
{
"id": "creator",
"name": "Creator"
},
{
"id": "holders",
"name": "Holders"
},
{
"id": "btc",
"name": "BTC payout"
}
],
"antiSnipe": {
"startFeeBps": 4999,
"endFeeBps": 100,
"periods": 3,
"seconds": 3,
"mode": "linear"
},
"eras": [
{
"index": 0,
"name": "Genesis"
},
{
"index": 1,
"name": "Halving I"
},
{
"index": 2,
"name": "Halving II"
},
{
"index": 3,
"name": "Final Era"
}
],
"fees": {
"tradingFeeBps": 100,
"protocolShareBps": 2000,
"creatorShareBps": 7500,
"buybackShareOfFees": 0.15,
"treasuryShareOfFees": 0.05,
"modes": {
"creator": "…",
"holders": "…",
"btc": "…"
}
},
"holders": {
"distributor": "<address>",
"shareOfPartnerBps": 7500,
"shareOfLpBps": 8000,
"minUsd": 10,
"minSatsNow": 11765,
"minClaimSats": 500,
"…": "…"
},
"btcPayout": {
"memo": "satpad btc payout: v1 <address>",
"network": "Bitcoin",
"minUsd": 10,
"maxFeeBps": 1000,
"dormant": {
"minUsd": 5,
"days": 90,
"maxFeeBps": 2000
},
"…": "…"
},
"programs": {
"dbc": "<program>",
"dammV2": "<program>"
},
"keeper": "<address>",
"treasury": "<address>",
"nativeCoin": {
"symbol": "21",
"mint": "<mint>"
},
"endpoints": {
"GET /api/v1/coins": "…"
}
}List coins
GET/api/v1/coins
Every coin with its live state and metadata. The list is refreshed every few seconds; page through it with limit and offset.
Limit 120 reads per 1 min per IP · cached 3 s
Query
sortstring- Newest first, largest fully diluted value first, or closest to graduation first.One of
new,fdv,progress.Defaultnew. graduatedbooleantrue: only coins trading on DAMM v2.false: only coins still on the bonding curve.tierstring- Only coins on this curve tier (legacy ids select coins from the first ladder).One of
light,standard,deep,max,satoshi,hal,whitepaper,bitcoin. modestring- Only coins in this launch mode.One of
creator,holders,btc. limitinteger- Page size, 1 to 200.Default
50. offsetinteger- Coins to skip.Default
0.
curl "https://satpad.fun/api/v1/coins?sort=progress&graduated=false&limit=10"{
"total": 42,
"offset": 0,
"limit": 10,
"coins": [
{
"mint": "<mint>",
"pool": "<dbc pool>",
"config": "<config>",
"tier": "light",
"mode": "creator",
"legacy": false,
"feeBpsNow": 100,
"antiSnipeUntil": 1791103359,
"creator": "<wallet>",
"activatedAt": 1791103356,
"priceSats": 3.3362,
"fdvBtc": 0.7006,
"btcRaised": 0.201,
"graduationBtc": 0.125,
"progress": 0.201,
"era": {
"index": 0,
"name": "Genesis",
"progress": 0.8033,
"coinsToHalving": 1390686.34
},
"coinsSold": 8030111.07,
"migrated": false,
"dammPool": null,
"partnerFeeBtcUnclaimed": 1.1e-7,
"tradingFeeBtcTotal": 0.00002011,
"creatorFeeBtcUnclaimed": 3.3e-7,
"creatorSurplusPending": false,
"meta": {
"name": "Satoshi Cat",
"symbol": "SCAT",
"uri": "https://…/metadata.json",
"image": "https://…/image.png",
"description": "…",
"socials": {
"twitter": "https://x.com/…"
}
},
"isNative": false
},
"…"
]
}400A parameter is missing or invalid;errornames it.
Get a coin
GET/api/v1/coins/{mint}
One coin's live state and metadata, the same object as in the list.
Limit 120 reads per 1 min per IP · cached 2 s
Path
mintstringrequired- The coin's mint address.
curl https://satpad.fun/api/v1/coins/<mint>{
"mint": "<mint>",
"pool": "<dbc pool>",
"config": "<config>",
"tier": "light",
"mode": "creator",
"legacy": false,
"feeBpsNow": 100,
"antiSnipeUntil": 1791103359,
"creator": "<wallet>",
"activatedAt": 1791103356,
"priceSats": 3.3362,
"fdvBtc": 0.7006,
"btcRaised": 0.201,
"graduationBtc": 0.125,
"progress": 0.201,
"era": {
"index": 0,
"name": "Genesis",
"progress": 0.8033,
"coinsToHalving": 1390686.34
},
"coinsSold": 8030111.07,
"migrated": false,
"dammPool": null,
"partnerFeeBtcUnclaimed": 1.1e-7,
"tradingFeeBtcTotal": 0.00002011,
"creatorFeeBtcUnclaimed": 3.3e-7,
"creatorSurplusPending": false,
"meta": {
"name": "Satoshi Cat",
"symbol": "SCAT",
"uri": "https://…/metadata.json",
"image": "https://…/image.png",
"description": "…",
"socials": {
"twitter": "https://x.com/…"
}
},
"isNative": false
}400A parameter is missing or invalid;errornames it.404The mint is not a coin on any of this network's curve tiers.
Get the halving curves
GET/api/v1/curves
Each launchable tier's curve (both launch modes of a tier share it): the four eras with their BTC and price ranges and coins sold, sampled points for charting, and the coins that go into the DAMM v2 pool at graduation. Curves are immutable on chain, so cache this as long as you like.
Limit 120 reads per 1 min per IP · cached 60 min
curl https://satpad.fun/api/v1/curves{
"light": {
"tier": "light",
"graduationBtc": 0.125,
"eras": [
{
"index": 0,
"name": "Genesis",
"fromBtc": 0,
"toBtc": 0.025025,
"fromSats": 0.1878,
"toSats": 0.3757,
"coins": 9420797.41
},
{
"index": 1,
"name": "Halving I",
"fromBtc": 0.025025,
"toBtc": 0.05005,
"fromSats": 0.3757,
"toSats": 0.7513,
"coins": 4710398.7
},
"…"
],
"points": [
{
"coins": 0,
"sats": 0.1878,
"btc": 0,
"era": 0
},
{
"coins": 329661.73,
"sats": 0.1917,
"btc": 0.00062562,
"era": 0
},
"…"
],
"lpCoins": 3335232.78
},
"standard": "…"
}Get buyback & burn
GET/api/v1/burns
The native coin's circulating and burned supply (21,000,000 minus the mint's supply), the treasury's BTC balance, and the keeper's buyback & burn transactions, newest first. why is buyback or leftover (a graduated coin's unsold curve remainder).
Limit 120 reads per 1 min per IP · cached 15 s
curl https://satpad.fun/api/v1/burns{
"nativeMint": "<mint>",
"nativeSymbol": "21",
"keeper": "<address>",
"burnedCoins": 21955.54,
"supplyCoins": 20978044.46,
"treasury": {
"address": "<address>",
"btc": 0.00005112
},
"burns": [
{
"signature": "<signature>",
"time": 1791140807,
"mint": "<mint>",
"coins": 10.2039,
"why": "buyback"
},
"…"
]
}Get BTC/USD
GET/api/v1/btc-usd
The BTC/USD rate the site uses for dollar figures; null when every price source is down. Multiply a BTC amount by it, or priceSats / 1e8 × usd for a coin's dollar price.
Limit 120 reads per 1 min per IP · cached 30 s
curl https://satpad.fun/api/v1/btc-usd{
"usd": 85811
}Trading
Quote a trade
GET/api/v1/quote
What a buy or sell would return right now, without building a transaction. On the bonding curve it also previews the era after the trade: whether it crosses a halving, or completes the curve and graduates. That preview uses the BTC raised after the 1% fee; treat it as an estimate.
Limit 60 quotes per 1 min per IP · cached 2 s
Query
mintstringrequired- The coin.
sidestring- Buy coins, or sell coins for BTC.One of
buy,sell.Defaultbuy. amountnumberrequired- Buys: BTC to spend (or SOL with
in=sol). Sells: coins to sell. instring- Buys only: pay in BTC, or in SOL swapped to BTC in the same transaction.One of
btc,sol.Defaultbtc. slippageBpsinteger- Sets
out.min, 0 to 5000.Default200.
curl "https://satpad.fun/api/v1/quote?mint=<mint>&side=buy&amount=0.03"{
"mint": "<mint>",
"side": "buy",
"in": {
"asset": "btc",
"amount": 0.03
},
"out": {
"asset": "coin",
"expected": 858566.35,
"min": 841395.02
},
"viaBtc": null,
"slippageBps": 200,
"spotPriceSats": 3.3362,
"avgPriceSats": 3.4942,
"priceImpact": 0.0474,
"venue": "curve",
"era": {
"before": 0,
"after": 1,
"crossesHalving": true,
"graduates": false
}
}400A parameter is missing or invalid;errornames it.404The mint is not a coin on any of this network's curve tiers.
Build a buy
POST/api/v1/tx/buy
An unsigned transaction that buys the coin for wallet. Send exactly one of btc or sol. With sol, one v0 transaction swaps SOL to BTC and buys with the BTC that swap guarantees; any extra BTC stays in the wallet. Graduated coins are bought on their DAMM v2 pool.
Limit 30 buy / sell transactions per 1 min per IP · not cached
JSON body
walletstringrequired- The buyer; signs and pays fees.
mintstringrequired- The coin.
btcnumber- BTC to spend.
solnumber- SOL to spend instead of BTC.
slippageBpsinteger- Maximum slippage in basis points, 0 to 5000. 200 = 2%.Default
200.
curl -X POST https://satpad.fun/api/v1/tx/buy -H "Content-Type: application/json" \
-d '{"wallet":"<wallet>","mint":"<mint>","btc":0.001,"slippageBps":200}'{
"transaction": "<base64>",
"version": "legacy",
"mint": "<mint>",
"wallet": "<wallet>",
"in": {
"asset": "btc",
"amount": 0.001
},
"expectedCoins": 29711.4,
"minCoins": 29117.17,
"note": "Sign with the wallet and send within ~60 s (the blockhash expires)."
}400A parameter is missing or invalid;errornames it.404The mint is not a coin on any of this network's curve tiers.502The chain read or the SOL route failed (for SOL buys: try a different amount, or pay in BTC).
Build a sell
POST/api/v1/tx/sell
An unsigned transaction that sells coins from wallet for BTC, on the bonding curve or the DAMM v2 pool.
Limit 30 buy / sell transactions per 1 min per IP · not cached
JSON body
walletstringrequired- The seller; signs and pays fees.
mintstringrequired- The coin.
coinsnumberrequired- Coins to sell.
slippageBpsinteger- Maximum slippage in basis points, 0 to 5000. 200 = 2%.Default
200.
curl -X POST https://satpad.fun/api/v1/tx/sell -H "Content-Type: application/json" \
-d '{"wallet":"<wallet>","mint":"<mint>","coins":1000}'{
"transaction": "<base64>",
"version": "legacy",
"mint": "<mint>",
"wallet": "<wallet>",
"in": {
"asset": "coin",
"amount": 1000
},
"expectedBtc": 0.00031037,
"minBtc": 0.00030416,
"note": "Sign with the wallet and send within ~60 s (the blockhash expires)."
}400A parameter is missing or invalid;errornames it.404The mint is not a coin on any of this network's curve tiers.
Launch
Upload metadata
POST/api/v1/metadata
Stores the coin's image and a Metaplex-style metadata JSON on permanent storage and returns its uri for the launch. Send the image as a file, or link one with imageUrl.
Limit 10 metadata uploads per 10 min per IP · not cached
Form fields (multipart/form-data)
namestringrequired- Up to 32 characters.
symbolstringrequired- 2 to 10 letters or digits; uppercased.
imagefile- PNG, JPG or GIF, at most 1 MB.
imageUrlstring- An http(s) image URL instead of a file.
descriptionstring- Up to 500 characters.
twitterstring- http(s) URL.
telegramstring- http(s) URL.
websitestring- http(s) URL.
curl -X POST https://satpad.fun/api/v1/metadata \
-F name="Satoshi Cat" -F symbol=SCAT -F image=@cat.png -F twitter=https://x.com/satoshicat{
"uri": "https://…/metadata.json",
"image": "https://…/image.png",
"provider": "…"
}400A parameter is missing or invalid;errornames it.413The image is over 1 MB.415The image is not a PNG, JPG or GIF.502The storage provider failed.
Build a launch
POST/api/v1/tx/launch
A transaction that creates the coin on the chosen curve tier and launch mode, with an optional first buy in the same transaction so nobody can buy ahead of the creator. The first buy pays the 1% fee; separate buys in the first 3 seconds pay the anti-snipe fee (49.99% / 33.66% / 17.33% / 1%). Legacy tier ids are refused with the id to use instead (use). Mode btc needs btcAddress: the transaction then ends with one Memo v2 instruction satpad btc payout: v1 <address> listing the wallet as a signer, which binds the address for good (a launch built elsewhere must carry exactly that one memo, or its creator share goes to the platform). Generate a fresh keypair for the mint and send only its public key: the transaction needs the wallet's and the mint keypair's signatures. Supply, decimals and the revoked mint and freeze authorities come from the tier's config and can't be changed.
Limit 5 launch transactions per 10 min per IP · not cached
JSON body
walletstringrequired- The creator; signs and pays fees. In creator mode it receives the creator share of trading fees.
mintstringrequired- Public key of a fresh keypair you generated.
tierstring- The curve's graduation target: 0.125, 0.25, 0.5 or 1 BTC. Permanent. Old ids (satoshi → light, hal → standard, whitepaper → deep, bitcoin → max) get a 400 naming the new one.One of
light,standard,deep,max.Defaultlight. modestring- Who gets the creator share (60% of every fee): the creator, the coin's holders (credited pro rata, claimed with /holders/claim), or btc (paid in BTC to btcAddress, after bridge fees). Permanent.One of
creator,holders,btc.Defaultcreator. btcAddressstring- Mode btc only, then required: the native Bitcoin address the creator share is paid to (bc1q, bc1p, 1 or 3 on mainnet), in payouts of 0.00012 BTC or more after bridge fees. Bound by the launch memo: permanent, no update path.
namestringrequired- Up to 32 characters.
symbolstringrequired- 2 to 10 letters or digits.
uristringrequired- The metadata JSON URL, from POST /api/v1/metadata.
firstBuyBtcnumber- BTC the creator buys with in the launch transaction.Default
0.
curl -X POST https://satpad.fun/api/v1/tx/launch -H "Content-Type: application/json" \
-d '{"wallet":"<wallet>","mint":"<new mint>","tier":"light","mode":"creator","name":"Satoshi Cat",
"symbol":"SCAT","uri":"https://…/metadata.json","firstBuyBtc":0.001}'{
"transaction": "<base64>",
"version": "legacy",
"mint": "<mint>",
"wallet": "<wallet>",
"tier": "light",
"mode": "creator",
"config": "<config>",
"pool": "<dbc pool>",
"firstBuyBtc": 0.001,
"btcAddress": "(mode btc) <address>",
"btcPayoutMemo": "(mode btc) satpad btc payout: v1 <address>",
"signers": [
"<wallet>",
"<mint>"
],
"note": "Sign with the wallet and the mint keypair, then send within ~60 s (the blockhash expires)."
}400A parameter is missing or invalid;errornames it.409That mint already has a pool: generate a fresh keypair.422Mode btc: the bridge refuses btcAddress (mainnet). Use another address.
Holders
Get a coin's holder totals
GET/api/v1/coins/{mint}/holders
Holders-mode coins: 60% of every trading fee is the holders' fee share, credited in BTC to wallets holding at least $10 of the coin (valued at each snapshot: coins × the coin's price × BTC/USD), pro rata, at frequent random snapshots. It depends only on trading volume and can be zero; it is not a return on buying the coin. This returns what was credited and paid so far, in sats, and the threshold: minUsd, and minSats / minCoins at the current prices.
Limit 120 reads per 1 min per IP · cached 5 s
Path
mintstringrequired- The coin.
curl https://satpad.fun/api/v1/coins/<mint>/holders{
"mint": "<mint>",
"tracked": true,
"creditedSats": 13398,
"paidSats": 4100,
"pendingSats": 0,
"wallets": 4,
"lastSnapshotAt": "2026-10-05T16:20:46.937Z",
"feeCursorAtoms": 17864,
"claimsPaused": false,
"minUsd": 10,
"minSats": 11765,
"minCoins": 51153
}404Not a holders-mode coin (or not snapshotted yet).503This server has no holder ledger.
Prepare a claim
POST/api/v1/holders/claim/prepare
The wallet's whole claimable share (from 500 sats) as a legacy transaction with the holder as fee payer, plus a token bound to its exact bytes. Sign it with the wallet (sign only, don't send): the holder pays the network fee and, once, the rent of their BTC token account. Nothing is reserved yet. Limit: 10 per minute per wallet and per IP.
Limit 10 claim prepares per 1 min per IP · not cached
JSON body
mintstringrequired- The coin.
walletstringrequired- The holder (signs as fee payer).
curl -X POST https://satpad.fun/api/v1/holders/claim/prepare -H "Content-Type: application/json" -d '{"mint":"<mint>","wallet":"<wallet>"}'{
"transaction": "<base64>",
"token": "<token>",
"sats": 4817,
"lastValidBlockHeight": 491234567
}400A parameter is missing or invalid;errornames it.409A claim is already in flight.429Rate limited.503Claims are paused or not set up.
Submit a signed claim
POST/api/v1/holders/claim/submit
The holder-signed transaction from prepare. The claim service checks the bytes are exactly the prepared ones and the holder's signature, reserves the amount, then co-signs and sends. Any change to the transaction (for example an instruction a wallet adds) is refused: prepare again. Poll the status until it settles. Limit: 5 per minute per wallet and per IP.
Limit 5 claim submits per 1 min per IP · not cached
JSON body
tokenstringrequired- From prepare.
signedTxstringrequired- The transaction signed by the holder, base64.
curl -X POST https://satpad.fun/api/v1/holders/claim/submit -H "Content-Type: application/json" -d '{"token":"<token>","signedTx":"<base64>"}'{
"signature": "<signature>",
"status": "pending",
"wallet": "<wallet>"
}400A parameter is missing or invalid;errornames it.409Over the claimable share, or a claim already in flight.410The blockhash is about to expire: prepare again.503Funds settling (try again in about a minute) or claims paused.
Get a claim's status
GET/api/v1/holders/claim/status
pending until the chain confirms the transfer (confirmed), it lands with an error (failed: nothing deducted), or its blockhash dies without it landing (expired: nothing deducted).
Limit 120 reads per 1 min per IP · not cached
Query
signaturestringrequired- The claim's signature from submit.
curl 'https://satpad.fun/api/v1/holders/claim/status?signature=<signature>'{
"signature": "<signature>",
"status": "confirmed",
"sats": 4817,
"mint": "<mint>",
"wallet": "<wallet>",
"error": null
}404No claim with that signature.
Payouts
Check a payout address
POST/api/v1/btc-address/check
Would a btc-mode launch accept this address: its checksum and network, and on mainnet a read-only quote from the bridge (Relay) to it. tx/launch runs the same check.
Limit 60 quotes per 1 min per IP · not cached
JSON body
addressstringrequired- The Bitcoin address.
curl -X POST https://satpad.fun/api/v1/btc-address/check -H "Content-Type: application/json" -d '{"address":"<address>"}'{
"ok": true,
"address": "<address>",
"type": "p2tr",
"bridge": "relay"
}400Not a valid address for this network.422The bridge refuses this address.503The bridge didn't answer; try again.
Get a coin's BTC payouts
GET/api/v1/coins/{mint}/payouts
BTC payout coins: 60% of every trading fee (and 80% of LP fees after graduation) is owed to the Bitcoin address bound at launch and paid automatically once 12,000 sats or more are owed and the bridge costs at most 10% (after 90 days without a payout: from 6,000 sats at up to 20%). Bridge fees come out of the payout. Amounts depend on trading volume and can be zero; it is not a return. Each payout: gross sent into the bridge, bridge fee, net BTC delivered, the Bitcoin txid (mainnet) and the Solana deposit.
Limit 120 reads per 1 min per IP · cached 5 s
Path
mintstringrequired- The coin.
curl https://satpad.fun/api/v1/coins/<mint>/payouts{
"mint": "<mint>",
"status": "bound",
"address": "<address>",
"addressUrl": "https://mempool.space/address/<address>",
"bindSignature": "<launch signature>",
"unboundReason": null,
"owedSats": 31044,
"inFlightSats": 0,
"paidGrossSats": 90744,
"paidNetSats": 89713,
"minPayoutUsd": 10,
"history": [
{
"id": 1,
"state": "btc_confirmed",
"createdAt": "2026-10-05T17:49:51.000Z",
"settledAt": "2026-10-05T17:51:17.000Z",
"grossSats": 90744,
"feeSats": 1031,
"netSats": 89713,
"btcTxid": "<txid>",
"btcTxUrl": "https://mempool.space/tx/<txid>",
"depositSignature": "<signature>",
"refundedSats": null
}
]
}404Not a BTC payout coin (or not registered yet), or no payout ledger on this server.
Events
Stream events
GET/api/v1/events
Server-sent events (text/event-stream) for new coins, halvings, graduations and burns, a few seconds after they land on chain. Each event has an id; reconnect with the Last-Event-ID header (browsers' EventSource does this for you) or ?since=<id> to receive what you missed. The server keeps the last 200 events since it started.
Limit 10 event stream connections per 1 min per IP · not cached
Query
sinceinteger- Replay buffered events after this id.
curl -N https://satpad.fun/api/v1/eventsevent: ready
data: {"cluster":"mainnet-beta"}
id: 7
event: coin.halving
data: {"mint":"<mint>","symbol":"SCAT","name":"Satoshi Cat","tier":"light","fromEra":0,"toEra":1,"eraName":"Halving I","priceSats":0.4494,"at":1791147084827}
: ping503Too many open streams on the server; retry after theRetry-Afterseconds.
Objects
Coin
Returned by /coins and /coins/{mint}.
mintstring- The coin's mint address.
poolstring- Its Meteora DBC pool.
configstring- The DBC config it launched on (one per tier and launch mode).
tierstring- light, standard, deep, max; coins from the first ladder: satoshi, hal, whitepaper, bitcoin.
modestring- creator (the creator gets the creator share of fees), holders (holders do) or btc (it is paid to the Bitcoin address bound at launch).
btcPayoutobject | null- Mode btc only: address, status (bound | unbound | null), owedSats, paidSats (net BTC delivered). Unbound coins (no valid launch memo) credit the share to the platform.
legacyboolean- Launched on the first ladder (flat fee; no new launches).
feeBpsNowinteger- The live base fee in bps: anti-snipe in a coin's first 3 s (49.99% / 33.66% / 17.33% / 1%), then 100.
antiSnipeUntilinteger | null- Unix seconds the anti-snipe schedule ends (null: flat fee).
creatorstring- The wallet that launched it.
activatedAtinteger- Unix seconds trading opened.
priceSatsnumber- Current price in sats per coin, from the curve or, once graduated, the DAMM v2 pool.
fdvBtcnumber- Fully diluted value in BTC: priceSats × 21,000,000 ÷ 10^8.
btcRaisednumber- BTC on the bonding curve.
graduationBtcnumber- BTC raised at which the coin graduates (the tier's target).
progressnumber- btcRaised ÷ graduationBtc, 0 to 1.
era.indexinteger- 0 Genesis, 1 Halving I, 2 Halving II, 3 Final Era
era.namestring- The era's name.
era.progressnumber- 0 to 1 through the current era, by BTC raised.
era.coinsToHalvingnumber- Coins left to buy before the next halving.
coinsSoldnumber- Coins bought off the curve so far.
migratedboolean- Graduated: now trades on Meteora DAMM v2.
dammPoolstring | null- The DAMM v2 pool once graduated.
partnerFeeBtcUnclaimednumber- Partner-side fees on the curve not yet claimed (creator mode: the platform's; holders mode: holders' and platform's).
tradingFeeBtcTotalnumber- Creator-share and platform fees on the curve, all time (Meteora's cut excluded).
creatorFeeBtcUnclaimednumber- The creator's unclaimed fees (always 0 in holders mode).
metaobject- name, symbol, uri, image, description and socials (twitter, telegram, website).
isNativeboolean- This is the platform's native coin, the one fees buy back and burn.
Quote
inobject- What goes in: asset (btc, sol or coin) and amount.
out.expectednumber- Coins (buys) or BTC (sells) out at the current state.
out.minnumber- The least the trade can return with your slippage; the transaction fails below it.
viaBtcnumber | null- SOL buys: the BTC the SOL swap guarantees, which then buys the coin.
spotPriceSatsnumber- Price before the trade, sats per coin.
avgPriceSatsnumber- BTC paid or received ÷ coins, in sats per coin, fee included.
priceImpactnumber- avgPriceSats ÷ spotPriceSats − 1: positive on buys, negative on sells.
venuestring- curve (Meteora DBC) or dammV2 (graduated).
feeBpsinteger- The base fee the quote used: up to 4999 in a new coin's first seconds (anti-snipe), else 100.
eraobject | null- Curve only: before, after (null = graduates), crossesHalving, graduates.
Transaction response
transactionstring- The serialized transaction, base64.
version"legacy" | 0- Deserialize with Transaction.from (legacy) or VersionedTransaction.deserialize (0).
expectedCoins / expectedBtcnumber- What the trade should return.
minCoins / minBtcnumber- The least it can return; below that it fails and nothing is spent.
notestring- Signing reminder.
Event types
From GET /api/v1/events. Every payload has at, the Unix ms the server saw it. The stream also sends a ready event on connect and a comment ping every 15 seconds.
coin.created
A coin launched.
{
"mint": "<mint>",
"symbol": "SCAT",
"name": "Satoshi Cat",
"tier": "light",
"creator": "<wallet>",
"pool": "<dbc pool>",
"activatedAt": 1791147066,
"at": 1791147073843
}coin.halving
A coin on the bonding curve entered its next era: the price has doubled since the era began and each BTC now buys half as many coins.
{
"mint": "<mint>",
"symbol": "SCAT",
"name": "Satoshi Cat",
"tier": "light",
"fromEra": 0,
"toEra": 1,
"eraName": "Halving I",
"priceSats": 0.4494,
"at": 1791147084827
}coin.graduated
A coin raised its tier's target and moved to its Meteora DAMM v2 pool.
{
"mint": "<mint>",
"symbol": "SCAT",
"name": "Satoshi Cat",
"tier": "light",
"dammPool": "<damm v2 pool>",
"priceSats": 2.9983,
"at": 1791150000000
}burn
A keeper buyback & burn (or a graduated coin's leftover burn) landed.
{
"signature": "<signature>",
"time": 1791140807,
"mint": "<mint>",
"coins": 10.2039,
"why": "buyback",
"at": 1791140812000
}Recipes
Launch a coin from your app
import { Keypair, Transaction } from "@solana/web3.js";
// 1. Image + metadata -> uri
const form = new FormData();
form.set("name", "Satoshi Cat");
form.set("symbol", "SCAT");
form.set("image", file); // a File from an <input type="file">
const meta = await fetch("https://satpad.fun/api/v1/metadata", { method: "POST", body: form }).then((r) => r.json());
if (meta.error) throw new Error(meta.error);
// 2. A fresh keypair becomes the coin's mint
const mint = Keypair.generate();
const res = await fetch("https://satpad.fun/api/v1/tx/launch", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
wallet: wallet.publicKey.toBase58(),
mint: mint.publicKey.toBase58(),
tier: "light",
name: "Satoshi Cat",
symbol: "SCAT",
uri: meta.uri,
firstBuyBtc: 0.001,
}),
}).then((r) => r.json());
if (res.error) throw new Error(res.error);
// 3. Wallet + mint sign; the coin page is live once it confirms
const tx = Transaction.from(Uint8Array.from(atob(res.transaction), (c) => c.charCodeAt(0)));
const signature = await wallet.sendTransaction(tx, connection, { signers: [mint] });
console.log("https://satpad.fun/c/" + res.mint);A halving alert bot
Node 22 or later, no dependencies: read the stream with fetch and post wherever you like.
const res = await fetch("https://satpad.fun/api/v1/events");
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
let buf = "";
for (;;) {
const { value, done } = await reader.read();
if (done) break; // reconnect here with ?since=<last id>
buf += value;
let end;
while ((end = buf.indexOf("\n\n")) >= 0) {
const block = buf.slice(0, end);
buf = buf.slice(end + 2);
const type = /^event: (.*)$/m.exec(block)?.[1];
const data = /^data: (.*)$/m.exec(block)?.[1];
if (type === "coin.halving") {
const e = JSON.parse(data);
console.log(`$${e.symbol} entered ${e.eraName} at ${e.priceSats.toFixed(2)} sats https://satpad.fun/c/${e.mint}`);
}
}
}In a browser it is shorter:
const es = new EventSource("https://satpad.fun/api/v1/events");
es.addEventListener("coin.graduated", (e) => console.log("graduated", JSON.parse(e.data).symbol));Coins about to graduate
import requests
r = requests.get("https://satpad.fun/api/v1/coins", params={"sort": "progress", "graduated": "false", "limit": 10})
for c in r.json()["coins"]:
print(f'{c["meta"]["symbol"]:>8} {c["progress"]:6.1%} {c["era"]["name"]:<10} {c["priceSats"]:.2f} sats')Price in dollars
const [{ usd }, coin] = await Promise.all([
fetch("https://satpad.fun/api/v1/btc-usd").then((r) => r.json()),
fetch("https://satpad.fun/api/v1/coins/<mint>").then((r) => r.json()),
]);
const priceUsd = (coin.priceSats / 1e8) * usd;Read straight from the chain
There is no satpad program. A coin belongs to satpad exactly when its Meteora DBC pool uses one of these configs, so you can index or trade without this API using @meteora-ag/dynamic-bonding-curve-sdk and @meteora-ag/cp-amm-sdk.
| What | Address |
|---|---|
| Light config (0.125 BTC, creator) | E4Jjic9izNkcyRKGnQKydh3qFqju5MJdfx5FgjD8BmZH |
| Light config (0.125 BTC, holders) | 7uRc2rnJrtWFbnsBBKoCGYzn6GXHW4REco6CMhwpNyGr |
| Light config (0.125 BTC, btc) | 3bvQxeRyMffiiKFK1nirKnYgmsr4WzPvjnSLPQYs54ir |
| Standard config (0.25 BTC, creator) | 6dcwMyFyDX6tkh4S1z99u4vW8ntE8SRmVcgDkJMjAuF4 |
| Standard config (0.25 BTC, holders) | 27TKwFZx4FEwsiyUPyTf599YC3USaaCEfaHXu9sXdnA7 |
| Standard config (0.25 BTC, btc) | 5qQj8QoUeLCM3qiLZyC9EojKyohRaubYUCAnFruwKbFC |
| Deep config (0.5 BTC, creator) | 6cT2t2BRc5v8djLgfSe4SstfsasX1mvpApnCk1hTapTP |
| Deep config (0.5 BTC, holders) | 5AZ6vyFSS1mxsiDhmvugBNDpLXmDJjogRGtXDsZjKqHG |
| Deep config (0.5 BTC, btc) | HHxgeAeLJbmKK5YfiiPkTq2BhCCuLPXwxfNtkrCWd1os |
| Max config (1 BTC, creator) | 3UkZ5vb84hrGX2vZHZVCE5Lpuib5BVp4bQBZBcZdqRiw |
| Max config (1 BTC, holders) | 2xDDz6zWK76ujquitez2ASQa1nwLRjZYD5cwwxam2zb8 |
| Max config (1 BTC, btc) | 46jZ1HopNTvUWeJJbfrUR7fYp4Z5LxfoUN1eGcFkq43p |
| Distributor: holders and BTC payout configs (holders: holdings worth $10+) | 5P1iGbus5qF6rHqRLLn9uHwnPRZAkAGFT5v6SvPkBvPR |
| BTC payout bridge sender | 8QjVF9wNuePLCTthk8c3q7HtFKUCQUZE2KgjjzjBHWao |
| BTC quote mint | cbbtcf3aa214zXHbiAZQwf4122FBYbraNdFqgw4iMij |
| DBC program | dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN |
| DAMM v2 program | cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG |
A coin's pool is deriveDbcPoolAddress(btcMint, mint, config). Era boundaries follow from the config's curve: four constant-product segments, one per era, each raising a quarter of the graduation target.
Versioning
- Within
/api/v1, responses only gain fields. Ignore fields you don't know. Removing or renaming anything means/api/v2. - The unversioned
/api/*routes serve this site and can change without notice. Build on/api/v1. - Fees bought back and burned describe what the platform does with its share; nothing here is a promise about price or returns.