Launch Types

Launching a Bonding Curve via the Metaplex API

Last updated April 9, 2026

Use the Genesis SDK and the Metaplex API to create, sign, send, and register a bonding curve token launch on Solana.

What You'll Build

This guide covers:

  • Launching a bonding curve token in a single call with createAndRegisterLaunch
  • Adding creator fees — to a specific wallet or to an agent PDA automatically
  • Configuring a fee-free first buy at launch
  • Signing and registering the launch manually with createLaunch + registerLaunch
  • Testing on devnet and using a custom API base URL or transaction sender
  • Handling typed SDK errors

Summary

createAndRegisterLaunch (or its lower-level equivalents) calls POST /v1/launches/create, returns unsigned Solana transactions, signs and sends them, then registers the launch so the token appears on metaplex.com.

  • One-liner pathcreateAndRegisterLaunch handles the full flow in a single awaited call
  • Manual pathcreateLaunch + signAndSendLaunchTransactions + registerLaunch for custom signing, bundles, or retry logic
  • Creator fees — optional per-swap fee earned on the bonding curve and in the post-graduation Raydium pool; configurable per-wallet or derived automatically for agent launches
  • First buy — optional fee-free initial purchase reserved for the launching wallet or agent PDA at curve creation

Quick Start

Jump to: Installation · Setup · One-Liner Launch · Creator Fees · First Buy · Manual Signing · Token Metadata · Devnet · Advanced · Errors · API Reference

  1. Install the Genesis SDK and configure a Umi instance with your keypair identity
  2. Call createAndRegisterLaunch with your token metadata and a launch: {} object
  3. Read result.mintAddress and result.launch.link from the response

For custom signing or retry logic, use Manual Signing Flow instead.

Prerequisites

  • Node.js 18+ — required for native BigInt support
  • A Solana wallet keypair funded with SOL for transaction fees and the optional first buy amount
  • A Solana RPC endpoint (mainnet-beta or devnet)
  • An image pre-uploaded to Irys — the token metadata image field must be an Irys gateway URL

Installation

Install the three required packages.

Terminal
npm install @metaplex-foundation/genesis \
@metaplex-foundation/umi \
@metaplex-foundation/umi-bundle-defaults

Umi Setup

Configure a Umi instance with your keypair identity before calling any Genesis API function.

setup.ts
1import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
2import { keypairIdentity } from '@metaplex-foundation/umi';
3
4const umi = createUmi('https://api.mainnet-beta.solana.com');
5
6// Load your keypair — use your preferred key management solution in production.
7const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes);
8umi.use(keypairIdentity(keypair));

The Genesis API functions do not require the genesis() plugin — they talk to the hosted Metaplex API over HTTP rather than submitting instructions directly. The Umi instance is used only for its signer identity and transaction-sending capability.

Launching a Bonding Curve (One-Liner Flow)

createAndRegisterLaunch is the simplest path — it creates the launch, signs and sends all transactions, and registers the token on metaplex.com in one awaited call.

1import { createAndRegisterLaunch } from '@metaplex-foundation/genesis/api';
2import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
3import { keypairIdentity } from '@metaplex-foundation/umi';
4
5const umi = createUmi('https://api.mainnet-beta.solana.com');
6const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes);
7umi.use(keypairIdentity(keypair));
8
9const result = await createAndRegisterLaunch(umi, {}, {
10 wallet: umi.identity.publicKey,
11 launchType: 'bondingCurve',
12 token: {
13 name: 'My Token',
14 symbol: 'MTK',
15 image: 'https://gateway.irys.xyz/your-image-id',
16 },
17 launch: {},
18});
19
20console.log('Token launched!');
21console.log('Mint address:', result.mintAddress);
22console.log('View at:', result.launch.link);
23
24// Token launched!
25// Mint address: <base58 mint address>
26// View at: https://www.metaplex.com/...

All protocol parameters — supply splits, virtual reserves, fund flows, and lock schedules — are set to protocol defaults when launch: {} is empty. The sections below show how to add creator fees and a first buy.

Creator Fees

An optional per-swap fee accrues to a configured wallet on every buy and sell. Set creatorFeeWallet in the launch object to redirect fees to a specific address; the launching wallet is used by default.

launch-with-creator-fee.ts
launch: {
creatorFeeWallet: 'FeeRecipientWalletAddress...',
},

For full configuration options, how to check the accrued balance, and the claiming instructions (claimBondingCurveCreatorFeeV2 / claimRaydiumCreatorFeeV2), see Creator Fees.

