SDK

API Client

Last updated April 23, 2026

The Genesis API client provides high-level functions for creating and registering token launches. It handles transaction building, signing, and on-chain registration through a simple interface built on Umi.

We recommend using the SDK to create launches programmatically, as metaplex.com does not yet support the full feature set of the Genesis program. Mainnet launches created through the API will appear on metaplex.com once registered.

Installation

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

Setup

import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
import { genesis } from '@metaplex-foundation/genesis';
import { keypairIdentity } from '@metaplex-foundation/umi';
const umi = createUmi('https://api.mainnet-beta.solana.com')
.use(genesis());
// For server-side or scripts, load a keypair
umi.use(keypairIdentity(myKeypair));

Function Overview

FunctionPurpose
createAndRegisterLaunchOne-call token launch — create, sign, send, and register
createLaunchBuild unsigned launch transactions for custom signing flows
registerLaunchRegister a launch after its create transactions confirm on-chain
claimCreatorRewardsClaim accrued creator rewards across every bucket for a wallet

Three Integration Modes

The SDK offers three modes for creating launches, from fully automatic to fully manual.

Easy Mode — createAndRegisterLaunch

The simplest approach. One function call handles everything: creates the on-chain accounts, signs and sends transactions via Umi, and registers the launch.

createAndRegisterLaunch.ts
1import {
2 createAndRegisterLaunch,
3 CreateLaunchInput,
4 genesis,
5} from '@metaplex-foundation/genesis'
6import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
7import { keypairIdentity } from '@metaplex-foundation/umi'
8
9const umi = createUmi('https://api.mainnet-beta.solana.com')
10 .use(genesis())
11
12// Use keypairIdentity to set a wallet when running server-side:
13// umi.use(keypairIdentity(myKeypair))
14
15const input: CreateLaunchInput = {
16 wallet: umi.identity.publicKey,
17 token: {
18 name: 'My Token',
19 symbol: 'MTK',
20 image: 'https://gateway.irys.xyz/...',
21 },
22 launchType: 'launchpool',
23 launch: {
24 launchpool: {
25 tokenAllocation: 500_000_000,
26 depositStartTime: new Date(Date.now() + 48 * 60 * 60 * 1000),
27 raiseGoal: 250,
28 raydiumLiquidityBps: 5000,
29 fundsRecipient: umi.identity.publicKey,
30 },
31 },
32}
33
34const result = await createAndRegisterLaunch(umi, {}, input)
35console.log(`Launch live at: ${result.launch.link}`)

Returns CreateAndRegisterLaunchResult:

FieldTypeDescription
signaturesUint8Array[]Transaction signatures
mintAddressstringCreated token mint address
genesisAccountstringGenesis account PDA address
launch.idstringLaunch ID
launch.linkstringLaunch page URL
token.idstringToken ID
token.mintAddressstringToken mint address

Medium Mode — Custom Transaction Sender

Use createAndRegisterLaunch with a custom txSender callback for scenarios like multisig wallets or custom retry logic.

customTxSender.ts
1import {
2 createAndRegisterLaunch,
3 SignAndSendOptions,
4} from '@metaplex-foundation/genesis'
5
6// Assumes umi and input from the Easy Mode example.
7
8const options: SignAndSendOptions = {
9 txSender: async (transactions) => {
10 // Replace myCustomSign / myCustomSend with your own
11 // signing and sending implementation.
12 const signatures: Uint8Array[] = []
13 for (const tx of transactions) {
14 const signed = await myCustomSign(tx) // your signer
15 const sig = await myCustomSend(signed) // your sender
16 signatures.push(sig)
17 }
18 return signatures
19 },
20}
21
22const result = await createAndRegisterLaunch(umi, {}, input, options)
23console.log(`Launch live at: ${result.launch.link}`)

The txSender callback receives the array of unsigned transactions and must return an array of signatures. The SDK handles registration after the callback completes.

Full Control — createLaunch + registerLaunch

For complete control over the transaction lifecycle. You call createLaunch to get unsigned transactions, handle signing and sending yourself, then call registerLaunch.

