Skip to main content

Pump.fun Trading API: Client-Side Signing for Full Wallet Custody

The pumpdev.io Trading API lets you buy and sell pump.fun tokens programmatically with full wallet custody. Your private keys never leave your machine. The API generates unsigned transactions that you sign locally and broadcast through your own Solana RPC endpoint.

This is the foundation for building pump.fun trading bots, sniper bots, copy trading systems, and automated DeFi tools on Solana.

API Endpoint: POST https://pumpdev.io/api/trade-local


Why Use the pumpdev.io Trading API​

  • Non-custodial — Private keys stay on your server. The API never sees them.
  • Client-side signing — Sign transactions locally with any Solana wallet or keypair.
  • RPC flexibility — Broadcast through your own RPC for speed and reliability.
  • Full trade control — Buy with the pair's quote asset, sell by percentage, or sell exact token amounts.
  • Bot-ready — Combine with the WebSocket Data API for real-time automated trading.
  • Same shape on another chain — a bot written against this endpoint runs on Robinhood Chain by changing the URL; see the PONS Trading API.

Prerequisites​

  • Node.js 16+ (or any environment supporting ES modules)
  • @solana/web3.js and bs58 packages installed
  • A Solana wallet keypair (base58-encoded private key)
  • A Solana RPC endpoint (public or private)

Request Parameters​

ParameterTypeRequiredDescription
publicKeystringYesYour wallet public key
actionstringYes"buy" or "sell"
mintstringYesToken mint address
amountnumber/stringYesAmount to trade
denominatedInSolstringNoLegacy field name. Set "true" when a buy amount is denominated in the pair's quote asset: SOL for SOL pairs, USDC for USDC pairs. Keep "false" for sell percentages or exact token amounts
slippagenumberNoSlippage percentage (default: 90)
priorityFeenumberNoPriority fee in SOL (default: 0.00005)
computeUnitsnumberNoOverride compute unit limit. If omitted, PumpDev auto-selects a trade-specific limit
jitoTipnumberNoJito tip in SOL — only provide if sending via Jito (default: 0)
creatorstringNoCreator public key. Used only as a fallback when the bonding curve is not readable yet (a just-created mint). When PumpDev can read the curve, the on-chain creator wins — a stale value here would make pump.fun reject the transaction with a creator_vault seeds error
closeTokenAccountbooleanNoClose the token ATA after selling to reclaim ~0.002 SOL rent. Auto-enabled on "100%" sells. Set false to disable

Quote mint handling: You do not pass quoteMint to trade endpoints. PumpDev resolves it from the token on-chain and builds the correct V2 trade automatically. For SOL pairs, keep using native SOL. For USDC pairs, send the buy amount in USDC units with denominatedInSol: "true". Buying a non-SOL pair spends that token (USDC, PUMP, xStocks…) from your wallet, so hold enough of it first — otherwise the request fails with Insufficient quote token balance.

Amount Formats​

For buy transactions:

  • 0.1 with denominatedInSol: "true" = Buy with 0.1 SOL
  • 25 with denominatedInSol: "true" on a USDC-quoted token = Buy with 25 USDC

For sell transactions:

  • "100%" = Sell all tokens
  • "50%" = Sell half your tokens
  • 1000000 with denominatedInSol: "false" = Sell exact token amount

Response​

Returns a binary serialized unsigned VersionedTransaction. Deserialize it with VersionedTransaction.deserialize(), sign with your keypair, then send via any Solana RPC connection.


How to Buy Pump.fun Tokens​

Execute a buy order by spending the pair's quote asset to acquire tokens. For SOL pairs that quote asset is native SOL; for USDC pairs it is USDC.

import { VersionedTransaction, Connection, Keypair } from '@solana/web3.js';
import bs58 from 'bs58';

// Configuration
const API_URL = 'https://pumpdev.io';
const RPC_URL = 'https://api.mainnet-beta.solana.com';
const PRIVATE_KEY = 'YourBase58PrivateKey';

async function buyToken() {
const keypair = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY));
const publicKey = keypair.publicKey.toBase58();

// 1. Build buy transaction
const response = await fetch(`${API_URL}/api/trade-local`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
publicKey: publicKey,
action: 'buy',
mint: 'TokenMintAddress',
amount: 0.1, // Buy with 0.1 SOL
denominatedInSol: 'true',
slippage: 15 // 15% slippage tolerance
})
});

if (response.status !== 200) {
const error = await response.json();
console.error('API Error:', error);
return;
}

// 2. Deserialize the transaction
const data = await response.arrayBuffer();
const tx = VersionedTransaction.deserialize(new Uint8Array(data));