First Buy

The first buy reserves the initial swap on the curve for the launching wallet at a specified SOL amount, with all fees waived.

Set firstBuyAmount to the SOL amount for the fee-free initial purchase.

1import { createAndRegisterLaunch } from '@metaplex-foundation/genesis/api';
2import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
3import { keypairIdentity } from '@metaplex-foundation/umi';
4
5const umi = createUmi('https://api.mainnet-beta.solana.com');
6const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes);
7umi.use(keypairIdentity(keypair));
8
9const result = await createAndRegisterLaunch(umi, {}, {
10 wallet: umi.identity.publicKey,
11 launchType: 'bondingCurve',
12 token: {
13 name: 'My Token',
14 symbol: 'MTK',
15 image: 'https://gateway.irys.xyz/your-image-id',
16 },
17 launch: {
18 firstBuyAmount: 0.1, // 0.1 SOL, fee-free initial purchase
19 },
20});

The API executes the first buy as part of the launch transaction flow — the curve already has the initial purchase applied once the transactions confirm. The buyer defaults to the launching wallet, or to the agent PDA when agent is provided. Override with firstBuyWallet (a Signer) to designate a different buyer.

When firstBuyAmount is omitted or 0, no first buy restriction is applied and any wallet can make the first swap.

You can combine a first buy with a creator fee wallet:

launch-combined.ts
launch: {
creatorFeeWallet: 'FeeRecipientWalletAddress...',
firstBuyAmount: 0.5,
},

Manual Signing Flow

Use createLaunch and registerLaunch separately when you need control over how transactions are signed and submitted — for example, when using Jito bundles, priority fees, or custom retry logic.

manual-launch.ts
1import {
2 createLaunch,
3 registerLaunch,
4 signAndSendLaunchTransactions,
5} from '@metaplex-foundation/genesis/api';
6
7// Step 1: Call the API to get unsigned transactions.
8const createResult = await createLaunch(umi, {}, {
9 wallet: umi.identity.publicKey,
10 launchType: 'bondingCurve',
11 token: {
12 name: 'My Token',
13 symbol: 'MTK',
14 image: 'https://gateway.irys.xyz/your-image-id',
15 },
16 launch: {
17 creatorFeeWallet: 'FeeRecipientWalletAddress...',
18 },
19});
20
21console.log('Mint address:', createResult.mintAddress);
22console.log('Transactions to sign:', createResult.transactions.length);
23
24// Step 2: Sign and send the transactions.
25const signatures = await signAndSendLaunchTransactions(umi, createResult);
26
27// Step 3: Register the launch after all transactions are confirmed onchain.
28const registered = await registerLaunch(umi, {}, {
29 genesisAccount: createResult.genesisAccount,
30 createLaunchInput: {
31 wallet: umi.identity.publicKey,
32 launchType: 'bondingCurve',
33 token: {
34 name: 'My Token',
35 symbol: 'MTK',
36 image: 'https://gateway.irys.xyz/your-image-id',
37 },
38 launch: {
39 creatorFeeWallet: 'FeeRecipientWalletAddress...',
40 },
41 },
42});
43
44console.log('Launch live at:', registered.launch.link);

Call registerLaunch only after the create transactions are confirmed onchain. The API verifies that the genesis account exists before registering — calling it too early will return an API error.

Token Metadata

Every launch requires a token object with the following fields.

FieldRequiredConstraints
nameYes1–32 characters
symbolYes1–10 characters
imageYesMust be an Irys URL (https://gateway.irys.xyz/...)
descriptionNoMax 250 characters
externalLinksNoOptional website, twitter, and telegram values
token-metadata.ts
token: {
name: 'My Token',
symbol: 'MTK',
image: 'https://gateway.irys.xyz/your-image-id',
description: 'A token launched on the bonding curve',
externalLinks: {
website: 'https://mytoken.com',
twitter: '@mytoken',
telegram: '@mytoken',
},
},

Devnet Testing

Pass network: 'solana-devnet' and point the Umi instance at the devnet RPC endpoint to route the launch through devnet infrastructure. For the CLI, the network is determined by your configured RPC endpoint.

1import { createAndRegisterLaunch } from '@metaplex-foundation/genesis/api';
2import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
3import { keypairIdentity } from '@metaplex-foundation/umi';
4
5const umi = createUmi('https://api.devnet.solana.com');
6const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes);
7umi.use(keypairIdentity(keypair));
8
9const result = await createAndRegisterLaunch(umi, {}, {
10 wallet: umi.identity.publicKey,
11 launchType: 'bondingCurve',
12 network: 'solana-devnet',
13 token: {
14 name: 'Test Token',
15 symbol: 'TEST',
16 image: 'https://gateway.irys.xyz/test-image',
17 },
18 launch: {},
19});