fullControl.ts
1import {
2 createLaunch,
3 registerLaunch,
4} from '@metaplex-foundation/genesis'
5
6// Assumes umi and input from the Easy Mode example.
7
8// Step 1: Get unsigned transactions from the API
9const createResult = await createLaunch(umi, {}, input)
10
11// Step 2: Sign and send each transaction
12for (const tx of createResult.transactions) {
13 const signedTx = await umi.identity.signTransaction(tx)
14 const signature = await umi.rpc.sendTransaction(signedTx, {
15 commitment: 'confirmed',
16 preflightCommitment: 'confirmed',
17 })
18 await umi.rpc.confirmTransaction(signature, {
19 commitment: 'confirmed',
20 strategy: {
21 type: 'blockhash',
22 ...createResult.blockhash,
23 },
24 })
25}
26
27// Step 3: Register the launch
28const registerResult = await registerLaunch(umi, {}, {
29 genesisAccount: createResult.genesisAccount,
30 createLaunchInput: input,
31})
32console.log(`Launch live at: ${registerResult.launch.link}`)

createLaunch returns CreateLaunchResponse:

FieldTypeDescription
transactionsTransaction[]Unsigned Umi transactions to sign and send
blockhashBlockhashWithExpiryBlockHeightBlockhash for confirming transactions
mintAddressstringCreated token mint address
genesisAccountstringGenesis account PDA address

registerLaunch returns RegisterLaunchResponse:

FieldTypeDescription
existingboolean?true if launch was already registered
launch.idstringLaunch ID
launch.linkstringLaunch page URL
token.idstringToken ID
token.mintAddressstringToken mint address

Transactions must be confirmed on-chain before calling registerLaunch. The register endpoint validates that the genesis account exists and matches the expected configuration.


Claim Creator Rewards

claimCreatorRewards calls POST /v1/creator-rewards/claim and returns the base64-encoded claim transactions for every eligible bonding-curve and Raydium bucket, already deserialized into Umi Transactions. Sign and send each one using the Umi identity. For the end-to-end claim flow — including the creator fee configuration, accrual checks, and the lower-level per-bucket instructions — see Creator Fees on the Genesis Bonding Curve.

claimCreatorRewards.ts
1import { claimCreatorRewards } from '@metaplex-foundation/genesis'
2import { base58 } from '@metaplex-foundation/umi/serializers'
3import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
4import { keypairIdentity } from '@metaplex-foundation/umi'
5
6const umi = createUmi('https://api.mainnet-beta.solana.com')
7const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes)
8umi.use(keypairIdentity(keypair))
9
10const result = await claimCreatorRewards(umi, {}, {
11 wallet: umi.identity.publicKey,
12 network: 'solana-mainnet',
13 // payer is optional — defaults to `wallet` on the server.
14 // Set it to have a different wallet cover rent and transaction fees.
15 // payer: umi.identity.publicKey,
16})
17
18for (const tx of result.transactions) {
19 const signed = await umi.identity.signTransaction(tx)
20 const signature = await umi.rpc.sendTransaction(signed, {
21 preflightCommitment: 'confirmed',
22 })
23 await umi.rpc.confirmTransaction(signature, {
24 strategy: { type: 'blockhash', ...result.blockhash },
25 commitment: 'confirmed',
26 })
27 console.log('Claimed:', base58.deserialize(signature)[0])
28}
29
30// Claimed: 5uGGYEMmjP2HpyFCvLPNpVDSQEBtUE3LR6ZQFqhJxQSh5FbKacSyN8nQmAJowuFs6BTCdwzoFyyJz8Y2hQx8kPxo
31// Claimed: 3TAroVovEap1ZEAJYq3WiDZoMK3GU3soCdrhvZJNg6b9EANqvWrVcDGNffm7mD8wvtpR7ynWQBcbrmz8AK6nrhfy

ClaimCreatorRewardsInput

FieldTypeRequiredDescription
walletPublicKey | stringYesThe creator fee wallet to claim for.
networkSvmNetworkNo'solana-mainnet' (default) or 'solana-devnet'. Must match the API base URL — https://api.metaplex.com
payerPublicKey | stringNoWallet that covers fees and rent on the returned transactions. Defaults to wallet. Use this when the creator fee wallet does not hold SOL (for example, an agent PDA). The payer must sign the returned transactions; the creator fee wallet still receives the claimed SOL.

ClaimCreatorRewardsResponse

FieldTypeDescription
transactionsTransaction[]Deserialized Umi transactions, one per bucket being claimed. Sign each with the payer identity and send.
blockhashBlockhashWithExpiryBlockHeightThe blockhash the transactions were built against. Use it to confirm — do not substitute a freshly-fetched blockhash.

No-rewards is a typed error, not an empty array

When the wallet has nothing to claim, the API returns HTTP 400 with {"error":{"message":"No rewards available to claim"}} and the SDK throws GenesisApiError. Callers must catch the error and branch on err.message (or err.statusCode === 400) — they cannot rely on result.transactions.length === 0. See Handling the No-Rewards Case.

