Build on halvd
Everything the site does, over plain HTTP: list and read coins, quote a trade, get a buy, a sell, a launch or a holder claim back as an unsigned transaction, and follow new coins, epochs, graduations, trades and burns as they happen.
List coins in a wallet app, run a trading bot, chart a curve, ping a chat when a coin graduates. halvd builds each transaction and stops there: the person using your app signs it in their own wallet.
One API, the whole launchpad
Every halvd coin is a Meteora pool anyone can read. The API saves you the indexing, the curve math and the transaction building.
- Base URL
https://halvd.fun/api/v1- Format
- JSON in and out (one multipart upload). Big integers such as atom amounts are strings; everything else is a plain number.
- Access
- No key, no sign-up. Any website may call it straight from the browser: CORS allows all origins.
- Network
- Every response carries
X-Cluster:mainnetorfork(a local test copy of mainnet). Compare it with the RPC your app sends through: a transaction built against the fork can never land on mainnet, and the reverse. This server saysmainnet. - Transactions
- Builders return unsigned transactions with the user as fee payer. halvd never holds or asks for a user's key. Real cbBTC moves on mainnet.
- Spec
- /api/v1/openapi.json (OpenAPI 3.1) for client generators, Postman or Insomnia. The index at /api/v1 lists tiers, configs, fees and thresholds for this cluster.
From zero to a signed buy
Pick a coin, see what a buy returns, then let the user's wallet sign the transaction the API builds.
1 · Choose a coin
Curve coins closest to graduation first. Each coin carries its price in sats, its epoch (1 to 4) and its progress to the target.
curl "https://halvd.fun/api/v1/coins?sort=progress&graduated=false&limit=5"2 · Price the trade
What 0.001 BTC buys right now. outUi is the expected coins, minOutUi the floor your slippage allows, feeBps the live fee (higher in a coin's first seconds).
curl "https://halvd.fun/api/v1/quote?mint=<mint>&side=buy&amount=0.001"3 · Build, sign, send
Post the same numbers to /tx/buy and you get the transaction. Decode it, hand it to the wallet, wait for confirmation. The swap carries your floor: if the price runs past it, the program rejects the swap and the user is out only the network fee.
curl -X POST https://halvd.fun/api/v1/tx/buy \
-H "content-type: application/json" \
-d '{"wallet":"<your wallet>","mint":"<mint>","amount":"0.001","slippageBps":200}'import { Transaction, VersionedTransaction } from "@solana/web3.js";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
const API = "https://halvd.fun/api/v1";
/** base64 → the transaction class its `version` says */
function decode(b64: string, version: "legacy" | 0) {
const bytes = Uint8Array.from(atob(b64), (ch) => ch.charCodeAt(0));
return version === 0 ? VersionedTransaction.deserialize(bytes) : Transaction.from(bytes);
}
export function useBuy() {
const { publicKey, sendTransaction } = useWallet();
const { connection } = useConnection();
return async function buy(mint: string, btc: string) {
if (!publicKey) throw new Error("Connect a wallet first");
const res = await fetch(`${API}/tx/buy`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ wallet: publicKey.toBase58(), mint, amount: btc, slippageBps: 200 }),
});
const built = await res.json();
if (!res.ok) throw new Error(built.error); // says what to fix
// the wallet is the only signer, so a fresh blockhash is fine and buys signing time
const tx = decode(built.transaction, built.version);
const latest = await connection.getLatestBlockhash();
if (tx instanceof Transaction) tx.recentBlockhash = latest.blockhash;
else tx.message.recentBlockhash = latest.blockhash;
const signature = await sendTransaction(tx, connection);
await connection.confirmTransaction({ signature, ...latest }, "confirmed");
return { signature, coinsAtLeast: built.quote.minOutUi };
};
}Sats first
Prices are sats per whole coin. Field names say the unit: Sats, Btc, Usd, Ui or atoms.
| Field | What it holds | Example |
|---|---|---|
amount | What you send: BTC or SOL to spend on a buy, coins on a sell, as a decimal string | "0.001" |
amountAtoms | The same in the smallest unit: sats (8 decimals), lamports (9) or coin atoms (6) | "100000" |
…Sats | Whole sats as integers; 100,000,000 make 1 BTC | 12500000 |
priceSats | Sats for one whole coin; a float, usually under 1 early on | 0.23 |
fdvBtc, mcapBtc | Whole BTC: priceSats × 21,000,000 (or × coins out) ÷ 10^8 | 0.0492 |
inAmount, outAmount, minOut | Atom counts as strings, since they can exceed 2^53; minOut is the floor the transaction checks | "402069365110" |
inUi, outUi, minOutUi | The same three as decimals, for showing people | 402069.36511 |
createdAt, ts, at | Unix seconds | 1792193381 |
- Coins
- Every coin has 21,000,000 supply and 6 decimals. A balance of
1000000atoms is one coin. - BTC
- cbBTC, Coinbase's wrapped bitcoin, with 8 decimals: its smallest unit is exactly one sat. Mint
cbbtcf3aa214zXHbiAZQwf4122FBYbraNdFqgw4iMij. - Dollars
- Only as a convenience (
priceUsd,/btc-usd). They come from BTC/USD at read time and can be null.
Launch, four epochs, a pool
What happens to a coin, and how each step shows up in the API.
- Launch
- The creator picks a tier (spark 0.125 BTC · block 0.25 BTC · vault 0.5 BTC · whole 1 BTC) and a mode (creator, holders, longhold, btc, hash). Each pair is its own DBC config, so both are fixed on chain. The launch transaction can carry the creator's own first buy, which pays the normal 1%. Anyone buying in the following 3 seconds pays a falling fee, 49.99%, then 33.66%, then 17.33%, before it settles at 1%. Read the live figure from a quote's
feeBps. - Epochs
- The price doubles from one epoch to the next and each epoch raises a quarter of the target. On Spark the curve starts at 0.23 sats and epoch 4 ends at 3.75 sats. A coin's
epochreads 1 to 4,toNextHalvingcounts the coins left before the next step, and the stream sendscoin.epochthe first time a buy crosses one./curveshas every boundary. - Sold out
- When epoch 4 fills, the curve stops trading. A buy that would overshoot fills to the end and refunds the rest (
refundSatsin its quote). Until the pool opens, quotes and builders answer409for a few seconds. - Graduation
- Meteora moves the raised BTC and the unsold coins into a DAMM v2 pool whose liquidity is locked for good. The coin then reads
graduated: truewith itsdammPoolfilled in, and stream listeners getcoin.graduated. Keep calling the same endpoints: the server trades on the pool from then on, and a quote'svenueturns fromcurvetodammV2. - Fees
- 1% of every trade in cbBTC, on the curve and in the pool. Who gets the 60% fee share depends on the mode; claims and payouts have their own endpoints below.
Your users sign
Builders return a transaction, never a signature. What to do with it depends on what it is.
- Decoding
- The bytes arrive base64-encoded. Look at
versionfirst:"legacy"goes throughTransaction.from,0throughVersionedTransaction.deserialize. You'll see0whenever SOL is involved, because the Jupiter leg needs address lookup tables. - Fee payer
- The wallet you named, listed first in
signers. It pays the network fee and any token account rent (a first cbBTC or coin account). - Blockhash
- The network accepts a blockhash for roughly 150 blocks, about a minute. A user who takes longer needs a rebuilt transaction. For trades and boosts the wallet is the only signer, so your app may drop in a newer blockhash itself right before signing, as the sample above does.
- Simulation
- Trade and boost builders simulate first: a trade that would fail (no balance, slippage) comes back as a 400 with the reason instead of a transaction.
- Launches
- Two signers: the wallet and a keypair you generate for the mint (its public key goes in
mintPubkey). Never reuse a mint keypair. Then post the signature to/launch/record. - Holder claims
- halvd's distributor key signs the claim before you see it, over these exact bytes. Call
signTransactionand post the result to/holders/claim/submit; halvd broadcasts it and settles the books. Some wallets slip a compute-budget instruction in while signing, which alters the bytes: that claim comes back422, and you prepare a new one. A prepared claim reserves the owed sats for 2 minutes.
const mint = Keypair.generate(); // becomes the coin's address; keep it until the tx lands
const built = await post("/tx/launch", {
wallet: publicKey.toBase58(),
mintPubkey: mint.publicKey.toBase58(),
tier: "spark", mode: "creator", name: "Sat Stacker", symbol: "STACK", uri,
});
const tx = decode(built.transaction, built.version);
// the wallet signs as fee payer, the mint keypair as the new mint
const signature = await sendTransaction(tx, connection, { signers: [mint] });
await post("/launch/record", { signature }); // optional: indexes it now instead of in a few secondsconst prep = await post("/holders/claim/prepare", { wallet: publicKey.toBase58() });
const tx = Transaction.from(Uint8Array.from(atob(prep.transaction), (ch) => ch.charCodeAt(0)));
const signed = await signTransaction(tx); // signTransaction, not sendTransaction: halvd broadcasts it
const signedTx = btoa(String.fromCharCode(...signed.serialize()));
const done = await post("/holders/claim/submit", { claimId: prep.claimId, signedTx });
// done.status === "confirmed", done.sig is the transactionPlain errors, fair limits
A failed call answers with a status from the list below and a JSON body holding one sentence, such as { "error": "sort must be one of new, boost, mcap, progress, fdv, volume, hashrate" }.
- 200 / 201
- Done. 201 means something was created (metadata, a recorded launch).
- 400
- The request breaks a rule, or a transaction failed its simulation. The message names the field or the reason.
- 404
- No such coin, claim or endpoint on this cluster. A coin that launched seconds ago can 404 on routes that read the index; /coins/{mint} doesn't.
- 405
- Wrong method: the builders are POST only.
- 409
- A state conflict: a mint already in use, a claim already open, or a curve that sold out a moment ago and hasn't migrated yet.
- 410 / 422
- Claims only: the two-minute lock lapsed (410), or the signed transaction isn't the one prepared (422).
- 413 / 415
- Uploads only: over 1 MB (413), or not multipart / not an image (415). POST bodies must be JSON (415).
- 429
- Over a rate limit or the stream cap. The body has retryAfterSecs; Retry-After says the same.
- 500
- Something failed on halvd's side. The message keeps a short reason.
- 502
- An upstream failed: Jupiter had no route, or the RPC didn't answer a chain check.
- 503
- Trading or launches are paused, the RPC is busy, or the event stream is full. Safe to retry.
Rate limits
Counted per IP address, in buckets. Every endpoint in a bucket draws on one allowance, which you can spend in a burst; it then refills at an even pace across the window. These numbers come from the same table the server enforces.
- read
- 240 per minute. Reads answered from halvd's index.
GET /GET /openapi.jsonGET /coinsGET /coins/{mint}/candlesGET /curvesGET /burnsGET /btc-usdGET /btc-address/checkGET /coins/{mint}/holdersGET /coins/{mint}/holders/{wallet}GET /wallets/{wallet}/sharesGET /coins/{mint}/payoutsGET /coins/{mint}/rigGET /rigsGET /blocks - live
- 90 per minute. Reads that go to the chain first.
GET /coins/{mint}GET /coins/{mint}/checkGET /quoteGET /holders/claim/status - build
- 30 per minute. Trade and boost transactions.
POST /tx/buyPOST /tx/sellPOST /tx/boost - claim
- 12 per minute. Holder claims.
POST /holders/claim/preparePOST /holders/claim/submit - launch
- 10 per 10 minutes. Launch transactions.
POST /tx/launch - record
- 30 per 10 minutes. Recording a landed launch.
POST /launch/record - metadata
- 10 per 10 minutes. Image and metadata uploads.
POST /metadata - stream
- 12 per minute. Opening the event stream.
GET /events - Event stream
- At most 4 open streams per IP (the next answers
429) and 100 on the server (then503with Retry-After). - Spending less
- Each read sends a
Cache-Controllifetime; a CDN or a small in-memory cache answers repeats for free. For new coins and trades, one stream replaces any amount of polling.
Index and spec
/api/v1The index
Cluster, coin rules, tiers with their config per mode, fees, anti-snipe schedule, off-chain thresholds, public addresses and this endpoint list.
- Fetch it when your app boots: it carries this cluster's config addresses and thresholds, so nothing needs hard-coding.
curl "https://halvd.fun/api/v1"{
"name": "halvd API",
"version": "1",
"cluster": "fork",
"docs": "/developers",
"openapi": "/api/v1/openapi.json",
"token": {
"supply": 21000000,
"decimals": 6,
"mintAuthority": null,
"freezeAuthority": null,
"metadata": "immutable"
},
"btc": {
"symbol": "cbBTC",
"mint": "cbbtcf3aa214zXHbiAZQwf4122FBYbraNdFqgw4iMij",
"decimals": 8
},
"tiers": [
{
"id": "spark",
"name": "Spark",
"targetBtc": 0.125,
"targetSats": 12500000,
"startSats": 0.23452125193542817,
"graduationSats": 3.75234003096685,
"configs": {
"creator": "7p7GuuTzNsQumkFPWxPGNX2Hyj1M23aroc7BkpCSu3SW",
"holders": "26yVFHMfzRBTJV7GcQonc6Lj1PbEGcNNBgny3PMrpFfp",
"longhold": "BEmJTmLwDKvVDc2yuNEJWLAARESN1XAS7DFjqHqcN51Q",
"btc": "2tXdcQhK7HhKLyDYhmf7vJ8S4bEFpHGEmeuL8NAWfUHV",
"hash": "EqHJ9tADga9F1SzD4CoNQPxgVL75ruqeCzkKHcTQJdtD"
}
}
],
"modes": [
{
"id": "creator",
"name": "Creator",
"claimer": "creator",
"summary": "The fee share is yours, paid in BTC. Claim it any time from Wallet."
}
],
"epochs": [
{
"epoch": 1,
"subsidyBtc": "50",
"priceMultipleAtStart": 1
}
],
"antiSnipe": {
"scheduleBps": [
4999
],
"stepSeconds": 1,
"afterBps": 100,
"firstBuyInLaunchTxBps": 100
},
"fees": {
"tradeFeeBps": 100,
"paidIn": "cbBTC",
"meteoraShare": 0.2,
"feeShareOfRest": 0.75,
"split": {
"feeShare": 0.6,
"burn": 0.15,
"treasury": 0.05,
"meteora": 0.2
},
"afterGraduation": {
"poolFeeBps": 100,
"lockedLiquidityPct": 100,
"creatorMode": {
"creatorPct": 80,
"platformPct": 20
},
"otherModes": {
"claimerPct": 100
}
}
},
"offchain": {
"snapshotFastSecs": 60,
"snapshotFastForSecs": 3600,
"snapshotSlowSecs": 600,
"minHoldingUsd": 10,
"minClaimSats": 500,
"payoutSats": 12000,
"payoutMaxFee": 0.1,
"payoutStaleDays": 90,
"payoutStaleSats": 6000,
"payoutStaleMaxFee": 0.2,
"hashFundSats": 11000,
"hashFundMaxFee": 0.1,
"hashBidSats": 10000,
"hashFinderShare": 0.1,
"hashBurnShare": 0.05,
"hashConfirmations": 100,
"claimMinSats": 2000
},
"longholdSteps": [
{
"afterHours": 0,
"weight": 1
}
],
"boosts": {
"minBurn": 1000,
"fadeSeconds": 86400
},
"memos": {
"btcPayout": "halvd payout v1 ",
"hash": "halvd hash v1 ",
"boost": "halvd boost v1 ",
"burn": "halvd burn",
"treasury": "halvd treasury",
"platform": "halvd platform share",
"claim": "halvd claim"
},
"addresses": {
"programs": {
"dbc": "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN",
"dammV2": "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"
},
"keeper": "BdcxkPEKvPJ74vX6gKRXhCgV4ZUAkCaCnkXuUES3nMSY",
"distributor": "7Jzj13Kn8Q7B9esBHxGRYtuSmuM4zVuH3Pk8vk88FJKV",
"miner": "93aTDkTC48wH56S1NEB9X3FSPzV7uPqqYv3M7eUcxMnF",
"sender": "DMZjK5wdTSUaukUK8p856oNFsNRXYPVCMcu3fie5VZ46",
"treasury": "5Bf6vRccSczDFcxVvdHJ9FfV2EXL1AyZ1N9fX1hxJyAs",
"houseMint": "2sNyqhYwx9TQ6BrMCscPowKnpYn3oWiQ4bHjyu5iXy4G",
"configsReady": true
},
"stream": {
"open": 0,
"maxOpen": 100,
"maxOpenPerIp": 4,
"polling": false,
"pollMs": 2000,
"heartbeatS": 15,
"types": [
"coin.created"
]
},
"limits": {
"read": {
"limit": 240,
"windowSecs": 60
},
"live": {
"limit": 90,
"windowSecs": 60
},
"build": {
"limit": 30,
"windowSecs": 60
},
"claim": {
"limit": 12,
"windowSecs": 60
},
"launch": {
"limit": 10,
"windowSecs": 600
},
"record": {
"limit": 30,
"windowSecs": 600
},
"metadata": {
"limit": 10,
"windowSecs": 600
},
"stream": {
"limit": 12,
"windowSecs": 60
}
},
"endpoints": [
{
"method": "GET",
"path": "/api/v1",
"summary": "Cluster, coin rules, tiers with their config per mode, fees, anti-snipe schedule, off-chain thresholds, public addresses and this endpoint list.",
"bucket": "read"
},
{
"method": "GET",
"path": "/api/v1/openapi.json",
"summary": "An OpenAPI 3.1 description of every v1 route, with schemas for the main objects.",
"bucket": "read"
},
{
"method": "GET",
"path": "/api/v1/coins",
"summary": "Coins from halvd's index, sorted and filtered, a page at a time.",
"bucket": "read"
}
]
}- 429Over the rate limit; see retryAfterSecs.
/api/v1/openapi.jsonOpenAPI document
An OpenAPI 3.1 description of every v1 route, with schemas for the main objects.
- OpenAPI 3.1, JSON. Load it into Postman, Insomnia or a client generator.
curl "https://halvd.fun/api/v1/openapi.json"{
"openapi": "3.1.0",
"info": {
"title": "halvd API",
"version": "1",
"description": "Read halvd coins and build their transactions over HTTP. JSON, open CORS, no key. Transactions come back unsigned: the user's wallet signs and sends them. Every response carries X-Cluster (mainnet or fork). Guide: /developers."
},
"paths": "28 paths",
"components": {
"schemas": "32 schemas"
}
}- 429Over the rate limit; see retryAfterSecs.
Coins and market data
/api/v1/coinsBrowse coins
Coins from halvd's index, sorted and filtered, a page at a time.
sortstringquery- new: newest first · boost: coins with boost left, most first · mcap / fdv / volume: largest first · progress: closest to graduation, curve coins only · hashrate: Hashrate mode coins only.
newboostmcapprogressfdvvolumehashrate limitintegerquery- Page size, 1 to 100.Default 24.
offsetintegerquery- Coins to skip.Default 0.
modestringquery- Only coins in this mode.
creatorholderslongholdbtchash creatorstringquery- Only coins launched by this wallet.
graduatedbooleanquery- true: only coins on DAMM v2 · false: only coins still on the curve.
qstringquery- Search names and tickers, or match an exact mint. Up to 64 characters.
curl "https://halvd.fun/api/v1/coins?sort=progress&graduated=false&limit=2"{
"total": 101,
"limit": 2,
"offset": 0,
"sort": "progress",
"btcUsd": 81541.875,
"coins": [
{
"mint": "ABa4BRS5CfGrEuGVrHhWVa5niZJqvbV8HmrmiYiGhuD9",
"pool": "3BPgdBGNVjfGC67bQjC9WGqyVu12Y6UHAFLcJhfEkWqh",
"config": "7p7GuuTzNsQumkFPWxPGNX2Hyj1M23aroc7BkpCSu3SW",
"tier": "spark",
"mode": "creator",
"creator": "5q1JUAkPmWVTyVDuYjVqTrRj3aeTJHMaP8hwJk1Aw2tJ",
"name": "Halving Test",
"symbol": "HTEST",
"image": null,
"description": "",
"xUrl": null,
"website": null,
"createdAt": 1792184933,
"createSig": "5hsQmJNLy9TTa5EWBXAR6jMQ896Cd1DReFfvoZydoATbxBsNMiKZ6uczgJzbkNg9GEzDq14HBzGJCqd2mkhNRvfa",
"priceSats": 2.9896370107075807,
"priceUsd": 0.002437806074224912,
"fdvBtc": 0.627823772248592,
"mcapBtc": 0.5179405228298422,
"epoch": 4,
"graduated": false,
"raisedSats": 11354135,
"targetSats": 12500000,
"progress": 0.9083308,
"soldCoins": 17324528.729568314,
"toNextHalving": 342116.6674795151,
"dammPool": null,
"graduatedAt": null,
"volumeSats": 11354135,
"trades": 1,
"boost": 0,
"btcAddress": null,
"hashDest": null,
"hashRig": null
}
]
}- 400A parameter is missing or malformed; the message names it.
- 429Over the rate limit; see retryAfterSecs.
/api/v1/coins/{mint}One coin
A coin with its price re-read from its pool when the index copy is more than a few seconds old, plus its recent boosts.
- A coin the indexer hasn't recorded yet is built from chain (source: chain), so this works seconds after a launch.
mintstringpathrequired- The coin's mint address.
curl "https://halvd.fun/api/v1/coins/<mint>"{
"coin": {
"mint": "ABa4BRS5CfGrEuGVrHhWVa5niZJqvbV8HmrmiYiGhuD9",
"pool": "3BPgdBGNVjfGC67bQjC9WGqyVu12Y6UHAFLcJhfEkWqh",
"config": "7p7GuuTzNsQumkFPWxPGNX2Hyj1M23aroc7BkpCSu3SW",
"tier": "spark",
"mode": "creator",
"creator": "5q1JUAkPmWVTyVDuYjVqTrRj3aeTJHMaP8hwJk1Aw2tJ",
"name": "Halving Test",
"symbol": "HTEST",
"image": null,
"description": "",
"xUrl": null,
"website": null,
"createdAt": 1792184933,
"createSig": "5hsQmJNLy9TTa5EWBXAR6jMQ896Cd1DReFfvoZydoATbxBsNMiKZ6uczgJzbkNg9GEzDq14HBzGJCqd2mkhNRvfa",
"priceSats": 2.9896370107075807,
"priceUsd": 0.002437806074224912,
"fdvBtc": 0.627823772248592,
"mcapBtc": 0.5179405228298422,
"epoch": 4,
"graduated": false,
"raisedSats": 11354135,
"targetSats": 12500000,
"progress": 0.9083308,
"soldCoins": 17324528.729568314,
"toNextHalving": 342116.6674795151,
"dammPool": null,
"graduatedAt": null,
"volumeSats": 11354135,
"trades": 1,
"boost": 0,
"btcAddress": null,
"hashDest": null,
"hashRig": null
},
"source": "db",
"readAt": 1792194011,
"boosts": [],
"boostToLead": 4816
}- 400A parameter is missing or malformed; the message names it.
- 404No halvd coin with this mint on this cluster.
- 429Over the rate limit; see retryAfterSecs.
/api/v1/coins/{mint}/candlesPrice candles
OHLC candles in sats per coin, built from every pool change the indexer saw. Quiet buckets are filled flat.
mintstringpathrequired- The coin's mint address.
intervalstringquery- Seconds per candle. auto picks the smallest that fits the coin's history in about limit candles.
auto6030090036001440086400 limitintegerquery- Candles, 10 to 500.Default 120.
curl "https://halvd.fun/api/v1/coins/<mint>/candles?interval=60&limit=3"{
"mint": "ABa4BRS5CfGrEuGVrHhWVa5niZJqvbV8HmrmiYiGhuD9",
"interval": 60,
"intervals": [
60,
300
],
"count": 1,
"last": 2.9896370107075807,
"candles": [
{
"t": 1792193460,
"o": 2.9896370107075807,
"h": 2.9896370107075807,
"l": 2.9896370107075807,
"c": 2.9896370107075807,
"v": 0,
"n": 0
},
{
"t": 1792193520,
"o": 2.9896370107075807,
"h": 2.9896370107075807,
"l": 2.9896370107075807,
"c": 2.9896370107075807,
"v": 0,
"n": 0
}
]
}- 400A parameter is missing or malformed; the message names it.
- 404No indexed coin with this mint.
- 429Over the rate limit; see retryAfterSecs.
/api/v1/coins/{mint}/checkChain check
Four properties read straight from the mint and pool: the coin count, no mint key, no freeze key, and a cbBTC pairing on a halvd config.
mintstringpathrequired- The coin's mint address.
curl "https://halvd.fun/api/v1/coins/<mint>/check"{
"mint": "ABa4BRS5CfGrEuGVrHhWVa5niZJqvbV8HmrmiYiGhuD9",
"ok": true,
"checks": [
{
"id": "supply",
"ok": true,
"label": "Exactly 21,000,000 coins exist",
"detail": "mint account: 21000000000000 base units, 6 decimals"
},
{
"id": "mint",
"ok": true,
"label": "Nobody holds the mint key",
"detail": "mint authority: none, so the count can't grow"
}
],
"checkedAt": 1792194011
}- 400A parameter is missing or malformed; the message names it.
- 404No halvd coin with this mint on this cluster.
- 429Over the rate limit; see retryAfterSecs.
- 502The RPC didn't answer. Retry.
/api/v1/curvesCurve shapes
Every tier's halving curve: epoch boundaries, prices in sats, coins per epoch, pool seed and FDV at start and at graduation.
- A tier's numbers never change, so one fetch is enough.
curl "https://halvd.fun/api/v1/curves"{
"supply": 21000000,
"note": "Ideal curve. The on-chain configs round a few atoms.",
"tiers": [
{
"tier": "spark",
"name": "Spark",
"targetBtc": 0.125,
"targetSats": 12500000,
"startSats": 0.23452125193542817,
"graduationSats": 3.75234003096685,
"startFdvBtc": 0.04924946290643991,
"graduationFdvBtc": 0.7879914065030386,
"soldCoins": 17666645.397047833,
"poolCoins": 3331254.602952168,
"leftoverCoins": 2099.9999999995343,
"epochs": [
{
"epoch": 1,
"subsidyBtc": "50",
"startSats": 0.23452125193542817,
"endSats": 0.4690425038708563,
"coins": 9422210.87842551,
"raisesSats": 3125000,
"fromSupplyShare": 0,
"toSupplyShare": 0.44867670849645286
}
],
"points": [
{
"coins": 0,
"sats": 0.23452125193542817,
"epoch": 1
}
]
}
]
}- 429Over the rate limit; see retryAfterSecs.
/api/v1/burnsBurn ledger
$HALVD burned so far, the treasury balance and the burn ledger (buybacks, curve dust, boosts, block burns), ten rows a page.
pageintegerquery- Page, from 1. Ten rows a page, newest first.Default 1.
kindstringquery- Only this kind of burn.
buybackleftoverboostblock
curl "https://halvd.fun/api/v1/burns?kind=buyback"{
"totals": {
"houseMint": "2sNyqhYwx9TQ6BrMCscPowKnpYn3oWiQ4bHjyu5iXy4G",
"burned": 10750870.351181,
"burnedShare": 0.5119462071990952,
"circulating": 10249129.648819,
"source": "chain",
"transactions": 27,
"btcSpent": 0.05666465
},
"keeper": "BdcxkPEKvPJ74vX6gKRXhCgV4ZUAkCaCnkXuUES3nMSY",
"treasury": {
"address": "5Bf6vRccSczDFcxVvdHJ9FfV2EXL1AyZ1N9fX1hxJyAs",
"btc": 0.01242347
},
"rows": [
{
"sig": "2oNRjurPzSeYLnNdoFwvXfpadoz1JB4RjVCR2rEmJMiuqmkrDBQgJVs2epEw2wqVX1VqQFagZJSJkf3HmXZEv6Wn",
"ix": 3,
"ts": 1792193266,
"kind": "leftover",
"mint": "AL6sL2cgQiZSSKmp65stYgmVAxdFrNorXnDpbLHyapeb",
"amount": 2100.000005,
"btcSpent": 0,
"target": "",
"wallet": "BdcxkPEKvPJ74vX6gKRXhCgV4ZUAkCaCnkXuUES3nMSY",
"coin": "AL6sL2cgQiZSSKmp65stYgmVAxdFrNorXnDpbLHyapeb",
"coinSymbol": "KTA",
"coinName": "Keeper Test KTA"
},
{
"sig": "2Lk4jeyRPZW21Cm9AbzUtmU7bRFtN3RdGtdmUebnYQB6ojsQAuoRsLN22BbakJH7qNLszDXZLNT1K1PMjsoVV2xF",
"ix": 3,
"ts": 1792193265,
"kind": "leftover",
"mint": "HB6EW46PBHsGHzfJrx74nWzLGqUMKMPfzZ7vm9gehsQ6",
"amount": 2100.000008,
"btcSpent": 0,
"target": "",
"wallet": "BdcxkPEKvPJ74vX6gKRXhCgV4ZUAkCaCnkXuUES3nMSY",
"coin": "HB6EW46PBHsGHzfJrx74nWzLGqUMKMPfzZ7vm9gehsQ6",
"coinSymbol": "TTEST",
"coinName": "Trade Test"
}
],
"page": 1,
"pages": 3,
"total": 27,
"kind": ""
}- 400A parameter is missing or malformed; the message names it.
- 429Over the rate limit; see retryAfterSecs.
/api/v1/btc-usdBTC price
The BTC/USD price halvd uses for dollar figures, or null when no source answers.
curl "https://halvd.fun/api/v1/btc-usd"{
"usd": 81541.875,
"usdPerSat": 0.00081541875,
"at": 1792194011
}- 429Over the rate limit; see retryAfterSecs.
Trades and boosts
/api/v1/quotePrice a trade
What a buy or sell returns right now, on the curve or the DAMM v2 pool, with the live fee (anti-snipe included) and price impact.
mintstringqueryrequired- The coin.
sidestringquery- buy coins, or sell them.
buysell assetstringquery- buy: what you pay with · sell: what you're paid in. SOL is swapped through Jupiter in the same transaction.
btcsol amountstringquery- Decimal. buy: BTC or SOL to spend · sell: coins to sell. Required unless amountAtoms is sent.
amountAtomsstringquery- The same amount as an integer of atoms (sats, lamports or coin atoms).
slippageBpsintegerquery- Sets minOut, 0 to 5000.Default 200.
curl "https://halvd.fun/api/v1/quote?mint=<mint>&side=buy&amount=0.001"{
"mint": "ABa4BRS5CfGrEuGVrHhWVa5niZJqvbV8HmrmiYiGhuD9",
"venue": "curve",
"side": "buy",
"asset": "btc",
"inAmount": "100000",
"outAmount": "32773696138",
"minOut": "32118222215",
"feeBps": 100,
"priceImpact": 0.01039528397135614,
"inUi": 0.001,
"outUi": 32773.696138,
"minOutUi": 32118.222215,
"inSymbol": "cbBTC",
"outSymbol": "coin",
"spotSats": 2.9896370107075807,
"avgSats": 3.051227410510265,
"poolSats": "100000",
"refundSats": "0",
"graduates": false,
"ageSeconds": 9027,
"slippageBps": 200
}- 400A parameter is missing or malformed; the message names it.
- 404No halvd coin with this mint on this cluster.
- 409Epoch 4 sold out and the DAMM v2 pool isn't open yet. Retry in a moment.
- 429Over the rate limit; see retryAfterSecs.
- 502SOL only: Jupiter has no route right now.
- 503The RPC was busy. Safe to retry.
/api/v1/tx/buyBuy transaction
An unsigned buy, paid in cbBTC (legacy transaction) or SOL routed through Jupiter (v0). Simulated before it's returned.
- On the curve buys fill up to graduation and refund the rest (quote.refundSats).
walletstringJSON bodyrequired- The buyer. Signs and pays the network fee.
mintstringJSON bodyrequired- The coin.
amountstringJSON body- BTC (asset btc) or SOL (asset sol) to spend, as a decimal string. Or send amountAtoms.
amountAtomsstringJSON body- The amount in sats or lamports.
assetstringJSON body- btc: one legacy transaction · sol: Jupiter SOL→cbBTC and the buy in one v0 transaction.
btcsol slippageBpsintegerJSON body- 0 to 5000.Default 200.
simulatebooleanJSON body- Simulate before returning, so a trade that would fail comes back as a 400 with the reason.Default true.
curl -X POST https://halvd.fun/api/v1/tx/buy \
-H "content-type: application/json" \
-d '{"wallet":"<your wallet>","mint":"<mint>","amount":"0.001","asset":"btc","slippageBps":200}'{
"transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA…",
"version": "legacy",
"bytes": 720,
"signers": [
"6GNru4XLq44kSQcfCnqYgcwv2e8M33g9rPyRUgvmFrcw"
],
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"quote": {
"venue": "curve",
"side": "buy",
"asset": "btc",
"inAmount": "100000",
"outAmount": "414504500114",
"minOut": "402069365110",
"feeBps": 100,
"priceImpact": 0.013087936928250388,
"inUi": 0.001,
"outUi": 414504.500114,
"minOutUi": 402069.36511,
"inSymbol": "cbBTC",
"outSymbol": "coin",
"spotSats": 0.2357538537048299,
"avgSats": 0.2412519043158694,
"poolSats": "100000",
"refundSats": "0",
"graduates": false,
"ageSeconds": 5,
"slippageBps": 300
}
}- 400Bad input, or the simulation failed (balance, slippage); the message says which.
- 404No halvd coin with this mint on this cluster.
- 409The curve just sold out; the pool opens shortly.
- 429Over the rate limit; see retryAfterSecs.
- 502SOL only: no Jupiter route.
- 503Trading is paused, or the RPC is busy.
/api/v1/tx/sellSell transaction
An unsigned sell, paid out in cbBTC (legacy) or SOL (v0). Simulated before it's returned.
walletstringJSON bodyrequired- The seller. Signs and pays the network fee.
mintstringJSON bodyrequired- The coin.
amountstringJSON body- Coins to sell, as a decimal string (6 decimals at most). Or send amountAtoms.
amountAtomsstringJSON body- Coin atoms to sell.
assetstringJSON body- Be paid in cbBTC (legacy tx) or SOL (v0 tx through Jupiter).
btcsol slippageBpsintegerJSON body- 0 to 5000.Default 200.
simulatebooleanJSON body- Simulate before returning.Default true.
curl -X POST https://halvd.fun/api/v1/tx/sell \
-H "content-type: application/json" \
-d '{"wallet":"<your wallet>","mint":"<mint>","amount":"1000","asset":"btc"}'{
"transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA…",
"version": "legacy",
"bytes": 720,
"signers": [
"6GNru4XLq44kSQcfCnqYgcwv2e8M33g9rPyRUgvmFrcw"
],
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"quote": {
"venue": "curve",
"side": "sell",
"asset": "btc",
"inAmount": "124677704821",
"outAmount": "29748",
"minOut": "29153",
"feeBps": 100,
"priceImpact": 0.003951030340377248,
"inUi": 124677.704821,
"outUi": 0.00029748,
"minOutUi": 0.00029153,
"inSymbol": "coin",
"outSymbol": "cbBTC",
"spotSats": 0.24196530008234407,
"avgSats": 0.23859919496199625,
"poolSats": "29748",
"refundSats": "0",
"graduates": false,
"ageSeconds": 8,
"slippageBps": 200
}
}- 400Bad input, or the simulation failed (not enough coins, slippage).
- 404No halvd coin with this mint on this cluster.
- 409The curve just sold out; the pool opens shortly.
- 429Over the rate limit; see retryAfterSecs.
- 502SOL only: no Jupiter route.
- 503Trading is paused, or the RPC is busy.
/api/v1/tx/boostBoost transaction
An unsigned burn of $HALVD with the boost memo naming a coin.
walletstringJSON bodyrequired- Holds the $HALVD, signs the burn and the memo.
mintstringJSON bodyrequired- The coin to boost.
amountstringJSON bodyrequired- Whole $HALVD to burn, 1,000 or more.
curl -X POST https://halvd.fun/api/v1/tx/boost \
-H "content-type: application/json" \
-d '{"wallet":"<your wallet>","mint":"<mint>","amount":"1000"}'{
"transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA…",
"version": "legacy",
"bytes": 393,
"signers": [
"6GNru4XLq44kSQcfCnqYgcwv2e8M33g9rPyRUgvmFrcw"
],
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"amount": 1000,
"memo": "halvd boost v1 9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY"
}- 400Under 1,000, more than the wallet holds, or the simulation failed.
- 404No halvd coin with this mint on this cluster.
- 409$HALVD hasn't launched on this cluster.
- 429Over the rate limit; see retryAfterSecs.
Launching a coin
/api/v1/metadataStore coin metadata
Stores the coin's image and details and returns the metadata uri to put on chain.
- The metadata is hosted by halvd under /api/meta/{id}; the uri is what goes on chain, and on-chain metadata can't be edited later.
namestringform fieldrequired- Up to 32 characters.
symbolstringform fieldrequired- 1 to 10 of A–Z and 0–9; a leading $ is dropped, letters are uppercased.
imagefileform fieldrequired- PNG, JPG, GIF or WebP, at most 1 MB (checked by its bytes, not its name).
descriptionstringform field- Up to 280 characters.
xstringform field- An X handle or an x.com / twitter.com link.
websitestringform field- An http(s) link.
curl -X POST https://halvd.fun/api/v1/metadata \
-F name="Sat Stacker" \
-F symbol="STACK" \
-F image=@stack.png \
-F website="https://example.com"{
"id": "LOEl69iWayBy",
"uri": "http://localhost:3112/api/meta/LOEl69iWayBy",
"image": "http://localhost:3112/api/img/LOEl69iWayBy"
}- 400A field breaks a rule; the message names it.
- 413The image is over 1 MB.
- 415Not multipart/form-data, or the file isn't PNG, JPG, GIF or WebP.
- 429Over the rate limit; see retryAfterSecs.
/api/v1/tx/launchLaunch transaction
An unsigned create-pool transaction on the tier and mode you pick, with an optional first buy in the same transaction.
walletstringJSON bodyrequired- The creator: signs, pays rent and fees (about 0.03 SOL), and is the pool creator.
mintPubkeystringJSON bodyrequired- Public key of a keypair you just generated. It becomes the coin's mint and must sign too.
tierstringJSON bodyrequired- Graduation target: 0.125, 0.25, 0.5 or 1 BTC. Permanent.
sparkblockvaultwhole modestringJSON bodyrequired- Who gets the 60% fee share. Permanent.
creatorholderslongholdbtchash namestringJSON bodyrequired- Up to 32 characters (and bytes).
symbolstringJSON bodyrequired- 1 to 10 of A–Z and 0–9.
uristringJSON bodyrequired- Metadata JSON URL, http(s), at most 200 bytes. Use /metadata to get one.
btcAddressstringJSON body- btc mode, required: the mainnet bc1 address the fee share is paid to. Bound for good by a memo the wallet signs.
hashDeststringJSON body- hash mode, required: where found blocks go.
holdersburn hashRigstringJSON body- hash mode, required: mine with the shared pool rig or alone.
poolsolo firstBuyobjectJSON body- Optional { amount, payWith: btc | sol }: whole BTC (from 1,000 sats, at most 90% of the target) or whole SOL (from 0.001). Lands in the launch transaction at the 1% fee.
curl -X POST https://halvd.fun/api/v1/tx/launch \
-H "content-type: application/json" \
-d '{"wallet":"<your wallet>","mintPubkey":"<new mint public key>","tier":"spark","mode":"holders","name":"Sat Stacker","symbol":"STACK","uri":"<uri from /metadata>","firstBuy":{"amount":0.0001,"payWith":"btc"}}'{
"transaction": "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA…",
"version": "legacy",
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"pool": "9UVU8pMz1E8BqgBRTS4GCayrXvafFV2bQnWoQfyqwWhL",
"config": "26yVFHMfzRBTJV7GcQonc6Lj1PbEGcNNBgny3PMrpFfp",
"tier": "spark",
"mode": "holders",
"memo": null,
"bytes": 943,
"firstBuy": {
"payWith": "btc",
"spend": "20000",
"btcIn": "20000",
"coinsOut": 84206.32077816188,
"minCoinsOut": 79996.00473925378
},
"blockhash": "SURFNETxSAFEHASHxxxxxxxxxxxxxxxxxxxxxxx5c7c",
"lastValidBlockHeight": 23827,
"signers": [
"6GNru4XLq44kSQcfCnqYgcwv2e8M33g9rPyRUgvmFrcw",
"9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY"
]
}- 400A field breaks a rule, or the wallet can't cover rent, fees or the first buy.
- 409That mint is already in use; generate a new keypair.
- 429Over the rate limit; see retryAfterSecs.
- 502SOL first buy: Jupiter has no route.
- 503Launches are paused, or configs aren't set up on this cluster.
/api/v1/launch/recordRecord a launch
After the launch lands: verifies the transaction and indexes the coin right away.
- Optional: the indexer finds every launch on its own. Recording makes the coin page and /coins/{mint} complete immediately.
signaturestringJSON bodyrequired- The launch transaction's signature.
curl -X POST https://halvd.fun/api/v1/launch/record \
-H "content-type: application/json" \
-d '{"signature":"<launch signature>"}'{
"ok": true,
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"pool": "9UVU8pMz1E8BqgBRTS4GCayrXvafFV2bQnWoQfyqwWhL",
"config": "26yVFHMfzRBTJV7GcQonc6Lj1PbEGcNNBgny3PMrpFfp",
"tier": "spark",
"mode": "holders",
"creator": "6GNru4XLq44kSQcfCnqYgcwv2e8M33g9rPyRUgvmFrcw",
"name": "API Stack",
"symbol": "STACK",
"uri": "http://localhost:3112/api/meta/LOEl69iWayBy",
"memo": null,
"btcAddress": "",
"hashDest": "",
"hashRig": "",
"inserted": true
}- 400Not a signature, the tx failed, or it didn't create a pool on a halvd config.
- 404Not visible yet. Retry in a few seconds.
- 429Over the rate limit; see retryAfterSecs.
/api/v1/btc-address/checkValidate a Bitcoin address
Whether a Bitcoin address can be bound to a BTC address coin: mainnet bc1 only.
- Native segwit (bc1q) and taproot (bc1p) on mainnet only. An invalid address is a 200 with valid: false and a reason.
addressstringqueryrequired- The Bitcoin address to check.
curl "https://halvd.fun/api/v1/btc-address/check?address=bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"{
"valid": true,
"type": "p2wpkh",
"normalized": "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"
}- 400address missing or longer than 120 characters.
- 429Over the rate limit; see retryAfterSecs.
Holder shares and claims
/api/v1/coins/{mint}/holdersA coin's holder pot
What came into the coin's holder pot, what snapshots credited, what's been claimed, and the last snapshot.
mintstringpathrequired- The coin's mint address.
curl "https://halvd.fun/api/v1/coins/<mint>/holders"{
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"symbol": "STACK",
"mode": "holders",
"potSats": 0,
"inSats": 1500,
"creditedSats": 1500,
"claimedSats": 0,
"owedSats": 1500,
"walletsCredited": 1,
"holdersCounted": 1,
"snapshots": 1,
"lastSnapshot": {
"ts": 1792194228,
"counted": 1,
"creditedSats": 1500,
"skipped": null
}
}- 400A parameter is missing or malformed; the message names it.
- 404No indexed coin with this mint.
- 429Over the rate limit; see retryAfterSecs.
/api/v1/coins/{mint}/holders/{wallet}Holder balance on one coin
What one wallet is owed by one coin, has claimed, and (Long hold) its current weight.
mintstringpathrequired- The coin's mint address.
walletstringpathrequired- A wallet address.
curl "https://halvd.fun/api/v1/coins/<mint>/holders/<wallet>"{
"wallet": "6GNru4XLq44kSQcfCnqYgcwv2e8M33g9rPyRUgvmFrcw",
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"symbol": "STACK",
"name": "API Stack",
"mode": "holders",
"owedSats": 1500,
"claimedSats": 0,
"creditedSats": 1500,
"pendingSats": 0,
"balance": "498710819286"
}- 400A parameter is missing or malformed; the message names it.
- 404No indexed coin with this mint.
- 429Over the rate limit; see retryAfterSecs.
/api/v1/wallets/{wallet}/sharesAll shares of a wallet
Every coin that owes or has paid this wallet a holder share.
walletstringpathrequired- A wallet address.
curl "https://halvd.fun/api/v1/wallets/<wallet>/shares"{
"wallet": "6GNru4XLq44kSQcfCnqYgcwv2e8M33g9rPyRUgvmFrcw",
"owedSats": 1500,
"minClaimSats": 500,
"claimable": true,
"shares": [
{
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"symbol": "STACK",
"name": "API Stack",
"mode": "holders",
"owedSats": 1500,
"claimedSats": 0,
"creditedSats": 1500,
"pendingSats": 0,
"balance": "498710819286"
}
]
}- 400A parameter is missing or malformed; the message names it.
- 429Over the rate limit; see retryAfterSecs.
/api/v1/holders/claim/prepareStart a claim
One transaction paying the wallet everything it's owed in cbBTC. The distributor has signed already; the wallet signs it unchanged.
walletstringJSON bodyrequired- The holder. Must be a regular (on-curve) key: it signs and pays the network fee.
mintsstring[]JSON body- Claim only these coins (up to 50). Default: everything owed.
curl -X POST https://halvd.fun/api/v1/holders/claim/prepare \
-H "content-type: application/json" \
-d '{"wallet":"<your wallet>"}'{
"claimId": "2011254774982b6ad7ea8555",
"transaction": "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA…",
"version": "legacy",
"totalSats": 1500,
"items": [
{
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"sats": 1500
}
],
"expiresAt": 1792194348,
"signers": [
"6GNru4XLq44kSQcfCnqYgcwv2e8M33g9rPyRUgvmFrcw"
]
}- 400Nothing owed, or under the 500-sat minimum.
- 409Another claim for this wallet is in progress (locks last two minutes).
- 429Over the rate limit; see retryAfterSecs.
- 503The distributor is between top-ups. Retry in a few minutes.
/api/v1/holders/claim/submitHand in a signed claim
Checks the signed claim is byte-for-byte the prepared one, sends it and settles the books.
- The wallet signs with signTransaction and your app posts the result here; halvd broadcasts it, waits for confirmation and only then marks the sats claimed. If the wallet inserted anything while signing, the bytes no longer match and the answer is 422.
claimIdstringJSON bodyrequired- From prepare.
signedTxstringJSON bodyrequired- The prepared transaction after the wallet signed it, base64.
curl -X POST https://halvd.fun/api/v1/holders/claim/submit \
-H "content-type: application/json" \
-d '{"claimId":"<claimId>","signedTx":"<base64 signed by the wallet>"}'{
"claimId": "2011254774982b6ad7ea8555",
"status": "confirmed",
"sig": "32UG99ab2YjN7VEKphKVuYNbhLT8S8Y53R47CySUJmnra4z8w5CKowaD81qg9CuzNTTubP7B5ZAYMysM6AHcAg1w",
"totalSats": 1500
}- 400A parameter is missing or malformed; the message names it.
- 404Unknown claimId.
- 409Already sent, or no longer pending.
- 410The two-minute lock lapsed. Prepare again.
- 422The transaction was changed, or the wallet's signature is missing or invalid.
- 429Over the rate limit; see retryAfterSecs.
- 502Sending failed; status says whether it can still land.
/api/v1/holders/claim/statusFollow a claim
Where a claim stands: pending, sending, confirmed, failed or expired.
idstringqueryrequired- The claimId.
curl "https://halvd.fun/api/v1/holders/claim/status?id=<claimId>"{
"id": "2011254774982b6ad7ea8555",
"wallet": "6GNru4XLq44kSQcfCnqYgcwv2e8M33g9rPyRUgvmFrcw",
"status": "confirmed",
"totalSats": 1500,
"items": [
{
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"sats": 1500
}
],
"sig": "32UG99ab2YjN7VEKphKVuYNbhLT8S8Y53R47CySUJmnra4z8w5CKowaD81qg9CuzNTTubP7B5ZAYMysM6AHcAg1w",
"error": null,
"createdAt": 1792194228,
"expiresAt": 1792194348
}- 400A parameter is missing or malformed; the message names it.
- 404Unknown claim.
- 429Over the rate limit; see retryAfterSecs.
BTC payouts and hashrate
/api/v1/coins/{mint}/payoutsPayout ledger
A BTC address coin's bound address, what's owed, the payout rule in force, and every payout with its receipts.
mintstringpathrequired- The coin's mint address.
curl "https://halvd.fun/api/v1/coins/<mint>/payouts"{
"mint": "2Q9AxwQkre8RxvgC9AdhLwCrXFbN8NLV8BvQSmGTLry3",
"mode": "btc",
"address": "bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297",
"addressValid": true,
"addressType": "p2tr",
"owedSats": 0,
"paidSats": 0,
"inSats": 0,
"lastPayoutAt": null,
"rule": {
"minSats": 12000,
"maxFeePct": 10,
"relaxed": false
},
"lastCheck": null,
"payouts": []
}- 400A parameter is missing or malformed; the message names it.
- 404Not indexed, or not a btc mode coin.
- 429Over the rate limit; see retryAfterSecs.
/api/v1/coins/{mint}/rigRig detail
A Hashrate coin's rig: state, hash bought, claims, fundings, bids and the blocks it took part in.
mintstringpathrequired- The coin's mint address.
curl "https://halvd.fun/api/v1/coins/<mint>/rig"{
"rig": {
"mint": "2gwc5BPrwEDnbh27WGMsB9hkovpeeAm6iPe4M5Patr3J",
"name": "Keeper Test KTD",
"symbol": "KTD",
"image": "http://127.0.0.1:54404/img/KTD.png",
"rig": "solo",
"dest": "burn",
"address": "bc1qq58ftdp7sv8gr0qyjp45vmkagkathjanz09dru",
"worker": "2gwc5BPrwEDn",
"state": "idle",
"speedPh": 0,
"sharesM": 0,
"phHours": 0,
"spentSats": 0,
"potSats": 0,
"fundedSats": 0,
"budgetSats": 0,
"claimedSats": 0,
"nextPoolShare": null,
"blocks": 0
},
"config": {
"connected": false,
"missing": [
"BRAIINS_TOKEN",
"BRAIINS_DEPOSIT"
],
"poolAddress": "bc1qlye2k5652vs2ymdfth8j4n90dezr34lvln9crm",
"stratum": "stratum+tcp://solo.ckpool.org:3333",
"fundMinSats": 11000,
"bidMinSats": 10000,
"maxBridgeFee": 0.1,
"platformShare": 0.25,
"keeperBlockShare": 0.05,
"finderShare": 0.1,
"maturity": 100
},
"fundings": [],
"bids": [],
"blocks": [],
"claims": []
}- 400A parameter is missing or malformed; the message names it.
- 404Not a Hashrate coin.
- 429Over the rate limit; see retryAfterSecs.
/api/v1/rigsAll rigs
All Hashrate coins' rigs with totals, and whether the operator's hashrate account is connected.
curl "https://halvd.fun/api/v1/rigs"{
"stats": {
"speedPh": 0,
"mining": 0,
"hashCoins": 31,
"phHours": 0,
"spentSats": 0,
"blocks": 0,
"poolBlocks": 0,
"soloBlocks": 0,
"rewardSats": 0,
"creditedSats": 0
},
"rigs": [
{
"mint": "9MQtLqtxG2zVzrxHMFqYfJN4nRXoGpB2ZnaotXg3tc9o",
"name": "Halving Test Coin",
"symbol": "HTEST",
"image": "http://localhost:3112/api/img/X-0_swH18Yhq",
"rig": "solo",
"dest": "burn",
"address": "bc1q330xltq2kndhyqdl2u2gmk4na7ems027mwl3tq",
"worker": "9MQtLqtxG2zV",
"state": "idle",
"speedPh": 0,
"sharesM": 0,
"phHours": 0,
"spentSats": 0,
"potSats": 0,
"fundedSats": 0,
"budgetSats": 0,
"claimedSats": 0,
"nextPoolShare": null,
"blocks": 0
},
{
"mint": "spGwBAnMnWtrB3LrNkY2X5TzUhKpH9psktmX4wWPCvH",
"name": "Keeper Test KTD",
"symbol": "KTD",
"image": "http://127.0.0.1:62740/img/KTD.png",
"rig": "solo",
"dest": "burn",
"address": "bc1q5cvl4lf58ydz4ql053gczaw9cfl6wz02rk9zas",
"worker": "spGwBAnMnWtr",
"state": "idle",
"speedPh": 0,
"sharesM": 0,
"phHours": 0,
"spentSats": 0,
"potSats": 0,
"fundedSats": 0,
"budgetSats": 0,
"claimedSats": 0,
"nextPoolShare": null,
"blocks": 0
}
],
"config": {
"connected": false,
"missing": [
"BRAIINS_TOKEN",
"BRAIINS_DEPOSIT"
],
"poolAddress": "bc1qlye2k5652vs2ymdfth8j4n90dezr34lvln9crm",
"stratum": "stratum+tcp://solo.ckpool.org:3333",
"fundMinSats": 11000,
"bidMinSats": 10000,
"maxBridgeFee": 0.1,
"platformShare": 0.25,
"keeperBlockShare": 0.05,
"finderShare": 0.1,
"maturity": 100
},
"updatedAt": 1792194011
}- 429Over the rate limit; see retryAfterSecs.
/api/v1/blocksBlocks found
Bitcoin blocks halvd's rigs found, newest first, with how each splits and where it stands.
limitintegerquery- Blocks, 1 to 200.Default 50.
curl "https://halvd.fun/api/v1/blocks?limit=10"{
"stats": {
"speedPh": 0,
"mining": 0,
"hashCoins": 31,
"phHours": 0,
"spentSats": 0,
"blocks": 0,
"poolBlocks": 0,
"soloBlocks": 0,
"rewardSats": 0,
"creditedSats": 0
},
"blocks": []
}- 400A parameter is missing or malformed; the message names it.
- 429Over the rate limit; see retryAfterSecs.
Event stream
- Format
- Server-Sent Events:
event:is the type,data:one line of JSON,id:where the stream stands. Every payload hasat, the unix second of the change. - Delay
- Events come from halvd's index, which reads pools every few seconds; the stream checks it every 2 s. Expect events seconds after the chain, not milliseconds.
- On connect
- A
helloevent with the current id. Then live events, and a comment line every 15 s so proxies keep the connection open. - Resuming
- An id is a position in halvd's tables, so a client that drops off can pick up exactly where it stopped, even after hours: pass the last id in a
Last-Event-IDheader (a browser EventSource sends it on its own when it reconnects) or as?since=. Up to 1,000 missed events are replayed before the live ones; beyond that you get a singlegapevent and should catch up through the REST endpoints.
/api/v1/eventsLive events
Server-Sent Events for new coins, epochs, graduations, trades, burns and boosts. Resumable with Last-Event-ID.
sincestringquery- Resume after this event id (same as the Last-Event-ID header).
Last-Event-IDstringheader- Sent by EventSource on reconnect. Replays what happened after that id, up to 1,000 events.
curl -N https://halvd.fun/api/v1/events
# resume after the last id you saw
curl -N -H "Last-Event-ID: <id>" https://halvd.fun/api/v1/eventsretry: 3000
id: 114-151-27
event: hello
data: {"cluster":"fork","cursor":"114-151-27","pollMs":2000,"heartbeatS":15,"types":["coin.created","coin.epoch","coin.graduated","trade","burn","boost"]}
id: 114-0-0
event: coin.created
data: {"mint":"9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY","name":"API Stack","symbol":"STACK","tier":"spark","mode":"holders","creator":"6GNru4XLq44kSQcfCnqYgcwv2e8M33g9rPyRUgvmFrcw","pool":"9UVU8pMz1E8BqgBRTS4GCayrXvafFV2bQnWoQfyqwWhL","image":"http://localhost:3112/api/img/LOEl69iWayBy","createdAt":1792194168,"at":1792194168}
id: 114-151-0
event: trade
data: {"mint":"9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY","symbol":"STACK","tier":"spark","at":1792194225,"venue":"curve","side":"buy","btcSats":99000,"priceSats":0.24196530008234407,"reserveSats":118800}
: beat 1792234888- 400since isn't an event id.
- 429Over the connection rate, or 4 streams already open from this IP.
- 503The server's stream capacity is full. Retry after Retry-After.
Event types
coin.createdA coin entered halvd's index: recorded right after its launch, or found by the indexer.
mint, name, symbol, tier, mode, creator, pool, image, createdAt, at
id: 114-0-0
event: coin.created
data: {
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"name": "API Stack",
"symbol": "STACK",
"tier": "spark",
"mode": "holders",
"creator": "6GNru4XLq44kSQcfCnqYgcwv2e8M33g9rPyRUgvmFrcw",
"pool": "9UVU8pMz1E8BqgBRTS4GCayrXvafFV2bQnWoQfyqwWhL",
"image": "http://localhost:3112/api/img/LOEl69iWayBy",
"createdAt": 1792194168,
"at": 1792194168
}coin.epochA buy took a coin's curve price into an epoch it had never reached before. Selling back under a boundary and buying again doesn't repeat it.
mint, symbol, tier, from, to (both 1 to 4), priceSats, at
id: 114-150-0
event: coin.epoch
data: {
"mint": "FEJEf6fubFKfdgfA3G76MMc32ZoRu9EebcQ7ZA8BwVjs",
"symbol": "KTA",
"tier": "spark",
"at": 1792194225,
"from": 1,
"to": 4,
"priceSats": 3.7523401025205874
}coin.graduatedThe indexer saw the sold-out curve migrate to its DAMM v2 pool. Quotes and trades move there from now on.
mint, symbol, name, tier, dammPool, priceSats, graduatedAt, at
id: 114-143-0
event: coin.graduated
data: {
"mint": "AL6sL2cgQiZSSKmp65stYgmVAxdFrNorXnDpbLHyapeb",
"symbol": "KTA",
"tier": "spark",
"at": 1792193000,
"name": "Keeper Test KTA",
"dammPool": "Cd8JRpEVmKYUY6ssTzTVwfSGH2nQSSu8U175Gt3GqhJi",
"priceSats": 3.7523401025205874,
"graduatedAt": 1792192954
}tradeA pool changed and BTC moved. Side comes from the direction of the BTC reserve; trades that land between two reads arrive as one with the net change.
mint, symbol, tier, venue (curve | dammV2), side, btcSats, priceSats, reserveSats, at
id: 114-151-0
event: trade
data: {
"mint": "9FsMyTkjg1vrp8S6nv2Vb9GLBrpkpZdQyKqNHGZ3NBiY",
"symbol": "STACK",
"tier": "spark",
"at": 1792194225,
"venue": "curve",
"side": "buy",
"btcSats": 99000,
"priceSats": 0.24196530008234407,
"reserveSats": 118800
}burnA burn reached the ledger: the keeper's $HALVD buyback, a graduated curve's dust, or a mined block's burn share.
sig, kind (buyback | leftover | block), mint, amount, btcSpent, wallet, at
id: 114-151-27
event: burn
data: {
"sig": "2oNRjurPzSeYLnNdoFwvXfpadoz1JB4RjVCR2rEmJMiuqmkrDBQgJVs2epEw2wqVX1VqQFagZJSJkf3HmXZEv6Wn",
"kind": "leftover",
"mint": "AL6sL2cgQiZSSKmp65stYgmVAxdFrNorXnDpbLHyapeb",
"amount": 2100.000005,
"btcSpent": 0,
"wallet": "BdcxkPEKvPJ74vX6gKRXhCgV4ZUAkCaCnkXuUES3nMSY",
"at": 1792193266
}boostSomeone burned $HALVD to boost a coin.
sig, mint (the boosted coin), burnedMint, wallet, amount, at
id: 114-151-23
event: boost
data: {
"sig": "JVqC1BmYS7iTbwnsAUAw726pqubVAnUhJ24b57yTsZraLdzWwwoGScxW9byPRSyLm2cBusEYdTC2dT5BRaCCiKo",
"mint": "F1m1eZZMvVzwEwWRGuMVKQqhhPKQf6yzv28CDZgRkFAy",
"burnedMint": "2sNyqhYwx9TQ6BrMCscPowKnpYn3oWiQ4bHjyu5iXy4G",
"wallet": "3p88wLtDR5knZiktNStvhZd1iuPewGUYAccA1oUgVPmn",
"amount": 1500,
"at": 1792190907
}const stream = new EventSource("https://halvd.fun/api/v1/events");
stream.addEventListener("coin.epoch", (e) => {
const x = JSON.parse(e.data);
console.log(`$${x.symbol} reached epoch ${x.to} at ${x.priceSats.toFixed(3)} sats`);
});
stream.addEventListener("trade", (e) => {
const t = JSON.parse(e.data);
if (t.btcSats >= 1_000_000) console.log(`big ${t.side}: ${t.btcSats / 1e8} BTC of $${t.symbol}`);
});
// EventSource reconnects by itself and sends Last-Event-ID, so nothing is missed// Node 20+: no library needed
let last = ""; // keep the last id to resume after a restart
async function listen() {
const res = await fetch("https://halvd.fun/api/v1/events", { headers: last ? { "Last-Event-ID": last } : {} });
if (!res.ok) throw new Error((await res.json()).error);
let pending = "";
for await (const chunk of res.body.pipeThrough(new TextDecoderStream())) {
pending += chunk;
const frames = pending.split("\n\n");
pending = frames.pop();
for (const frame of frames) {
const field = (k) => frame.match(new RegExp(`^${k}: (.*)$`, "m"))?.[1];
if (field("id")) last = field("id");
if (field("event") === "coin.graduated") console.log("graduated", JSON.parse(field("data")).symbol);
}
}
}The shapes you'll see most
Field by field. Every other response is described in the OpenAPI document.
mintstring- The coin's mint (base58 address).
poolstring- Its Meteora DBC pool.
configstring- The DBC config it launched on: one per tier and mode.
tierstring- Graduation target tier. (spark, block, vault, whole)
modestring- Who receives the 60% fee share. (creator, holders, longhold, btc, hash)
creatorstring- The wallet that launched it.
namestring- Name from its metadata (can be empty for a moment after launch).
symbolstring- Ticker, without the $.
imagestring | null- Image URL from its metadata.
descriptionstring- Description from its metadata.
xUrlstring | null- X link from its metadata.
websitestring | null- Website from its metadata.
createdAtinteger- Unix seconds the pool opened.
createSigstring | null- The launch transaction's signature, once known.
priceSatsnumber- Sats per coin now: from the curve, or from the DAMM v2 pool after graduation.
priceUsdnumber | null- Dollars per coin at the current BTC/USD (null if no price source answered).
fdvBtcnumber- priceSats × 21,000,000 ÷ 10^8.
mcapBtcnumber- priceSats × coins in circulation ÷ 10^8. On the curve that's coins sold; after graduation the whole supply.
epochinteger- 1 to 4: the curve epoch the price sits in. A graduated coin reads 4.
graduatedboolean- True once the curve sold out and the coin moved to its DAMM v2 pool.
raisedSatsinteger- cbBTC on the curve, in sats (the target once graduated).
targetSatsinteger- The tier's graduation target, in sats.
progressnumber- raisedSats ÷ targetSats, 0 to 1.
soldCoinsnumber- Coins bought off the curve so far.
toNextHalvingnumber- Coins left in the current epoch before the price step (epoch 4: before graduation).
dammPoolstring | null- The DAMM v2 pool once graduated.
graduatedAtinteger | null- Unix seconds it graduated.
volumeSatsinteger- All-time BTC volume the indexer saw, in sats.
tradesinteger- Pool changes the indexer saw (about one per trade).
boostnumber- Boost left now, in whole $HALVD. Each boost burn fades to zero over 24 hours.
btcAddressstring | null- btc mode: the Bitcoin address bound at launch.
hashDeststring | null- hash mode: where found blocks go. (holders, burn)
hashRigstring | null- hash mode: pool or solo mining. (pool, solo)
mintstring- The coin.
venuestring- curve before graduation, dammV2 after. (curve, dammV2)
sidestring- buy or sell. (buy, sell)
assetstring- What a buy pays with, or what a sell pays out in. (btc, sol)
inAmountstring- Atoms going in.
outAmountstring- Atoms expected out.
minOutstring- The floor after slippage. The transaction fails below it.
feeBpsinteger- The pool fee on this trade, live: up to 4999 in a coin's first seconds, then 100.
priceImpactnumber- How much worse than spot, fee excluded: 0.012 = 1.2%.
inUinumber- inAmount as a decimal.
outUinumber- outAmount as a decimal.
minOutUinumber- minOut as a decimal.
inSymbolstring- cbBTC, SOL or coin.
outSymbolstring- cbBTC, SOL or coin.
spotSatsnumber- Sats per coin before the trade.
avgSatsnumber- Sats per coin this trade averages, fee included.
poolSatsstring- Sats that reach (buy) or leave (sell) the pool.
refundSatsstring- Curve buys: sats handed back because the curve ends first.
graduatesboolean- This buy fills the curve: the coin graduates.
ageSecondsinteger- Seconds since the pool opened, by chain time.
slippageBpsinteger- Slippage used for minOut.
jupobject · optional- SOL trades: the Jupiter leg.
transactionstring- The serialized transaction, base64.
versionstring | integer- legacy → Transaction.from(bytes); 0 → VersionedTransaction.deserialize(bytes). (legacy, 0)
bytesinteger- Serialized size (at most 1232).
signersstring[]- Public keys that must sign, fee payer first.
mintstring · optional- The coin.
quoteQuote · optionalamountnumber · optional- Boost: whole $HALVD burned.
memostring · optional- Boost: the memo the transaction carries.
Skip the API entirely
Nothing here needs halvd's servers. The coins are ordinary Meteora DBC pools; what marks one as halvd's is its config, and there are 20 of them, listed at the bottom of this section.
- Programs
- Meteora DBC
dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqNfor the curve and Meteora DAMM v2cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGGafter graduation. Trade them with@meteora-ag/dynamic-bonding-curve-sdkand@meteora-ag/cp-amm-sdk. - Finding coins
- List the DBC pools whose config is one of the 20 (a memcmp on the config field), or read the configs from
/api/v1and /api/addresses. - Buys
- Use DBC's
swap2with partial fill on the curve: an exact-in buy larger than what's left would fail, partial fill stops at the end and refunds.
import { PublicKey } from "@solana/web3.js";
import { DAMM_V2_MIGRATION_FEE_ADDRESS, deriveDammV2PoolAddress, deriveDbcPoolAddress } from "@meteora-ag/dynamic-bonding-curve-sdk";
const CBBTC = new PublicKey("cbbtcf3aa214zXHbiAZQwf4122FBYbraNdFqgw4iMij");
// the curve: one pool per (quote mint, coin mint, config)
const curve = deriveDbcPoolAddress(CBBTC, mint, config); // config: one of the 20 below
// after graduation: the DAMM v2 pool DBC migrates into
const pool = deriveDammV2PoolAddress(DAMM_V2_MIGRATION_FEE_ADDRESS[6], mint, CBBTC);Memos
Plain SPL Memo instructions. The launch memos are signed by the creator inside the launch transaction; anything else (no memo, two memos, an unsigned one) binds nothing.
halvd payout v1 <bc1 address>halvd hash v1 <holders|burn> <pool|solo>halvd boost v1 <coin mint>halvd burnhalvd treasuryhalvd platform sharehalvd claimAddresses on this cluster
v1 only grows
What you build against today keeps working.
- Growth only
- v1 may get new endpoints, new optional parameters and new fields in its answers. Write clients that skip keys they don't recognise and they'll keep working.
- Breaking changes
- They wait for a v2 path, and v1 keeps answering next to it for a deprecation period announced here.
- Private routes
- Paths under
/api/withoutv1exist for halvd's own pages. Their shapes follow the site's needs from one release to the next, so don't depend on them. - Rules
- Off-chain thresholds (holder minimum $10, claims from 500 sats, payouts from 12,000 sats) can be tuned; read them from the index rather than hard-coding them. On-chain rules (supply, curve, fees) are fixed by the configs. See the rules.
Examples on this page were captured from a local copy of mainnet and shortened. Nothing here is a promise about price or returns; burns of $HALVD reduce its supply and that is all.