Advanced

Custom API Base URL

The SDK defaults to https://api.metaplex.com. Pass baseUrl in the config object (the second argument) to target a different environment such as a staging API.

custom-base-url.ts
1const API_CONFIG = { baseUrl: 'https://your-api-base-url.example.com' };
2
3const result = await createAndRegisterLaunch(umi, API_CONFIG, {
4 wallet: umi.identity.publicKey,
5 launchType: 'bondingCurve',
6 token: {
7 name: 'My Token',
8 symbol: 'MTK',
9 image: 'https://gateway.irys.xyz/your-image-id',
10 },
11 launch: {},
12});

The same API_CONFIG object is accepted by createLaunch and registerLaunch in the manual signing flow.

Custom Transaction Sender

Pass a txSender callback in the options (fourth argument) to use your own signing and submission infrastructure.

custom-sender.ts
1const result = await createAndRegisterLaunch(
2 umi,
3 {},
4 {
5 wallet: umi.identity.publicKey,
6 launchType: 'bondingCurve',
7 token: {
8 name: 'My Token',
9 symbol: 'MTK',
10 image: 'https://gateway.irys.xyz/your-image-id',
11 },
12 launch: {},
13 },
14 {
15 txSender: async (txs) => {
16 const signatures = [];
17 for (const tx of txs) {
18 const signed = await umi.identity.signTransaction(tx);
19 signatures.push(await myCustomSend(signed));
20 }
21 return signatures;
22 },
23 }
24);

Common Errors

ErrorType checkCauseFix
Validation error on "token.image"isGenesisValidationErrorImage URL is not an Irys gateway URLUpload the image to Irys and use the https://gateway.irys.xyz/... URL
Validation error on "token.name"isGenesisValidationErrorName exceeds 32 characters or is emptyShorten the token name to 1–32 characters
Network errorisGenesisApiNetworkErrorCannot reach https://api.metaplex.comCheck connectivity or supply a baseUrl pointing to an accessible endpoint
API error (4xx)isGenesisApiErrorInvalid input rejected by the APIRead err.responseBody for the field-level error detail
API error (5xx)isGenesisApiErrorMetaplex API is unavailableRetry with exponential back-off; do not re-send already-confirmed transactions
registerLaunch API errorisGenesisApiErrorRegistered before create transactions confirmedWait for all signatures to confirm onchain before calling registerLaunch

Use the typed error guards to distinguish these cases:

error-handling.ts
1import {
2 createLaunch,
3 isGenesisApiError,
4 isGenesisApiNetworkError,
5 isGenesisValidationError,
6} from '@metaplex-foundation/genesis/api';
7
8try {
9 const result = await createLaunch(umi, {}, input);
10} catch (err) {
11 if (isGenesisValidationError(err)) {
12 console.error(`Validation error on "${err.field}": ${err.message}`);
13 } else if (isGenesisApiNetworkError(err)) {
14 console.error('Network error:', err.message);
15 } else if (isGenesisApiError(err)) {
16 console.error(`API error (${err.statusCode}): ${err.message}`);
17 console.error('Details:', err.responseBody);
18 } else {
19 throw err;
20 }
21}