errorHandling.ts
1import {
2 claimCreatorRewards,
3 isGenesisApiError,
4 isGenesisApiNetworkError,
5} from '@metaplex-foundation/genesis'
6
7// Assumes umi is configured with a keypair identity.
8
9try {
10 const result = await claimCreatorRewards(umi, {}, {
11 wallet: umi.identity.publicKey,
12 network: 'solana-mainnet',
13 })
14 console.log(`Claimable transactions: ${result.transactions.length}`)
15} catch (err) {
16 if (isGenesisApiError(err)) {
17 // The API returns HTTP 400 with
18 // { "error": { "message": "No rewards available to claim" } }
19 // when the wallet has no unclaimed creator rewards. Match on the message
20 // (or statusCode === 400) to handle this as a success case rather than a
21 // failure.
22 if (err.message === 'No rewards available to claim') {
23 console.log('Nothing to claim right now.')
24 } else {
25 console.error('API error:', err.statusCode, err.message)
26 }
27 } else if (isGenesisApiNetworkError(err)) {
28 console.error('Network error:', err.cause.message)
29 } else {
30 throw err
31 }
32}
33
34// Nothing to claim right now.

Configuration

CreateLaunchInput

FieldTypeRequiredDescription
walletPublicKey | stringYesCreator's wallet (signs transactions)
tokenTokenMetadataYesToken metadata
networkSvmNetworkNo'solana-mainnet' (default) or 'solana-devnet'
quoteMintQuoteMintInputNo'SOL' (default) or 'USDC'
launchTypeCreateLaunchTypeYes'launchpool' — the underlying launch mechanism
launchLaunchpoolLaunchInputYesLaunch configuration

TokenMetadata

FieldTypeRequiredDescription
namestringYesToken name, 1–32 characters
symbolstringYesToken symbol, 1–10 characters
imagestringYesImage URL (valid HTTPS URL)
descriptionstringNoMax 250 characters
externalLinksExternalLinksNoWebsite, Twitter, Telegram links
FieldTypeDescription
websitestring?Website URL
twitterstring?Twitter/X handle (@mytoken) or full URL
telegramstring?Telegram handle or full URL

LaunchpoolConfig

FieldTypeDescription
tokenAllocationnumberTokens to sell (portion of 1B total supply)
depositStartTimeDate | stringWhen the deposit period opens (lasts 48 hours)
raiseGoalnumberMinimum quote tokens to raise, in whole units (e.g. 250 SOL). Minimum 250 SOL or 5,000 USDC
raydiumLiquidityBpsnumber% of raised funds for Raydium LP, in basis points (2000–10000)
fundsRecipientPublicKey | stringReceives the unlocked portion of raised funds

LockedAllocation (Streamflow Lockup)

Optional locked token schedules can be added via launch.lockedAllocations:

FieldTypeDescription
namestringStream name, max 64 characters (e.g. "Team", "Advisors")
recipientPublicKey | stringLockup recipient wallet
tokenAmountnumberTotal tokens in the locked schedule
vestingStartTimeDate | stringWhen the unlock schedule begins
vestingDuration{ value: number, unit: TimeUnit }Full lockup period
unlockScheduleTimeUnitHow frequently tokens are released
cliffobject?Optional cliff with duration and unlockAmount

The vestingStartTime must be after the deposit period ends (i.e., after depositStartTime + 48 hours). The API will reject locked schedules that start before the deposit window closes.

TimeUnit values: 'SECOND', 'MINUTE', 'HOUR', 'DAY', 'WEEK', 'TWO_WEEKS', 'MONTH', 'QUARTER', 'YEAR'

Example with locked allocations:

lockedAllocations.ts
1import {
2 createAndRegisterLaunch,
3 CreateLaunchInput,
4 genesis,
5} from '@metaplex-foundation/genesis'
6import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
7import { keypairIdentity } from '@metaplex-foundation/umi'
8
9const umi = createUmi('https://api.mainnet-beta.solana.com')
10 .use(genesis())
11
12// Use keypairIdentity to set a wallet when running server-side:
13// umi.use(keypairIdentity(myKeypair))
14
15const input: CreateLaunchInput = {
16 wallet: umi.identity.publicKey,
17 token: {
18 name: 'My Token',
19 symbol: 'MTK',
20 image: 'https://gateway.irys.xyz/...',
21 description: 'A project token with locked allocations.',
22 externalLinks: {
23 website: 'https://example.com',
24 twitter: '@mytoken',
25 },
26 },
27 launchType: 'launchpool',
28 launch: {
29 launchpool: {
30 tokenAllocation: 500_000_000,
31 depositStartTime: new Date('2026-04-01T00:00:00Z'),
32 raiseGoal: 250,
33 raydiumLiquidityBps: 5000,
34 fundsRecipient: 'FundsRecipientWallet...',
35 },
36 lockedAllocations: [
37 {
38 name: 'Team',
39 recipient: 'TeamWallet...',
40 tokenAmount: 100_000_000,
41 vestingStartTime: new Date('2026-04-05T00:00:00Z'),
42 vestingDuration: { value: 1, unit: 'YEAR' },
43 unlockSchedule: 'MONTH',
44 cliff: {
45 duration: { value: 3, unit: 'MONTH' },
46 unlockAmount: 10_000_000,
47 },
48 },
49 ],
50 },
51}
52
53const result = await createAndRegisterLaunch(umi, {}, input)
54console.log(`Launch live at: ${result.launch.link}`)

