Migrate Pump.fun
Want to buy or sell first the moment a token migrates? On Pump.fun you can initiate the migration yourself β and act before anyone else.
How It Worksβ
Migration can only be initialized during a short window when tokensInPool == 0.
After a few seconds, the Pump.fun keeper will initialize the migration automatically β but you can do it first. No admin access required. Anyone can call it.
Just call the migrate action and you'll trigger the migration. This action is fail-safe, meaning your transaction will not fail even if someone executes it before you. You can even test it on already migrated tokens.
Buy or Sell on Migrationβ
If you also want to trade at migration time, you have two options:
- Jito Bundles (recommended) β send
migrateand the trade as two separate transactions inside one atomic bundle. Simpler, faster, and no CPI workarounds needed. - Actions β pack
migrate+ trade into one transaction. Requires a wSOL workaround due to Solana's CPI limit.
Pick a tab below.
- Jito Bundles (recommended)
- Actions (legacy)
Jito Bundles Approachβ
With Jito Bundles, migrate and the trade live in separate transactions inside one atomic bundle. Both land together or neither lands β and because each transaction has its own CPI budget, you don't need any wSOL tricks.
The flow is dead simple:
- tx1 β
migrate(initiates the migration) - tx2 β
buyorsell(executes immediately after migration)
Example Requestβ
{
"jitoTip": 0.00001,
"transactions": [
{
"privateKey": "base58_private_key",
"action": "migrate",
"mint": "token_address",
"quoteMint": "quote_token_address", // usually wsol address, but it can also be usdc
},
{
"privateKey": "base58_private_key",
"action": "buy",
"mint": "token_address",
"quoteMint": "quote_token_address", // usually wsol address, but it can also be usdc
"amount": 0.2,
"denominatedInQuote": true,
"slippage": 200
}
]
}
That's it. No wrapSol, no disableSolWrapper, no CPI math.
Code Exampleβ
The example below listens to the stream and fires a Jito Bundle (migrate + buy) the moment tokensInPool == 0.
- Python
- JavaScript
- Rust
- Go
import websockets
import orjson as json # or use the standard json module (orjson is faster)
import asyncio
import aiohttp
url = "https://api.pumpapi.io"
async def pumpapi_data_stream():
async with websockets.connect("wss://stream.pumpapi.io/") as websocket:
async for message in websocket:
event = json.loads(message)
if event.get("pool") == "pump" and event.get("tokensInPool") == 0:
print(event)
data = {
"jitoTip": 0.00001,
"transactions": [
{
"privateKey": "base58_private_key",
"action": "migrate",
"mint": event["mint"],
"quoteMint": event["quoteMint"],
},
{
"privateKey": "base58_private_key",
"action": "buy", # or "sell"
"mint": event["mint"],
"quoteMint": event["quoteMint"],
"amount": 0.2,
"denominatedInQuote": True,
"slippage": 200,
},
],
}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=data) as response:
text = await response.text()
print(text)
asyncio.run(pumpapi_data_stream())
import WebSocket from 'ws';
import axios from 'axios';
const url = "https://api.pumpapi.io";
async function pumpApiDataStream() {
const ws = new WebSocket("wss://stream.pumpapi.io/");
ws.on("message", async (message) => {
const event = JSON.parse(message);
if (event.pool === "pump" && event.tokensInPool === 0) {
console.log(event);
const data = {
jitoTip: 0.00001,
transactions: [
{
privateKey: "base58_private_key",
action: "migrate",
mint: event.mint,
quoteMint: event.quoteMint,
},
{
privateKey: "base58_private_key",
action: "buy", // or "sell"
mint: event.mint,
quoteMint: event.quoteMint,
amount: 0.2,
denominatedInQuote: true,
slippage: 200,
},
],
};
const response = await axios.post(url, data);
console.log(response.data);
}
});
ws.on("error", (err) => console.error("WebSocket error:", err));
}
pumpApiDataStream();
use futures_util::StreamExt;
use reqwest::Client;
use serde_json::{json, Value};
use tokio_tungstenite::connect_async;
use tungstenite::Message;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let url = "https://api.pumpapi.io";
let client = Client::new();
let (ws_stream, _) = connect_async("wss://stream.pumpapi.io/").await?;
let (_, mut read) = ws_stream.split();
while let Some(msg) = read.next().await {
let msg = msg?;
if let Message::Text(text) = msg {
let event: Value = serde_json::from_str(&text)?;
if event["pool"] == "pump" && event["tokensInPool"] == 0 {
println!("{}", event);
let res = client
.post(url)
.json(&json!({
"jitoTip": 0.00001,
"transactions": [
{
"privateKey": "base58_private_key",
"action": "migrate",
"mint": event["mint"],
"quoteMint": event["quoteMint"],
},
{
"privateKey": "base58_private_key",
"action": "buy", // or "sell"
"mint": event["mint"],
"quoteMint": event["quoteMint"],
"amount": 0.2,
"denominatedInQuote": true,
"slippage": 200
}
]
}))
.send()
.await?
.text()
.await?;
println!("{}", res);
}
}
}
Ok(())
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"github.com/gorilla/websocket"
)
const apiURL = "https://api.pumpapi.io"
func postJSON(data map[string]interface{}) {
body, _ := json.Marshal(data)
resp, err := http.Post(apiURL, "application/json", bytes.NewBuffer(body))
if err != nil {
log.Println("HTTP error:", err)
return
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
fmt.Println(string(respBody))
}
func main() {
conn, _, err := websocket.DefaultDialer.Dial("wss://stream.pumpapi.io/", nil)
if err != nil {
log.Fatal("WebSocket error:", err)
}
defer conn.Close()
for {
_, message, err := conn.ReadMessage()
if err != nil {
log.Println("Read error:", err)
break
}
var event map[string]interface{}
if err := json.Unmarshal(message, &event); err != nil {
continue
}
pool, _ := event["pool"].(string)
tokensInPool, _ := event["tokensInPool"].(float64)
if pool == "pump" && tokensInPool == 0 {
fmt.Println(event)
postJSON(map[string]interface{}{
"jitoTip": 0.00001,
"transactions": []map[string]interface{}{
{
"privateKey": "base58_private_key",
"action": "migrate",
"mint": event["mint"],
"quoteMint": event["quoteMint"],
},
{
"privateKey": "base58_private_key",
"action": "buy", // or "sell"
"mint": event["mint"],
"quoteMint": event["quoteMint"],
"amount": 0.2,
"denominatedInQuote": true,
"slippage": 200,
},
},
})
}
}
}
Actions Approachβ
This method is currently not working. Please use the Jito Bundles version. (left tab)
With Actions, migrate and the trade are packed into one transaction. This is more compact, but it runs into Solana's CPI limit and requires a wSOL workaround.
Solana has a CPI limit of 49. Migration with a trade is a complex operation, so you need to optimize your request carefully.
You cannot use regular SOL for the trade β wrapping SOL inline takes too many CPI calls. You must use Wrapped SOL (wSOL) instead.
Step 1 β Wrap your SOL (separate transaction)β
Call wrapSol in advance to convert SOL into wSOL:
data = {
"privateKey": "base58_private_key",
"action": "wrapSol",
"amount": "0.5", # wrap any amount β it's safe, not a trade
"priorityFee": "0.00002001",
}
You can wrap a large amount β it's not a trade, just an internal Solana instruction. When you later sell 100% of wSOL, the account closes and all funds return to SOL automatically.
Step 2 β Set disableSolWrapper: "true"β
Always pass "disableSolWrapper": "true" in your migrate request. Without it, the transaction will fail with an Unknown program error.
If you only want to initiate migration without trading, you don't need to wrap SOL or set disableSolWrapper. Just call migrate directly.
Example Requestβ
{
"privateKey": "base58_private_key",
"action": "migrate",
"initialTradeAction": "buy",
"disableSolWrapper": "true",
"mint": "token_address",
"quoteMint": "quote_token_address", // usually wsol address, but it can also be usdc
"amount": "0.01",
"denominatedInQuote": "true",
"slippage": "200",
"priorityFee": "0.00019"
}
Code Exampleβ
The example below wraps 0.5 SOL, then listens to the stream and triggers migration + buy the moment tokensInPool == 0.
- Python
- JavaScript
- Rust
- Go
import websockets
import orjson as json # or use the standard json module (orjson is faster)
import asyncio
import aiohttp
url = "https://api.pumpapi.io"
async def pumpapi_data_stream():
async with websockets.connect("wss://stream.pumpapi.io/") as websocket:
# Step 1 β wrap SOL before migration
data = {
"privateKey": "base58_private_key",
"action": "wrapSol",
"amount": "0.5", # safe to wrap any amount
"priorityFee": "0.00002001",
}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=data) as response:
text = await response.json()
print(f"Wrapped SOL -> {text}")
# Step 2 β wait for migration window and act first
async for message in websocket:
event = json.loads(message)
if event.get("pool") == "pump" and event.get("tokensInPool") == 0:
print(event)
data = {
"privateKey": "base58_private_key",
"action": "migrate",
"initialTradeAction": "buy", # remove this line to migrate only
"disableSolWrapper": True,
"mint": event["mint"],
"quoteMint": event["quoteMint"],
"amount": "0.2",
"denominatedInQuote": "true",
"slippage": "200",
"priorityFee": "0.00002001",
}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=data) as response:
text = await response.text()
print(text)
asyncio.run(pumpapi_data_stream())
import WebSocket from 'ws';
import axios from 'axios';
const url = "https://api.pumpapi.io";
async function pumpApiDataStream() {
// Step 1 β wrap SOL before migration
const wrapData = {
privateKey: "base58_private_key",
action: "wrapSol",
amount: "0.5",
priorityFee: "0.00002001",
};
const wrapResponse = await axios.post(url, wrapData);
console.log("Wrapped SOL ->", wrapResponse.data);
// Step 2 β wait for migration window and act first
const ws = new WebSocket("wss://stream.pumpapi.io/");
ws.on("message", async (message) => {
const event = JSON.parse(message);
if (event.pool === "pump" && event.tokensInPool === 0) {
console.log(event);
const migrateData = {
privateKey: "base58_private_key",
action: "migrate",
initialTradeAction: "buy", // remove this line to migrate only
disableSolWrapper: true,
mint: event.mint,
quoteMint: event.quoteMint,
amount: "0.2",
denominatedInQuote: "true",
slippage: "200",
priorityFee: "0.00002001",
};
const response = await axios.post(url, migrateData);
console.log(response.data);
}
});
ws.on("error", (err) => console.error("WebSocket error:", err));
}
pumpApiDataStream();
use futures_util::StreamExt;
use reqwest::Client;
use serde_json::{json, Value};
use tokio_tungstenite::connect_async;
use tungstenite::Message;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let url = "https://api.pumpapi.io";
let client = Client::new();
// Step 1 β wrap SOL before migration
let wrap_res = client
.post(url)
.json(&json!({
"privateKey": "base58_private_key",
"action": "wrapSol",
"amount": "0.5",
"priorityFee": "0.00002001"
}))
.send()
.await?
.text()
.await?;
println!("Wrapped SOL -> {}", wrap_res);
// Step 2 β wait for migration window and act first
let (ws_stream, _) = connect_async("wss://stream.pumpapi.io/").await?;
let (_, mut read) = ws_stream.split();
while let Some(msg) = read.next().await {
let msg = msg?;
if let Message::Text(text) = msg {
let event: Value = serde_json::from_str(&text)?;
if event["pool"] == "pump" && event["tokensInPool"] == 0 {
println!("{}", event);
let res = client
.post(url)
.json(&json!({
"privateKey": "base58_private_key",
"action": "migrate",
"initialTradeAction": "buy", // remove this line to migrate only
"disableSolWrapper": true,
"mint": event["mint"],
"quoteMint": event["quoteMint"],
"amount": "0.2",
"denominatedInQuote": "true",
"slippage": "200",
"priorityFee": "0.00002001"
}))
.send()
.await?
.text()
.await?;
println!("{}", res);
}
}
}
Ok(())
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"github.com/gorilla/websocket"
)
const apiURL = "https://api.pumpapi.io"
func postJSON(data map[string]interface{}) {
body, _ := json.Marshal(data)
resp, err := http.Post(apiURL, "application/json", bytes.NewBuffer(body))
if err != nil {
log.Println("HTTP error:", err)
return
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
fmt.Println(string(respBody))
}
func main() {
// Step 1 β wrap SOL before migration
fmt.Println("Wrapping SOL...")
postJSON(map[string]interface{}{
"privateKey": "base58_private_key",
"action": "wrapSol",
"amount": "0.5",
"priorityFee": "0.00002001",
})
// Step 2 β wait for migration window and act first
conn, _, err := websocket.DefaultDialer.Dial("wss://stream.pumpapi.io/", nil)
if err != nil {
log.Fatal("WebSocket error:", err)
}
defer conn.Close()
for {
_, message, err := conn.ReadMessage()
if err != nil {
log.Println("Read error:", err)
break
}
var event map[string]interface{}
if err := json.Unmarshal(message, &event); err != nil {
continue
}
pool, _ := event["pool"].(string)
tokensInPool, _ := event["tokensInPool"].(float64)
if pool == "pump" && tokensInPool == 0 {
fmt.Println(event)
postJSON(map[string]interface{}{
"privateKey": "base58_private_key",
"action": "migrate",
"initialTradeAction": "buy", // remove this line to migrate only
"disableSolWrapper": true,
"mint": event["mint"],
"quoteMint": event["quoteMint"],
"amount": "0.2",
"denominatedInQuote": "true",
"slippage": "200",
"priorityFee": "0.00002001",
})
}
}
}
Endpointβ
POST https://api.pumpapi.io
Local Transactionsβ
Local transactions are supported for both approaches above, but will be slower. Use the same logic as on the Trade API page.
Need help? Join our Telegram group.