// 3. Sign with your wallet
tx.sign([keypair]);

// 4. Send to Solana
const connection = new Connection(RPC_URL, 'confirmed');

try {
const signature = await connection.sendTransaction(tx, {
skipPreflight: false,
maxRetries: 3
});

console.log('Transaction sent!');
console.log('Signature:', signature);

// Wait for confirmation
const confirmation = await connection.confirmTransaction(signature, 'confirmed');

if (confirmation.value.err) {
console.error('Transaction failed:', confirmation.value.err);
} else {
console.log('Transaction confirmed!');
}
} catch (err) {
console.error('Send error:', err.message);
}
}

buyToken();

Buy a USDC-Quoted Coin​

await fetch(`${API_URL}/api/trade-local`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
publicKey: publicKey,
action: 'buy',
mint: 'UsdcQuotedTokenMint',
amount: 25, // Buy with 25 USDC
denominatedInSol: 'true',
slippage: 15
})
});

How to Sell Pump.fun Tokens​

Convert your token holdings back to the pair's quote asset by selling tokens through the bonding curve or PumpSwap.

import { VersionedTransaction, Connection, Keypair } from '@solana/web3.js';
import bs58 from 'bs58';

const API_URL = 'https://pumpdev.io';
const RPC_URL = 'https://api.mainnet-beta.solana.com';
const PRIVATE_KEY = 'YourBase58PrivateKey';

async function sellToken() {
const keypair = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY));
const publicKey = keypair.publicKey.toBase58();

// 1. Build sell transaction
const response = await fetch(`${API_URL}/api/trade-local`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
publicKey: publicKey,
action: 'sell',
mint: 'TokenMintAddress',
amount: '100%', // Sell all tokens
denominatedInSol: 'false',
slippage: 15
})
});

if (response.status !== 200) {
const error = await response.json();
console.error('API Error:', error);
return;
}

// 2. Deserialize and sign
const data = await response.arrayBuffer();
const tx = VersionedTransaction.deserialize(new Uint8Array(data));
tx.sign([keypair]);

// 3. Send to Solana
const connection = new Connection(RPC_URL, 'confirmed');

try {
const signature = await connection.sendTransaction(tx, {
skipPreflight: false,
maxRetries: 3
});

console.log('Sell transaction sent!');
console.log('Signature:', signature);
} catch (err) {
console.error('Send error:', err.message);
}
}

sellToken();

Executing Partial Sells​

Sell a specific percentage or exact amount of your token holdings rather than liquidating your entire position.

// Sell 50% of tokens
const response = await fetch(`${API_URL}/api/trade-local`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
publicKey: publicKey,
action: 'sell',
mint: 'TokenMintAddress',
amount: '50%', // Sell half
denominatedInSol: 'false',
slippage: 15
})
});

// Or sell exact token amount
const response2 = await fetch(`${API_URL}/api/trade-local`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
publicKey: publicKey,
action: 'sell',
mint: 'TokenMintAddress',
amount: 1000000, // Sell 1,000,000 tokens
denominatedInSol: 'false',
slippage: 15
})
});

Building an Automated Pump.fun Trading Bot​

Combine our WebSocket data feeds with the Trading API to create a Solana sniper bot that detects new pump.fun token launches and executes trades based on configurable criteria — fully automated and non-custodial.

import WebSocket from 'ws';
import { VersionedTransaction, Connection, Keypair } from '@solana/web3.js';
import bs58 from 'bs58';

const API_URL = 'https://pumpdev.io';
const WS_URL = 'wss://pumpdev.io/ws';
const RPC_URL = 'https://api.mainnet-beta.solana.com';
const PRIVATE_KEY = 'YourBase58PrivateKey';

// Bot settings
const BUY_AMOUNT_SOL = 0.01;
const SLIPPAGE = 20;
const MAX_MARKET_CAP_SOL = 50;
const MIN_INITIAL_BUY_SOL = 0.5;

let keypair;
let publicKey;
let connection;
let isBuying = false;

async function main() {
keypair = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY));
publicKey = keypair.publicKey.toBase58();
connection = new Connection(RPC_URL, 'confirmed');

console.log('Sniper Bot Started');
console.log('Wallet:', publicKey);
console.log('Buy amount:', BUY_AMOUNT_SOL, 'SOL');

const ws = new WebSocket(WS_URL);

ws.on('open', () => {
console.log('Connected to WebSocket');

// Subscribe to new token creations
ws.send(JSON.stringify({ method: 'subscribeNewToken' }));
console.log('Watching for new tokens...');
});