SignAndSendOptions

Options for createAndRegisterLaunch (extends RpcSendTransactionOptions):

FieldTypeDefaultDescription
txSender(txs: Transaction[]) => Promise<Uint8Array[]>Custom transaction sender callback
commitmentstring'confirmed'Commitment level for confirmation
preflightCommitmentstring'confirmed'Preflight commitment level
skipPreflightbooleanfalseSkip preflight checks

Error Handling

The SDK provides three error types with type guard functions.

GenesisApiError

Thrown when the API returns a non-success response.

import { isGenesisApiError } from '@metaplex-foundation/genesis';
try {
await createLaunch(umi, {}, input);
} catch (err) {
if (isGenesisApiError(err)) {
console.error('API error:', err.statusCode, err.responseBody);
}
}
PropertyTypeDescription
statusCodenumberHTTP status code
responseBodyunknownFull response body from the API

GenesisApiNetworkError

Thrown when the fetch call fails (network issue, DNS failure, etc.).

import { isGenesisApiNetworkError } from '@metaplex-foundation/genesis';
if (isGenesisApiNetworkError(err)) {
console.error('Network error:', err.cause.message);
}
PropertyTypeDescription
causeErrorThe underlying fetch error

GenesisValidationError

Thrown when input validation fails before making an API call.

import { isGenesisValidationError } from '@metaplex-foundation/genesis';
if (isGenesisValidationError(err)) {
console.error(`Validation failed on field "${err.field}":`, err.message);
}
PropertyTypeDescription
fieldstringThe input field that failed validation

Comprehensive Error Handling

errorHandling.ts
1import {
2 createAndRegisterLaunch,
3 isGenesisApiError,
4 isGenesisApiNetworkError,
5 isGenesisValidationError,
6} from '@metaplex-foundation/genesis'
7
8// Assumes umi and input from the Easy Mode example.
9
10try {
11 const result = await createAndRegisterLaunch(umi, {}, input)
12 console.log(`Launch live at: ${result.launch.link}`)
13} catch (err) {
14 if (isGenesisValidationError(err)) {
15 console.error(`Invalid input "${err.field}":`, err.message)
16 } else if (isGenesisApiError(err)) {
17 console.error('API error:', err.statusCode, err.responseBody)
18 } else if (isGenesisApiNetworkError(err)) {
19 console.error('Network error:', err.cause.message)
20 } else {
21 throw err
22 }
23}

Validation Rules

The SDK validates inputs before sending them to the API:

RuleConstraint
Token name1–32 characters
Token symbol1–10 characters
Token imageValid HTTPS URL
Token descriptionMax 250 characters
Token allocationGreater than 0
Raise goalGreater than 0
Raydium liquidity BPS2000–10000 (20%–100%)
Total supplyFixed at 1 billion tokens
Locked allocation nameMax 64 characters

The total supply is always 1 billion tokens. The SDK automatically calculates the creator allocation as the remainder after subtracting the launchpool, Raydium LP, and any locked allocations.


Helper Functions

signAndSendLaunchTransactions

If you want the default sign-and-send behavior as a standalone function (useful for retries or partial flows):

import {
createLaunch,
signAndSendLaunchTransactions,
} from '@metaplex-foundation/genesis';
const createResult = await createLaunch(umi, {}, input);
const signatures = await signAndSendLaunchTransactions(umi, createResult, {
commitment: 'confirmed',
});

Transactions are signed and sent sequentially — each is confirmed before the next is sent.

buildCreateLaunchPayload

Validates input and builds the raw API payload. Exported for advanced use cases:

import { buildCreateLaunchPayload } from '@metaplex-foundation/genesis';
const payload = buildCreateLaunchPayload(input);
// Use payload with your own HTTP client