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, add "initialTradeAction": "buy" or "sell" to your request. All other fields (amount, slippage, etc.) work exactly like the Trade API.
You can use Actions to buy just enough tokens to push tokensInPool to 0, then trigger the migration as the second action — all in one transaction. In this case, you can't use initialTradeAction - see why below.
CPI Limit — Important
Solana has a CPI limit of 49. Migration with a trade is a complex transaction, so you need to optimize your request carefully.
You cannot use regular SOL for the trade — it 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.
Endpoint
POST https://api.pumpapi.io
Request Fields (migrate + trade)
{
"privateKey": "base58_private_key",
"action": "migrate",
"initialTradeAction": "buy",
"disableSolWrapper": "true",
"mint": "token_address",
"amount": "0.01",
"denominatedInQuote": "true",
"slippage": "90",
"priorityFee": "0.00019"
}
Local Transactions
Local transactions are supported too, but will be slower. Use the same logic as on the Trade API page.
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"],
"amount": "0.2",
"denominatedInQuote": "true",
"slippage": "20",
"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,
amount: "0.2",
denominatedInQuote: "true",
slippage: "20",
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"],
"amount": "0.2",
"denominatedInQuote": "true",
"slippage": "20",
"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"],
"amount": "0.2",
"denominatedInQuote": "true",
"slippage": "20",
"priorityFee": "0.00002001",
})
}
}
}
Need help? Join our Telegram group.