ws.on('message', async (data) => {
const message = JSON.parse(data.toString());

if (message.txType === 'create') {
await handleNewToken(message);
}
});

ws.on('error', (err) => console.error('WebSocket error:', err.message));
}

async function handleNewToken(event) {
const { mint, name, symbol } = event;
const quoteAmount = event.quoteAmount ?? event.solAmount ?? 0;
const marketCap = event.marketCapQuote ?? event.marketCapSol ?? 0;
const quoteLabel =
event.quoteMint === 'So11111111111111111111111111111111111111112'
? 'SOL'
: event.quoteMint;

console.log(`\nNew Token: ${name} (${symbol})`);
console.log(` Mint: ${mint}`);
console.log(` Initial buy: ${quoteAmount} ${quoteLabel}`);
console.log(` Market cap: ${marketCap.toFixed(2)} ${quoteLabel}`);

// Check criteria
if (event.quoteMint !== 'So11111111111111111111111111111111111111112') {
console.log(' Skip: Non-SOL quote mint');
return;
}

if (marketCap > MAX_MARKET_CAP_SOL) {
console.log(' Skip: Market cap too high');
return;
}

if (quoteAmount < MIN_INITIAL_BUY_SOL) {
console.log(' Skip: Initial buy too low');
return;
}

if (isBuying) {
console.log(' Skip: Already buying');
return;
}

console.log(' Matches criteria! Buying...');
await buyToken(mint);
}

async function buyToken(mint) {
isBuying = true;

try {
// 1. Build transaction
const response = await fetch(`${API_URL}/api/trade-local`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
publicKey: publicKey,
action: 'buy',
mint: mint,
amount: BUY_AMOUNT_SOL,
denominatedInSol: 'true',
slippage: SLIPPAGE,
priorityFee: 0.001
})
});

if (response.status !== 200) {
const error = await response.json();
console.log(' API Error:', error.error);
return;
}

// 2. Sign and send
const data = await response.arrayBuffer();
const tx = VersionedTransaction.deserialize(new Uint8Array(data));
tx.sign([keypair]);

const signature = await connection.sendTransaction(tx, {
skipPreflight: true,
maxRetries: 2
});

console.log(' Transaction sent:', signature);
} catch (err) {
console.log(' Error:', err.message);
} finally {
isBuying = false;
}
}

main();

Error Handling & Troubleshooting​

The API returns structured JSON error responses with descriptive messages to help diagnose issues:

{
"error": "publicKey is required"
}

Common API Errors​

Error MessageRoot CauseSolution
Missing required fields: publicKey, action, mint, amountOne or more required fields missingInclude all required fields in the request body
action must be 'buy' or 'sell'Invalid action parameter valueUse exactly "buy" or "sell"
Invalid publicKey or mint addressMalformed Solana address formatVerify address format and encoding
Token bonding curve not foundToken doesn't exist on Pump.funVerify the mint address
PumpSwap pool not found. Use Jupiter or Raydium for this token.Graduated token without a PumpSwap poolUse Jupiter or Raydium instead
No token balance foundWallet doesn't hold tokens for sellEnsure you own tokens for this mint
Invalid percentage valueSell percentage is invalid (must be 1-100)Use a valid percentage like "50%" or "100%"
Calculated sell amount is zeroToken balance too small for the given percentageUse "100%" or a larger percentage

Frequently Asked Questions​

Do I need to share my private key with the API?​

No. The Trading API only receives your public key. It returns an unsigned transaction that you sign locally before broadcasting. Your private key never leaves your environment.

What is the difference between the Trading API and Lightning Trade?​

The Trading API requires you to sign and send the transaction yourself (client-side). Lightning Trade handles signing and broadcasting for you using a pre-configured Lightning wallet — ideal for simpler setups or rapid prototyping.

What slippage should I use for a sniper bot?​

For new token launches with high volatility, use 15-25% slippage. For established tokens with deeper liquidity, 5-10% is typically sufficient.

Can I use a custom RPC endpoint?​

Yes. Pass any Solana-compatible RPC URL to new Connection(RPC_URL). Using a private RPC (e.g., Helius, QuickNode, Triton) is recommended for sniper bots to reduce latency and avoid rate limits.

Why is my first request slower than the rest?​

The first HTTPS request from a fresh process pays DNS + TCP + TLS handshake costs (a few hundred ms) before the API even sees it — server processing time is the same for every request. For latency-sensitive bots, warm up the connection at startup with one or two GET /api/health calls, and send a periodic keep-alive ping (e.g., every 30s) while idle so trades reuse the warm connection instead of re-handshaking.


Next Steps​