Token launch (pumpfun / bonk / bags / meteora_dbc / meteora_cpamm)
Replaces POST /api/sol/create from public.raze.sh. Body discriminates
on platform.
pumpfun (incl. Mayhem via pumpType), bonk (Raydium LaunchLab:
letsbonk.fun and Bonkers) and bags (bags.fm on Meteora DBC — see the
LaunchRequest schema for its two-step config provisioning) are
implemented. meteora_dbc and meteora_cpamm still return a 501-style
error.
Bonk — what differs from pumpfun
The initialize is built in-process, so a launch with your own mint key
costs zero third-party round-trips. Consequences worth knowing:
mintPubkeyis rejected onbonk. The base mint must sign the create, and LaunchLab exposes no equivalent of pump’ssign-create-tx, so an address without its secret is unsignable by anyone. Send the 64-byte secret asmintKey, or omit both and one is generated.- The official
…bonkvanity suffix is not available here: it only comes from letsbonk’s own endpoint, which returns their transaction. - Quote is wSOL or USD1 — see Quote asset below.
- Curve shape is yours:
supply,totalBaseSell,fundRaisingQuote,decimals. Bounds are validated against the launchpad’s on-chainglobal_config, so you get a message naming the offending minimum instead of an opaquecustom program error: 0x9ca. Those minimums change on chain — they are read live, not hardcoded, and each quote asset has its own config with its own numbers. vestinglocks part of the supply for the creator (LaunchLabVestingParams). Omitted ⇒ no lock, which is what every mainnet LetsBonk launch does today.feeWallet+feeBpscharge your fee in the quote asset on each buyer’s raw amount, on that buyer’s own transaction — including the creator’s buy fused into the create.wallets[0]is the creator; itsamountbecomes a first buy fused into the create transaction. Each further wallet gets its own buy transaction, priced against the curve as the earlier buys leave it. Max 5 wallets (the Jito bundle cap).- Buy slippage defaults to 500 bps, not pumpfun’s 9900.
There is no preview mode. ?dryRun=true used to return a cost
estimate; it was retired on 2026-07-28 and the server now rejects the
flag with success: false. It priced the bonding curve from constants
compiled into the server rather than from the on-chain state the launch
would actually hit, so it could disagree with the very launch it was
previewing. The flag is rejected rather than ignored on purpose — a
silently-dropped dryRun would have turned a preview into a real launch.
Returns partially-signed transactions: you sign the wallet legs and submit them yourself, as one Jito bundle (max 5 tx), in the order given.
Quote asset
quoteMint selects what buyers pay with. What is available depends on
the platform, because the two launchpads model this differently.
pumpfun — omit it (or send "SOL") for a SOL-quoted launch, or
send the USDC mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v for a
USDC-quoted one. Only these two are supported: an unparseable value
silently falls back to SOL, and any other mint that happens to parse
is treated as 9-decimal, so do not send one.
bonk — LaunchLab keeps a separate global_config per quote
asset, so the choice selects the config, the pool address and the shape
of the buy leg all at once. Two exist on chain:
quoteMint | Config | Minimum raise |
|---|---|---|
"SOL" / So111…112 (default) | 6s1xP3hp… | 24 SOL |
"USD1" / USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB | EPiZbnrT… | 5 000 USD1 |
Anything else is a 400, never a fallback — a launch quoted in the
wrong asset cannot be undone once it is on chain. The server also reads
the config’s own quote_mint back and refuses if it disagrees with what
you asked for.
On a USD1 launch, wallets[].amount and fundRaisingQuote are in whole
USD1, and each buyer must already hold USD1 in its associated token
account — that is the account the swap debits. There is no wrap step
and the account is never closed. Rent and Jito tips are still SOL.
fundRaisingSol is a SOL-only alias for fundRaisingQuote; sending it
on a USD1 launch is rejected rather than reinterpreted, since the name
would be lying about the unit. fundRaisingQuote has no default on
USD1 — there is no observed mainnet convention to borrow, so state it.
On a USDC-quoted pumpfun launch every wallets[].amount is
denominated in USDC, and the wallets must already hold USDC: there is no
pre-swap, so a wallets[].sourceMint that differs from quoteMint is
rejected.
Lookup tables
A bonk response never contains a lookup table: every transaction is
compiled with an empty ALT list and lookupTableAddress is absent. So a
tip you add yourself is always a plain static account key that Jito and
the relay can see. (The warning about compressed tips applies to
pumpfun advanced mode, which does return a lookupTableAddress.)
Jito tip — you own it
The server does not sponsor the tip, and neither does the relay. How many transactions come back, and which one can carry a tip:
| What you launch | Transactions | Server-embedded tip |
|---|---|---|
| deploy only | 1 (create) | none |
| deploy + creator buy | 1 (create+buy fused) | none |
| deploy + creator buy + N buys | 1 + N | on the last buy |
A tip never rides a create transaction, so a single-transaction launch
comes back with no tip at all. Send it to sendBundle as-is and it is
rejected — you must attach your own tip first.
The recommended integration is to always own the tip: pass
jitoTipAmountSol: 0, then append your own SystemProgram.transfer to one
of the eight Jito tip accounts before submitting.
Authorizations
Body
Token launch. pumpfun, bonk and bags are implemented; meteora_dbc / meteora_cpamm return 501. Unknown keys are ignored silently, so check the exact casing of every field name — in particular jitoTipAmountSol and initialLiquiditySol end in Sol, not SOL.
bags behaves differently from the other two in ways you must plan for. pumpfun and bonk are built entirely on our side. A Bags launch cannot be: on the Meteora DBC initialize, the pool creator, the payer and the vanity …BAGS base mint are all signed by Bags, so the launch transaction comes from their API — already signed, and therefore unmodifiable.
The fee-share config is a reusable on-chain asset, not a step of the launch. Bags' launch build verifies the config exists on chain at request time, so a config created in the same request can never be referenced by it — bundling does not help. The flow is therefore:
-
Provision — call without
feeShareConfigKey(with yourbagsFeeClaimers/bagsConfigType). The response is astagesentry with Bags' unsigned config-creation transactions plusconfig,mintandmetadataUri. Sign and land the transactions, wait for confirmation. Land the stage promptly: the config transactions expire with their embedded blockhash (~60–90 s). A stage'stransactionsare the config creation, not the launch and not a Jito bundle — submit them individually, confirm, then do step 2; sending them as the launch bundle would spend the config rent and produce no token. -
Launch — call again with
feeShareConfigKey=<config>,mintPubkey=<mint>andtoken.metadataUri=<metadataUri>, all three exactly as returned — that second call returns the full launch bundle without burning a second mint.
One config per LAUNCH — a config cannot be reused for a different mint. Bags' launch build accepts any on-chain config, but their claim attribution is per mint (measured 2026-07-31 with real launches on one config: the provisioned mint's creator roster was intact, a reused launch's roster came back empty and its mint never appeared in the fee-share admin list — its trading fees are unclaimable). This binding is invisible on chain, so the server asks Bags directly whether the mint you pass was provisioned for the config you pass, and rejects the launch if not (and rejects feeShareConfigKey without mintPubkey outright). Pass the config, mint and metadataUri exactly as the provisioning call returned them — a crossed or fresh mint is refused before anything is signed, so you cannot accidentally launch a token whose fees no one can claim.
The on-chain fees follow the config. The fee_claimer frozen in the on-chain PoolConfig receives the token's trading fees. You do not have to police this: because the server rejects any config not provisioned for your mint (above), the only config you can launch against is the one born from your own bagsFeeClaimers on step 1 — so the recipients are what you asked for. The launch response echoes feeClaimer for your records. Because there is no non-mutating way to prove a config pays a given claimer list, bagsFeeClaimers and bagsConfigType are rejected alongside feeShareConfigKey — the split and the template are already frozen in the config.
Further consequences, all observable in the response:
-
the first transaction of the launch bundle is Bags-signed (the launch itself); do not re-serialise or re-order it;
-
5 wallets fit the bundle (
1 launch + 4 buys), likebonk; -
mintKeyis rejected (the mint is theirs);mintPubkeyis only accepted together withfeeShareConfigKey, to name the mint a provisioning call reserved; -
exactly one transfer of ours can ride on the launch transaction, so asking for both a
feeWallet/feeBpsand atipWallet/tipLamportswith only one wallet is a 400 rather than a silently dropped leg; -
jitoTipAmountSolneeds at least one extra wallet to attach to; on a single-wallet launch it is a 400 (send the tip as its own bundle transaction instead); -
a tip can only ride on a buy transaction of ours (the launch is sealed). With 1–4 wallets the bundle has a free slot: send
jitoTipAmountSol: 0and append your own tip transaction. With 5 wallets all five slots are full — passjitoTipAmountSoland we embed it on the last buy; -
the curve and the fee split come from a Bags fee template, picked whole rather than tuned field by field:
supply,totalBaseSell,fundRaisingSol,fundRaisingQuote,decimals,vestingandconfigAddressare rejected, not ignored — accepting them would let you believe you had shaped a curve you did not touch; -
the response carries
config(the Meteora DBCPoolConfig) andfeeClaimeralongsidemintandpool. You needconfigto build any later swap of your own, and it is not derivable from the mint; -
token.descriptionis required (Bags rejects an empty one), whereas it is optional onpumpfunandbonk— except on the follow-up call withmintPubkey, where the token info was already reserved; -
every wallet pays its own buy and the bundle is atomic: balances are pre-checked on our side (payer and co-buyers) and an underfunded wallet is a 400 naming it, instead of a silent on-chain revert.
pumpfun, bonk, bags, meteora_dbc, meteora_cpamm Quote asset for the launch. wallets[].amount is always denominated in it. The accepted values and the failure mode differ per platform — do not generalise from one to the other.
pumpfun: "SOL"/"wSOL"/the wSOL mint (or omitted), or USDC — either the string "USDC" or the mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v. Anything else is a 400 — never a fallback (since 2026-07-31; before that an unrecognized value silently launched in SOL, so a mistyped or wrong-cased USDC gave you the wrong asset with no error). The amount and any feeBps are denominated and charged in the chosen asset.
bonk: "SOL"/So111…112 (default) or "USD1"/USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB. Anything else is a 400 — never a fallback, and the server additionally reads the launchpad config's own quote_mint back and refuses if it disagrees. USDC is not accepted here: LaunchLab has no USDC global_config.
bags: "SOL"/So111…112 or omitted. Anything else is a 400 — never a fallback. Bags' configs are SOL-quoted and their API takes the first buy in lamports.
bs58-encoded 64-byte mint secret key. The create transaction is signed locally with it, so the returned bundle needs only your wallet signatures. Wins over mintPubkey if both are sent. Accepted on both platforms.
Omitting it differs per platform: on pumpfun the server reaches for a …pump vanity mint; on bonk it just generates a keypair in-process — the official …bonk suffix only exists behind letsbonk's own endpoint and is not available here.
bs58-encoded 32-byte mint address. On pumpfun: a Pump.fun vanity mint you already reserved — the mint signature slot is filled via the Pump frontend API, and the wallet legs stay unsigned for you to complete. Ignored when mintKey is also present.
On bags (since 2026-07-31): the …BAGS mint a provisioning call reserved, accepted only together with feeShareConfigKey and token.metadataUri — the follow-up launch then reuses it instead of burning a second mint. Without the key it is a 400.
On bonk this is a 400, not a silent downgrade: the base mint must sign initialize and LaunchLab has no equivalent of pump's sign-create-tx, so an address without its secret is unsignable by anyone. Send mintKey instead.
Buy slippage tolerance, 0–10000. Defaults to 9900 (99%) on pumpfun, 500 (5%) on bonk, and 500 (5%) on bags — on both of the latter the later buyers' prices are modelled exactly against the curve, so a 99% floor would be protection in name only.
bonk only. Selects the LaunchLab platform_config, which decides the swap fee and whether the token creator earns anything. letsbonk's own three modes, with their own names accepted as aliases:
| value | address | swap fee | creator fee | |---|---|---|---| | standard · letsbonk · memes · platformId | FfYek5vE… | 1.25% | 0 | | bonkers · tech · techPlatformId | 82NMHVCK… | 1.00% | 0.05% | | balanced · balancedPlatformId | BuM6KDpW… | 1.25% | 0 |
🔴 Changed 2026-07-30. bonkers used to resolve to BuM6KDpW…, which is letsbonk's Balanced mode — 1.25% and no creator fee, the same economics as standard, and the opposite of what their UI promises for the mode it labels "Bonkers" ("This mode has creator fees"). It now resolves to 82NMHVCK…. If you were sending bonkers and want the old target, send balanced.
Any other value is rejected. For a config none of these names covers, pass platformConfig.
standard, letsbonk, memes, platformId, bonkers, tech, techPlatformId, balanced, balancedPlatformId bonk only. Explicit LaunchLab platform_config address — there are ~2000 on chain and 30 alone are named letsbonk.fun, so the named modes above cannot cover them. Also accepted as platformId.
Mutually exclusive with bonkType: sending both is a 400, because two sources of truth for the same account is how a caller ends up on a config they did not pick.
Verified on chain before use — the account must exist, be owned by the LaunchLab program and carry the PlatformConfig discriminator. A wrong value is a 400, never a silent fall back to the default.
Since 2026-07-31 the config's on-chain fee rates (fee_rate, creator_fee_rate, plus the global trade_fee_rate) also price the buys: every minimumAmountOut floor is computed with the real total fee of the config you picked, not LetsBonk's 1.5%.
💰 Funding floor (bonk, all configs): a LaunchLab create debits wallets[0] 24_532_480 lamports (0.02453248 SOL) of rent and Metaplex fees — mint 1_461_600 + pool 3_876_720 + two vaults 2_039_280 each + metadata 15_115_600 (of which 10_000_000 is the Metaplex fee). Measured on three mainnet creates, reconciled to the lamport. On top of that the server's pre-check counts, because the runtime deducts or debits them too: the create transaction's own fees (10_000 base + the priority fee on the requested compute units — 20_000 at the defaults, scaling with computeUnitPrice); and, when the first buy is fused in: 2_039_280 for the creator's token ATA, the buy amount itself, another 2_039_280 for the transient wSOL account (recovered at close, but payable mid-transaction), the feeBps cut and any tipLamports. The rejection names the exact lamport total instead of letting the bundle die on chain after the vanity mint and the IPFS pin are already spent.
bonk only. Total token supply in base units. Defaults to the LetsBonk shape (1e15). Must be ≥ the launchpad's on-chain min_supply_a.
⚠️ Send this as a string from JavaScript. It is a u64, and the letsbonk UI offers supplies up to 1e18 base units — well past Number.MAX_SAFE_INTEGER (9.007e15), so a numeric literal like 123456789123456789 is already rounded to …780 before your request is sent. A decimal string carries all 64 bits, and letsbonk's own frontend sends it that way. Numbers are still accepted (added 2026-07-30; before that a string was a 422).
bonk only. Tokens sold on the curve before graduation; the remainder seeds migration liquidity. Defaults to 793_100_000_000_000 (79.31% of the default supply). Must satisfy the launchpad's min_sell_rate_a and min_migrate_rate_a.
bonk only. Amount raised on the curve before graduation, in whole quote tokens — 85 means 85 SOL on a SOL-quoted launch, 5000 means 5000 USD1 on a USD1-quoted one. Must be ≥ that config's on-chain min_fund_raising_b (24 SOL / 5 000 USD1 at the time of writing — read live, so treat these as indicative). Defaults to 85 SOL on a SOL launch; required on a USD1 launch.
bonk only. SOL-denominated alias for fundRaisingQuote, kept for compatibility. fundRaisingQuote wins when both are sent, and sending this on a non-SOL launch is rejected rather than reinterpreted.
bonk only. Base mint decimals. Defaults to 6.
bonk only. LaunchLab VestingParams — a creator lock carved out of the supply. Omitted ⇒ all zeroes (no lock), which is what every sampled mainnet LetsBonk create emits. totalLockedAmount is checked against the config's max_lock_rate (80% of supply today) before the transaction is built; the program enforces the rest.
meteora_dbc / meteora_cpamm only, both of which return 501 today.
Rejected on pumpfun, bonk and bags (since 2026-07-30) rather than ignored: it used to be accepted and dropped there — including values that are not even valid base58 — so a caller could not tell "ignored" from "accepted" without deserialising the returned transaction and reading which config it references. On bonk, use bonkType or platformConfig.
Note the casing: ...Sol, not ...SOL.
Custom tip recipient wallet (defaults to the Raze relay tip wallet).
Flat tip in lamports attached once to the create tx (payer pays). feeTipLamports is the legacy alias.
Legacy alias for tipLamports.
Custom fee recipient wallet. Only charged together with feeBps.
Percentage fee in basis points of each buyer's raw quote amount, paid by that buyer alongside its buy. pumpfun and bonk differ here: on bonk the creator's buy fused into the create is charged like any other buyer; on pumpfun the fee rides the buy transactions only, so wallets[0]'s fused buy is not charged. On a USD1-quoted bonk launch the fee is an SPL transfer in USD1 (the fee wallet's associated account is created if missing, paid for by the buyer); otherwise it is a lamport transfer. This is our fee leg, unrelated to LaunchLab's native share_fee_rate, which we always send as 0.
On a USDC-quoted pumpfun launch the fee is an SPL transfer in USDC (the fee wallet's associated account is created if missing, paid for by the buyer), exactly as on USD1-quoted bonk — the fee is always charged in the quote asset, never mis-scaled.
bags only. The fee template (Bags' MeteoraConfigType id). It decides the swap fee, the pre- vs post-migration split, how much compounds back into liquidity and, in some templates, how much of the supply is locked.
Omitted ⇒ ba28db46-ea6f-4452-8218-5587f6aca0a1 (DEFAULT_96_LOCKED), the value Bags' own SDK defaults to. We always send it downstream rather than leaving it out, so the template can never change under a caller because Bags changed a server-side default. Choosing one here overrides our pin, not theirs.
Rejected on pumpfun and bonk, which have no fee-share config — and rejected alongside feeShareConfigKey, where the template is already frozen in the on-chain config.
"ba28db46-ea6f-4452-8218-5587f6aca0a1"
bags only. Who earns the coin's trading fees besides the creator.
List only the other recipients — wallets[0] keeps the remainder, so the numbers you send are the ones you give away and you never restate your own share. Giving away all 10000 is allowed and simply leaves the creator out of the split.
Recipients are named by handle, not address: Bags resolves (provider, username) to that person's embedded wallet, so a collaborator can be paid before they have ever connected one. A handle that does not resolve is a 400 — never a silent fall back to the creator, which would quietly redirect someone else's fees.
Rejected on pumpfun and bonk — and rejected alongside feeShareConfigKey, because the split is already frozen in the on-chain config and there is no non-mutating way to prove it matches this list (check the response's feeClaimer instead). Errors, all 400 and all before any transaction is built: a total above 10000; more than 15 recipients once the creator's own slot is counted (past that Bags needs address lookup tables created in a separate transaction, which this endpoint does not do — refused rather than truncated); a repeated handle; a 0-bps entry; an unknown provider.
15bags only. The Meteora DBC PoolConfig a provisioning call created for this launch, already landed on chain — the launch then completes in one call. Always sent together with mintPubkey and token.metadataUri from the same provisioning response: one config per LAUNCH. Without mintPubkey it is rejected — Bags' claim attribution is per mint, and a config reused for a different mint makes that token's trading fees unclaimable (measured 2026-07-31).
This input moves money: the token's trading fees go to the fee_claimer frozen inside the config. The server validates the account (exists, owned by the DBC program, carries the PoolConfig discriminator, SOL-quoted) and returns the config's feeClaimer so you can verify the destination before signing — but it cannot prove the config pays any particular claimer list, which is why bagsFeeClaimers / bagsConfigType are rejected alongside it.
Not configAddress (a Meteora field of ours, still rejected on bags): this key names a config of Bags'.
Jito tip in SOL embedded by the server on the LAST buy transaction of the bundle, on both pumpfun and bonk. Omitting it means no tip — attach your own, which is what most integrators do. Note the exact casing: ...Sol, not ...SOL — an unrecognised key is silently ignored, which also lands you on no tip.
Priority fee in micro-lamports per compute unit, emitted as a SetComputeUnitPrice instruction. Defaults to 50000 on both platforms. There is no priorityFeeSol / priorityFee field — this is the only priority-fee knob, and an unrecognised key is dropped silently.
The compute budget costs 52 bytes (a program key plus the two instructions), and the widest bonk shapes land within that of the 1232-byte wire limit. Rather than reject a launch that would otherwise fit, a transaction gives things up in a fixed order, recoverable losses first:
- a rent-reclaim instruction — on a SOL launch the trailing
close_account, which leaves ~0.00204 SOL of rent (not funds) in your own wSOL account for you to close whenever you like; on a USD1 launch an idempotent account create that was a no-op anyway. 2. the default compute price. 3. the default compute limit, i.e. the whole budget.
Stranded rent is undone with one instruction at any time; a priority fee that was never attached cannot be recovered — hence that order. A bonk create with a long name, a USD1 fee leg and a tip measures 1196 bytes with the whole budget dropped (it needed all three rungs; keeping just the limit would have been 1236, four bytes over). It lands, with no priority fee, and the server logs the rung it settled on.
Nothing you asked for is ever on that list — not the fee leg, not the tip, not the swap, and not a computeUnitPrice / computeUnitLimit you sent explicitly. If an explicit value cannot fit, the request fails and says so rather than returning a transaction without the priority fee you were told you would pay. Want a fee on a shape that tight? Shorten token.name / token.symbol / the metadata URI, or drop a wallet — the error names those options.
Compute-unit cap. Sent explicitly, it applies to every transaction in the launch. Omitted, each transaction gets a per-shape default:
pumpfun — 400000 on the create / deploy-buy, 600000 on the buys.
bonk — 400000 on the create (+ fused first buy), 120000 on each subsequent buy. These are ≈1.8× the measured worst case (219000 CU for a create with a fused buy; ~70000 for a buy), not copied from pumpfun: Solana charges the priority fee on the units requested, not consumed, so an inflated limit costs real money.
base64, base58 Response
Response envelope. For pumpfun, transactions holds the partially-signed bundle legs (and stages when a lookup table has to be created first). Unimplemented platforms return success: false.
Shared envelope for every /utils/sol/* write endpoint. Optional
fields are omitted from the JSON when unset.
Serialized partially-signed transactions in submission order.
For batch endpoints (transfer, burn): packing metadata.
batches[i] describes transactions[i].
Set by /utils/sol/launch — the mint of the launched token.
Set by /utils/sol/launch and /utils/sol/fees/claim.
Set by /utils/sol/launch when the launch needs preparatory
transactions (today: creating an address lookup table for a
many-wallet bundle). Submit each stage, in order, as its own bundle
before transactions; a stage with waitForActivation: true
must also be given a slot to activate (~10 s for a new lookup table)
or the main bundle cannot resolve its addresses.
Set by /utils/sol/launch when a lookup table was created for the bundle — the address the stage transaction provisions.
Venue pool/curve address, returned by launches that derive one up front (bonk, bags). Absent on pumpfun, whose bonding curve is derivable from the mint alone.
Venue config account, when the venue keys its curve on one. Present on bags: the Meteora DBC PoolConfig the launch runs against. You need it to build any later swap of your own, it is not derivable from the mint — and on a provisioning response it is the feeShareConfigKey to pass on the follow-up call, together with the returned mint and metadataUri (the config belongs to that mint only — see feeShareConfigKey).
bags launches: where the config sends the token's trading fees — the fee_claimer frozen in the on-chain PoolConfig. Echoed for your records; you do not have to police it, since the server only lets you launch against the config provisioned for your own mint (see feeShareConfigKey).
bags provisioning responses: the reserved mint's metadata URI. Pass it back as token.metadataUri (with mintPubkey = the returned mint) on the follow-up launch call.
Set by /utils/sol/launch — true when the wallet count pushed the launch onto the multi-stage / lookup-table path.
Set by /utils/sol/fees/config for action=status reads.
Set when the request had simulate: true. One entry per built tx,
in the same order as transactions. Each entry is the RPC
simulateTransaction value object (logs / unitsConsumed / err).