Notes

  • createAndRegisterLaunch is atomic from the caller's perspective but internally makes two API calls — a failure after the create transactions confirm but before registerLaunch means the token exists onchain but is not yet visible on metaplex.com; call registerLaunch manually to complete registration
  • The Metaplex API endpoint (https://api.metaplex.com) is hosted infrastructure — it constructs and returns unsigned transactions; the caller always holds and controls signing
  • Virtual reserves, supply splits, and lock schedules are set by protocol defaults when launch: {} is empty; there is no API to override these per-launch
  • The agent.setToken flag is irreversible — once a token is set as an agent's primary token it cannot be changed or reassigned; see Create an Agent Token for the full agent launch flow
  • Once a curve is live, integrate swaps using the Bonding Curve Swap Integration guide
  • First buy is configured at launch creation and cannot be added after the curve is live; firstBuyAmount: 0 or omitting the field disables it entirely
  • Creator fees are accrued in the bucket, not transferred per-swap; claim via the permissionless claimBondingCurveCreatorFeeV2 (bonding curve) and claimRaydiumCreatorFeeV2 (post-graduation Raydium) instructions

API Reference

createAndRegisterLaunch(umi, config, input, options?)

Convenience function that orchestrates the full launch flow: create, sign, send, and register.

ParameterTypeDescription
umiUmiUmi instance with identity and RPC configured
configGenesisApiConfig | nullOptional API config (baseUrl, custom fetch)
inputCreateBondingCurveLaunchInputLaunch configuration
optionsSignAndSendOptionsOptional txSender override
registerOptionsRegisterOptionsOptional fields forwarded to registerLaunch (e.g. creatorWallet, twitterVerificationToken)

Returns Promise<CreateAndRegisterLaunchResult>:

FieldDescription
signaturesTransaction signatures
mintAddressCreated token mint address
genesisAccountGenesis account PDA
launch.linkURL to view the token on metaplex.com

createLaunch(umi, config, input)

Calls POST /v1/launches/create and returns deserialized transactions.

Returns Promise<CreateLaunchResponse>:

FieldDescription
transactionsArray of Umi Transaction objects to sign and send
blockhashBlockhash for transaction validity
mintAddressCreated token mint address
genesisAccountGenesis account PDA

registerLaunch(umi, config, input)

Registers a confirmed genesis account on metaplex.com. Call after all create transactions are confirmed onchain.

Returns Promise<RegisterLaunchResponse>:

FieldDescription
launch.idLaunch identifier
launch.linkURL to view the token
token.mintAddressConfirmed mint address

Types

types.ts
interface CreateBondingCurveLaunchInput {
wallet: PublicKey | string;
launchType: 'bondingCurve';
token: TokenMetadata;
network?: 'solana-mainnet' | 'solana-devnet';
quoteMint?: 'SOL';
agent?: {
mint: PublicKey | string; // Core asset (NFT) address
setToken: boolean; // set launched token as the agent's primary token
};
launch: BondingCurveLaunchInput;
}
interface BondingCurveLaunchInput {
creatorFeeWallet?: PublicKey | string;
firstBuyAmount?: number; // SOL amount (e.g. 0.1 = 0.1 SOL)
firstBuyWallet?: Signer;
}
interface TokenMetadata {
name: string; // max 32 characters
symbol: string; // max 10 characters
image: string; // must be an Irys URL: https://gateway.irys.xyz/...
description?: string; // max 250 characters
externalLinks?: {
website?: string;
twitter?: string;
telegram?: string;
};
}
interface GenesisApiConfig {
baseUrl?: string;
fetch?: typeof fetch;
}

FAQ

What is the difference between createAndRegisterLaunch and calling createLaunch then registerLaunch separately?

createAndRegisterLaunch is a convenience wrapper that handles the full flow in a single call. Use the lower-level functions separately when you need custom signing logic (e.g. Jito bundles, priority fees) or when you want to inspect or modify the unsigned transactions before submission. See Manual Signing Flow.

Can I test a bonding curve launch on devnet before going to mainnet?

Yes. Pass network: 'solana-devnet' in the input and point your Umi instance at https://api.devnet.solana.com. The API routes the request to devnet infrastructure. Make sure the wallet is funded with devnet SOL before sending transactions. See Devnet Testing.

What happens if I set agent.setToken: true by mistake?

Setting setToken: true permanently associates the launched token with the agent as its primary token — this is irreversible and cannot be undone or reassigned. If you are unsure, omit the agent field or set setToken: false and handle token association separately.

Can I combine a creator fee wallet with a first buy?

Yes. Set both creatorFeeWallet and firstBuyAmount in the launch object. The first buy itself is fee-free — no protocol fee or creator fee is charged on that initial purchase. Creator fees apply normally to all subsequent swaps. See First Buy.

What image format and hosting does the token metadata require?

The image field must be an Irys URL — https://gateway.irys.xyz/<id>. Upload your image to Irys first and use the returned gateway URL. Other hosts will fail API validation. The SDK surfaces this as an isGenesisValidationError on the token.image field.

Why must registerLaunch be called after the transactions confirm onchain?

registerLaunch writes the launch record to Metaplex's database and verifies the genesis account exists onchain before registering. Calling it before the create transactions confirm will return an API error because the account is not yet visible. In createAndRegisterLaunch, this sequencing is handled automatically.