# PumpApi Documentation > The fastest & simplest API for Pump.fun, Raydium and Meteora & Solana and token transfers ## Actions ### Actions **Actions** let you combine multiple operations — like `buy`, `sell`, `create`, `transfer`, `claimCashback`, `burn`, and more — into a **single transaction**. Great for increasing token volume, making multiple sales to clean up an account, and other strategies. #### Endpoint[​](#endpoint "Direct link to Endpoint") `POST https://api.pumpapi.io` #### Basic Structure[​](#basic-structure "Direct link to Basic Structure") ```json { "actions": [ { "privateKey": "base58_private_key", "action": "buy", "mint": "token_address", "amount": 0.1, "denominatedInQuote": True, "slippage": 99, "priorityFee": 0.00002 }, { "privateKey": "base58_private_key", "action": "sell", "mint": "token_address", "amount": "100%", "denominatedInQuote": True, "slippage": 99, "priorityFee": 0.00002 } ] } ``` Each object in `actions` follows the same fields as the [Trade API](/trade-api.md). *** #### Priority Fee[​](#priority-fee "Direct link to Priority Fee") `priorityFee` **accumulates across actions** — because each action adds complexity to the transaction. A transaction with two swaps is more complex than with one, so it requires a higher fee to land reliably. ##### Global Priority Fee[​](#global-priority-fee "Direct link to Global Priority Fee") If you want a single `priorityFee` for the whole transaction, set it **above** the `actions` block. Per-action `priorityFee` values will be ignored. ```json { "priorityFee": 0.00002, "actions": [ { "action": "buy", ... }, { "action": "sell", ... } ] } ``` *** #### Multiple Wallets[​](#multiple-wallets "Direct link to Multiple Wallets") You can use **different wallets** for different actions. Just set `privateKey` inside each action. You can also change the **transaction signer** (the wallet that pays the fee) by setting `privateKey` at the top level: ```json { "privateKey": "base58_fee_payer_key", "actions": [ { "privateKey": "wallet_a_key", "action": "buy", ... }, { "privateKey": "wallet_b_key", "action": "sell", ... } ] } ``` *** #### Solana Transaction Limits[​](#solana-transaction-limits "Direct link to Solana Transaction Limits") warning Solana has a **1232-byte limit** per transaction. Transaction size grows when actions involve **different AMMs**, because each AMM requires loading additional accounts. | Scenario | How many swaps fit | | ---------------------------- | ------------------ | | Same AMM (e.g. all Pump.fun) | ~4–5 swaps | | Mixed AMMs | ~2 | There's also a **CPI limit of 49**. A typical swap uses ~10 CPIs, so you'll hit this limit around 4–5 swaps even on a single AMM like Pump.fun. *** #### Actions vs. Jito Bundles[​](#actions-vs-jito-bundles "Direct link to Actions vs. Jito Bundles") | | Actions | Jito Bundles | | ------------ | --------------------------------------------------- | ------------------------------------------------ | | What it does | Combines multiple operations into **1 transaction** | Combines multiple transactions into **1 bundle** | | Use case | Multi-step logic in one atomic tx | Atomic ordering of separate txs | *** #### Local Transactions[​](#local-transactions "Direct link to Local Transactions") Actions also support **local (unsigned) transactions** — just use `publicKey` instead of `privateKey`, and sign + send the transaction yourself. See the [Trade API](/trade-api.md) page for the local logic. *** #### Code Examples[​](#code-examples "Direct link to Code Examples") * Python * JavaScript * Rust * Go ```python import requests url = "https://api.pumpapi.io" data = { "actions": [ { "privateKey": "base58_private_key", "action": "buy", "mint": "token_address", "amount": 0.1, "denominatedInQuote": True, "slippage": 99, "priorityFee": 0.00002, }, { "privateKey": "base58_private_key", "action": "sell", "mint": "token_address", "amount": "100%", "denominatedInQuote": True, "slippage": 99, "priorityFee": 0.00002, } ] } response = requests.post(url, json=data) print(response.json()) ``` ```javascript import axios from 'axios'; const data = { actions: [ { privateKey: "base58_private_key", action: "buy", mint: "token_address", amount: 0.1, denominatedInQuote: true, slippage: 99, priorityFee: 0.00002, }, { privateKey: "base58_private_key", action: "sell", mint: "token_address", amount: "100%", denominatedInQuote: true, slippage: 99, priorityFee: 0.00002, } ] }; axios.post("https://api.pumpapi.io", data) .then(response => console.log(response.data)) .catch(error => console.error(error)); ``` ```rust use reqwest::Client; use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let res = client .post("https://api.pumpapi.io") .json(&json!({ "actions": [ { "privateKey": "base58_private_key", "action": "buy", "mint": "token_address", "amount": 0.1, "denominatedInQuote": true, "slippage": 99, "priorityFee": 0.00002 }, { "privateKey": "base58_private_key", "action": "sell", "mint": "token_address", "amount": "100%", "denominatedInQuote": true, "slippage": 99, "priorityFee": 0.00002 } ] })) .send() .await? .text() .await?; println!("{}", res); Ok(()) } ``` ```go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "actions": []map[string]interface{}{ { "privateKey": "base58_private_key", "action": "buy", "mint": "token_address", "amount": 0.1, "denominatedInQuote": true, "slippage": 99, "priorityFee": 0.00002, }, { "privateKey": "base58_private_key", "action": "sell", "mint": "token_address", "amount": "100%", "denominatedInQuote": true, "slippage": 99, "priorityFee": 0.00002, }, }, } jsonData, _ := json.Marshal(data) resp, err := http.Post("https://api.pumpapi.io", "application/json", bytes.NewBuffer(jsonData)) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Println(result) } ``` *** Need help? Join our [Telegram group](https://t.me/pumpapi_devs). --- ## Burn ### 🔥 Burn Tokens – API Use this endpoint to **permanently remove tokens from circulation**.
Burning is **irreversible** — double‑check your parameters before sending the call. #### Endpoint[​](#endpoint "Direct link to Endpoint") `POST https://api.pumpapi.io` #### Request Body[​](#request-body "Direct link to Request Body") | Field | Description | | ------------- | -------------------------------------------------------------------------- | | `privateKey` | Wallet that owns the tokens | | `action` | Must be **"burn"** | | `mint` | Mint address of the token | | `mintRef` | Temporary token reference for [token creations](/create-token-pump-fun.md) | | `amount` | Amount to burn. `'100%'` burns all available balance of the token | | `priorityFee` | Optional extra fee in SOL to speed up the transaction | The response returns the burn transaction signature once confirmed. #### 📦 Code Examples[​](#-code-examples "Direct link to 📦 Code Examples") * Python * JavaScript * Rust * Go ```python import requests url = "http://api.pumpapi.io" data = { "privateKey": "private_key", "action": "burn", "mint": "mint_address", "amount": "100%", # burn all tokens "priorityFee": 0.000001 } response = requests.post(url, json=data) print(response.json()) # {'signature': '...'} ``` ```javascript import axios from 'axios'; const data = { privateKey: "private_key", action: "burn", mint: "mint_address", amount: "100%", // burn all tokens priorityFee: 0.000001 }; axios.post('http://api.pumpapi.io', data) .then(res => console.log(res.data)) // { signature: '...' } .catch(err => console.error(err)); ``` ```rust use reqwest::Client; use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let res = client .post("http://api.pumpapi.io") .json(&json!({ "privateKey": "private_key", "action": "burn", "mint": "mint_address", "amount": "100%", "priorityFee": 0.000001, })) .send() .await? .text() .await?; println!("{}", res); // {"signature":"..."} Ok(()) } ``` ```go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "privateKey": "private_key", "action": "burn", "mint": "mint_address", "amount": "100%", // burn all tokens "priorityFee": 0.000001, } jsonData, _ := json.Marshal(data) resp, err := http.Post("http://api.pumpapi.io", "application/json", bytes.NewBuffer(jsonData)) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Println(result) // map[signature:...] } ``` --- ## Claim Cashback ### Claim Cashback – pump.fun API Use this endpoint to **claim accumulated cashback from trades and creator fees** from **pump.fun** and **PumpSwap**. We claim cashback from trading in pools where `cashbackEnabled = True`, as well as **creator fees** from trades on tokens you created with `cashbackToTradersEnabled = False` (the default, no need to pass it when creating a token). ✅ If you are planning to discard your account and switch to a new one — do not forget to call this function first.
It will return several rent fees (~0.004 SOL) to your wallet, even if you have not traded in pools with cashback enabled or created any tokens. You can verify the refund on Solscan in the "Balance Changes" tab to confirm that the return was successfully processed. ✅ You **do not** need to provide a specific pool name — we do everything for you. > **Local transactions are supported**
For a local-transaction example, visit the **Trade API page** and reuse the same local-transaction code snippets — the flow works the same way here. #### Endpoint[​](#endpoint "Direct link to Endpoint") `POST https://api.pumpapi.io` #### Request Body[​](#request-body "Direct link to Request Body") | Field | Description | | ------------- | -------------------------------------------------------------- | | `privateKey` | Private key of the wallet that will sign the claim transaction | | `action` | Must be **"claimCashback"** | | `priorityFee` | Priority fee in SOL. | #### 📦 Code Examples[​](#-code-examples "Direct link to 📦 Code Examples") * Python * JavaScript * Rust * Go ```python import requests url = "https://api.pumpapi.io" data = { "privateKey": "private_key", "action": "claimCashback", "priorityFee": "0.0000012", } response = requests.post(url, json=data) print(response.json()) ``` ```javascript import axios from "axios"; const url = "https://api.pumpapi.io"; const data = { privateKey: "private_key", action: "claimCashback", priorityFee: "0.0000012", }; axios .post(url, data) .then((res) => console.log(res.data)) .catch((err) => console.error(err)); ``` ```rust use reqwest::Client; use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let res = client .post("https://api.pumpapi.io") .json(&json!({ "privateKey": "private_key", "action": "claimCashback", "priorityFee": "0.0000012" })) .send() .await? .text() .await?; println!("{}", res); Ok(()) } ``` ```go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { url := "https://api.pumpapi.io" data := map[string]any{ "privateKey": "private_key", "action": "claimCashback", "priorityFee": "0.0000012", } jsonData, _ := json.Marshal(data) resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) if err != nil { panic(err) } defer resp.Body.Close() var result any _ = json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%v\n", result) } ``` --- ## Create Token Pump Fun ### Create Token **Create tokens on Pump.fun** directly through our API — in a single request. This is an extension of the [Trade API](/trade-api.md): everything that works there works here too. #### Endpoint[​](#endpoint "Direct link to Endpoint") `POST https://api.pumpapi.io` #### Parameters[​](#parameters "Direct link to Parameters") | Parameter | Required | Description | | -------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | Yes | Token name | | `symbol` | Yes | Token ticker symbol | | `imageURL` | No | Token image URL. Without it, Pump.fun won't display your token — only bots will be able to trade it. You can host images on [prnt.sc](https://prnt.sc), [imgur.com](https://imgur.com), or [imgbb.com](https://imgbb.com), or simply copy an image URL from Google Images. | | `description` | No | Token description | | `website` | No | Project website URL | | `telegram` | No | Telegram channel/group link | | `x` | No | X (Twitter) profile link | | `uri` | No | Custom metadata URI. If provided, `imageURL`, `description`, `website`, `telegram`, and `x` are ignored | | `mayhemMode` | No | Default: `false` | | `cashbackToTradersEnabled` | No | Default: `false`. If `true`, trading fees are earned by traders, not you | | `mintRef` | No | When using Jito Bundles or Actions, you don’t yet know the token address assigned to you (unless you provide mintPrivateKey). To handle this, within a single request you can set "mintRef": "any value" (the default is "0") and reuse it across related transactions inside the same Jito Bundle or Actions. When buying the token, specify "mintRef": "the\_value\_you\_set\_earlier", and the backend will understand which token you’re referring to. Works within a single request; a second request requires providing the mint address. | | `mintPrivateKey` | No | Custom mint address key. By default we generate tokens with the `pump` ending — you don't need to set this | | `amount` | No | Amount of SOL to buy as dev on launch | *** #### Basic Example[​](#basic-example "Direct link to Basic Example") * Python * JavaScript * Rust * Go ```python import requests url = "https://api.pumpapi.io" data = { "privateKey": "base58_private_key", "action": "create", "name": "PumpApi", "symbol": "PAPI", "description": "Fast API for Pump.fun, Raydium, Meteora", "imageURL": "https://pumpapi.io/img/pumpapi_logo.webp", "website": "https://pumpapi.io", "telegram": "https://t.me/YOUR_TG", "x": "https://x.com/realpumpapi", "amount": "0.0001", "denominatedInQuote": "true", "priorityFee": "0.00002001", } response = requests.post(url, json=data) print(response.json()) ``` ```javascript import axios from 'axios'; const data = { privateKey: "base58_private_key", action: "create", name: "PumpApi", symbol: "PAPI", description: "Fast API for Pump.fun, Raydium, Meteora", imageURL: "https://pumpapi.io/img/pumpapi_logo.webp", website: "https://pumpapi.io", telegram: "https://t.me/YOUR_TG", x: "https://x.com/realpumpapi", amount: "0.0001", denominatedInQuote: "true", priorityFee: "0.00002001", }; axios.post("https://api.pumpapi.io", data) .then(response => console.log(response.data)) .catch(error => console.error(error)); ``` ```rust use reqwest::Client; use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let res = client .post("https://api.pumpapi.io") .json(&json!({ "privateKey": "base58_private_key", "action": "create", "name": "PumpApi", "symbol": "PAPI", "description": "Fast API for Pump.fun, Raydium, Meteora", "imageURL": "https://pumpapi.io/img/pumpapi_logo.webp", "website": "https://pumpapi.io", "telegram": "https://t.me/YOUR_TG", "x": "https://x.com/realpumpapi", "amount": "0.0001", "denominatedInQuote": "true", "priorityFee": "0.00002001" })) .send() .await? .text() .await?; println!("{}", res); Ok(()) } ``` ```go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "privateKey": "base58_private_key", "action": "create", "name": "PumpApi", "symbol": "PAPI", "description": "Fast API for Pump.fun, Raydium, Meteora", "imageURL": "https://pumpapi.io/img/pumpapi_logo.webp", "website": "https://pumpapi.io", "telegram": "https://t.me/YOUR_TG", "x": "https://x.com/realpumpapi", "amount": "0.0001", "denominatedInQuote": "true", "priorityFee": "0.00002001", } jsonData, _ := json.Marshal(data) resp, err := http.Post("https://api.pumpapi.io", "application/json", bytes.NewBuffer(jsonData)) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Println(result) } ``` *** #### Multi-Wallet Launch[​](#multi-wallet-launch "Direct link to Multi-Wallet Launch") You can launch your token and buy from multiple wallets in a **single request**, using either [Jito Bundles](/jito-bundles.md) or [Actions](/actions.md). Both are supported — pick whichever fits your strategy: * **Jito Bundles** — each buy is a **separate transaction** inside an atomic bundle. To outside observers the buys look unrelated, but no one can sandwich between them. Supports up to **5 transactions** per bundle. * **Actions** — all buys are packed into **one transaction**. Slightly cheaper, but on-chain everyone can see all the buys are connected. Limited to ~4–5 swaps per tx on Pump.fun due to Solana's tx size limit. In both cases, use `"mintRef": "0"` on the `create` action to set a temporary token reference, then reuse the same `mintRef` on every `buy` so the backend knows which mint you're referring to. * Jito Bundles (recommended) * Actions ```json { "jitoTip": 0.00001, "transactions": [ { "privateKey": "dev_wallet_key", "action": "create", "name": "PumpApi", "symbol": "PAPI", "imageURL": "https://pumpapi.io/img/pumpapi_logo.webp", "mintRef": "0", "initialTradeAction": "buy", "amount": 0.5, # dev buy "denominatedInQuote": true, "slippage": 99 }, { "privateKey": "wallet_b_key", "action": "buy", "mintRef": "0", "amount": 0.3, "denominatedInQuote": true, "slippage": 99 }, { "privateKey": "wallet_c_key", "action": "buy", "mintRef": "0", "amount": 0.3, "denominatedInQuote": true, "slippage": 99 }, { "privateKey": "wallet_d_key", "action": "buy", "mintRef": "0", "amount": 0.3, "denominatedInQuote": true, "slippage": 99 }, { "privateKey": "wallet_e_key", "action": "buy", "mintRef": "0", "amount": 0.3, "denominatedInQuote": true, "slippage": 99 } ] } ``` See the [Jito Bundles](/jito-bundles.md) page for full details. ```json { "privateKey": "base58_fee_payer_key", "actions": [ { "privateKey": "dev_wallet_key", "action": "create", "name": "PumpApi", "symbol": "PAPI", "imageURL": "https://pumpapi.io/img/pumpapi_logo.webp", "mintRef": "0", "amount": 0.1, "denominatedInQuote": true, "priorityFee": 0.00002 }, { "privateKey": "wallet_b_key", "action": "buy", "mintRef": "0", "amount": 0.9, "denominatedInQuote": true, "slippage": 99, "priorityFee": 0.00002 }, { "privateKey": "wallet_c_key", "action": "buy", "mintRef": "0", "amount": 0.9, "denominatedInQuote": true, "slippage": 99, "priorityFee": 0.00002 } ] } ``` You can also change the `txSigner` (the wallet that pays the fee) by setting `privateKey` at the top level — useful to avoid copy-traders. See the [Actions](/actions.md) page for full details. *** #### Local Transactions[​](#local-transactions "Direct link to Local Transactions") Local (unsigned) transactions are supported too — just send `publicKey` instead of `privateKey` to receive an unsigned transaction, sign it on your side, and broadcast it yourself. See the [Trade API](/trade-api.md) page for local transaction code examples. *** #### Cashback[​](#cashback "Direct link to Cashback") If you launched with `cashbackToTradersEnabled: false` (the default one, no need to pass it), creator trading fees can be withdrawn using the [claimCashback](/claim-cashback.md) method. *** #### Custom Metadata URI[​](#custom-metadata-uri "Direct link to Custom Metadata URI") For experts This step is **entirely optional**. By default, we host your token metadata on our server so you can create a token with a single request. If you want to use the standard Pump.fun method, you can upload your icon and metadata to IPFS yourself, then pass the resulting URI as `"uri"` when creating the token. When `uri` is set, the `imageURL`, `description`, `telegram`, and `x` fields are ignored. Show IPFS upload example * Python * JavaScript * Rust * Go ```python import requests # Define token metadata uri_data = { "name": "PumpApi", "symbol": "PAPI", "description": "Fast API for Pump.fun, Raydium, Meteora", "twitter": "https://x.com/realpumpapi", "telegram": "https://t.me/YOUR_TG", "website": "https://pumpapi.io", "showName": "true" } # Load your image # Windows: # with open("C:\\Users\\Admin\\Pictures\\coin_logo.webp", "rb") as f: # file_content = f.read() # Linux / macOS: with open("/path/to/coin_logo.webp", "rb") as f: file_content = f.read() coin_logo = {"file": ("coin_logo.webp", file_content, "image/webp")} # Upload to IPFS via Pump.fun response = requests.post("https://pump.fun/api/ipfs", data=uri_data, files=coin_logo) uri = response.json()["metadataUri"] # Use the URI when creating your token data = { "privateKey": "base58_private_key", "action": "create", "name": "PumpApi", "symbol": "PAPI", "uri": uri, "amount": "0.0001", "priorityFee": "0.00002001", } response = requests.post("https://api.pumpapi.io", json=data) print(response.json()) ``` ```javascript import axios from 'axios'; import fs from 'fs'; import FormData from 'form-data'; const uriData = { name: "PumpApi", symbol: "PAPI", description: "Fast API for Pump.fun, Raydium, Meteora", twitter: "https://x.com/realpumpapi", telegram: "https://t.me/YOUR_TG", website: "https://pumpapi.io", showName: "true", }; const form = new FormData(); Object.entries(uriData).forEach(([k, v]) => form.append(k, v)); form.append("file", fs.createReadStream("/path/to/coin_logo.webp"), "coin_logo.webp"); const ipfsRes = await axios.post("https://pump.fun/api/ipfs", form, { headers: form.getHeaders(), }); const uri = ipfsRes.data.metadataUri; const data = { privateKey: "base58_private_key", action: "create", name: "PumpApi", symbol: "PAPI", uri, amount: "0.0001", priorityFee: "0.00002001", }; const response = await axios.post("https://api.pumpapi.io", data); console.log(response.data); ``` ```rust use reqwest::{Client, multipart}; use serde_json::json; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let file_bytes = fs::read("/path/to/coin_logo.webp")?; let part = multipart::Part::bytes(file_bytes).file_name("coin_logo.webp"); let form = multipart::Form::new() .text("name", "PumpApi") .text("symbol", "PAPI") .text("description", "Fast API for Pump.fun, Raydium, Meteora") .text("twitter", "https://x.com/realpumpapi") .text("telegram", "https://t.me/YOUR_TG") .text("website", "https://pumpapi.io") .text("showName", "true") .part("file", part); let ipfs_res: serde_json::Value = client .post("https://pump.fun/api/ipfs") .multipart(form) .send() .await? .json() .await?; let uri = ipfs_res["metadataUri"].as_str().unwrap(); let res = client .post("https://api.pumpapi.io") .json(&json!({ "privateKey": "base58_private_key", "action": "create", "name": "PumpApi", "symbol": "PAPI", "uri": uri, "amount": "0.0001", "priorityFee": "0.00002001" })) .send() .await? .text() .await?; println!("{}", res); Ok(()) } ``` ```go package main import ( "bytes" "encoding/json" "fmt" "io" "mime/multipart" "net/http" "os" ) func main() { // Upload to IPFS var b bytes.Buffer w := multipart.NewWriter(&b) for k, v := range map[string]string{ "name": "PumpApi", "symbol": "PAPI", "description": "Fast API for Pump.fun, Raydium, Meteora", "twitter": "https://x.com/realpumpapi", "telegram": "https://t.me/YOUR_TG", "website": "https://pumpapi.io", "showName": "true", } { w.WriteField(k, v) } f, _ := os.Open("/path/to/coin_logo.webp") defer f.Close() fw, _ := w.CreateFormFile("file", "coin_logo.webp") io.Copy(fw, f) w.Close() ipfsResp, _ := http.Post("https://pump.fun/api/ipfs", w.FormDataContentType(), &b) var ipfsResult map[string]interface{} json.NewDecoder(ipfsResp.Body).Decode(&ipfsResult) uri := ipfsResult["metadataUri"].(string) // Create token data := map[string]interface{}{ "privateKey": "base58_private_key", "action": "create", "name": "PumpApi", "symbol": "PAPI", "uri": uri, "amount": "0.0001", "priorityFee": "0.00002001", } jsonData, _ := json.Marshal(data) resp, err := http.Post("https://api.pumpapi.io", "application/json", bytes.NewBuffer(jsonData)) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Println(result) } ``` *** #### Response Format[​](#response-format "Direct link to Response Format") | Request type | Response | | ------------------------------------------------- | ------------------------------------------------------------------------------- | | **Single transaction** | `{"signature": "...", "createdMints": ["mint_address_1"], "err": ""}` | | **[Jito Bundle](/jito-bundles.md)** (up to 5 txs) | `{"signatures": ["...", "..."], "createdMints": ["mint_address_1"], "err": ""}` | * `createdMints` — array of mint addresses for every token created in the request. If you create multiple tokens in one Jito Bundle, you'll get multiple entries here. * `err` is an **empty string `""` on success**, or the error message on failure. *** Need help? Join our [Telegram group](https://t.me/pumpapi_devs). --- ## FAQ ### ❓ Frequently Asked Questions Here’s some helpful information to get you started. **Which pools do you support?** We support the following platforms for both trading and data stream APIs: * pump.fun * pump-amm * Raydium Launchpad (including Bonk) * Raydium CPMM * Meteora Launchpad (including Bags, moonshot) * Meteora DAMM V1 * Meteora DAMM V2 In addition, we also support: * Native Solana (SOL) transfers * Token transfers **How can I have more than 2 connections to the data stream?** We send a very large number of transactions, so to avoid overloading our server, there is a strict limit of 1 connection per IP address. You can use ZeroMQ (highly recommended; it's extremely simple to use). The idea is straightforward: You run a single instance that receives all transactions from our server. Then, from that instance, you share each transaction using the PUB/SUB method. Here's how it works: You send data to a specific port, for example, 8939. Then the SUB (subscribers — your two, three, or more bots) connect to this port and receive all the transactions. This way, you only have one connection for all three bots. ZeroMQ is currently the most efficient solution (latency is practically nonexistent — measured in nanoseconds). But you can also use WebSockets to stream locally (a bit slower — latency in microseconds). This method may be better because you only need to change one URL from "wss://stream.pumpapi.io" to "ws://127.0.0.1:9999": Here's an example of the **data stream sender** (just run it and do not close it): **ZeroMQ method. Super fast. Requires a few changes on the bot side** * Python * JavaScript * Rust * Go ```python import asyncio import websockets import zmq # pip install pyzmq import sys ctx = zmq.Context() if sys.platform == "win32": sock = ctx.socket(zmq.PUB) sock.bind("tcp://127.0.0.1:8939") else: sock = ctx.socket(zmq.PUB) sock.bind("ipc://8939") # On Linux, IPC is slightly faster than TCP async def connect_pumpapi_stream(): while True: try: async with websockets.connect("wss://stream.pumpapi.io") as ws: while True: msg = await ws.recv() sock.send(msg) except Exception as e: print(e) await asyncio.sleep(1) asyncio.run(connect_pumpapi_stream()) ``` ```javascript const WebSocket = require('ws'); const zmq = require('zeromq'); async function createStreamer() { const sock = new zmq.Publisher(); if (process.platform === 'win32') { await sock.bind('tcp://127.0.0.1:8939'); } else { await sock.bind('ipc://8939'); // On Linux, IPC is slightly faster than TCP } async function connectPumpApiStream() { while (true) { try { const ws = new WebSocket('wss://stream.pumpapi.io'); ws.on('message', async (msg) => { await sock.send(msg); }); ws.on('error', (error) => { console.error(error); }); ws.on('close', () => { console.log('Connection closed, reconnecting...'); }); // Wait for connection to close await new Promise((resolve) => { ws.on('close', resolve); }); await new Promise(resolve => setTimeout(resolve, 1000)); } catch (error) { console.error(error); await new Promise(resolve => setTimeout(resolve, 1000)); } } } connectPumpApiStream(); } createStreamer(); ``` ```rust use tokio_tungstenite::{connect_async, tungstenite::Message}; use zmq::{Context, Socket, PUB}; use futures_util::{SinkExt, StreamExt}; use std::time::Duration; #[tokio::main] async fn main() { let context = Context::new(); let sock = context.socket(PUB).unwrap(); if cfg!(target_os = "windows") { sock.bind("tcp://127.0.0.1:8939").unwrap(); } else { sock.bind("ipc://8939").unwrap(); // On Linux, IPC is slightly faster than TCP } connect_pumpapi_stream(sock).await; } async fn connect_pumpapi_stream(sock: Socket) { loop { match connect_async("wss://stream.pumpapi.io").await { Ok((ws_stream, _)) => { let (mut write, mut read) = ws_stream.split(); while let Some(msg) = read.next().await { match msg { Ok(Message::Text(text)) => { if let Err(e) = sock.send(&text, 0) { eprintln!("Error sending message: {}", e); } } Ok(Message::Binary(data)) => { if let Err(e) = sock.send(&data, 0) { eprintln!("Error sending message: {}", e); } } Err(e) => { eprintln!("WebSocket error: {}", e); break; } _ => {} } } } Err(e) => { eprintln!("Connection error: {}", e); tokio::time::sleep(Duration::from_secs(1)).await; } } } } ``` ```go package main import ( "context" "fmt" "log" "runtime" "time" "github.com/gorilla/websocket" "github.com/pebbe/zmq4" ) func main() { ctx, err := zmq4.NewContext() if err != nil { log.Fatal(err) } defer ctx.Term() sock, err := ctx.NewSocket(zmq4.PUB) if err != nil { log.Fatal(err) } defer sock.Close() if runtime.GOOS == "windows" { err = sock.Bind("tcp://127.0.0.1:8939") } else { err = sock.Bind("ipc://8939") // On Linux, IPC is slightly faster than TCP } if err != nil { log.Fatal(err) } connectPumpApiStream(sock) } func connectPumpApiStream(sock *zmq4.Socket) { for { conn, _, err := websocket.DefaultDialer.Dial("wss://stream.pumpapi.io", nil) if err != nil { fmt.Printf("Connection error: %v\n", err) time.Sleep(1 * time.Second) continue } for { _, message, err := conn.ReadMessage() if err != nil { fmt.Printf("Read error: %v\n", err) conn.Close() break } _, err = sock.SendBytes(message, 0) if err != nil { fmt.Printf("Send error: %v\n", err) } } time.Sleep(1 * time.Second) } } ``` ****Here's an example of your bot that receives transactions (the getter):**** * Python * JavaScript * Rust * Go **Synchronous version:** ```python import zmq # there's also asynchonios package zmq.asyncio import sys import orjson # or json ctx = zmq.Context() if sys.platform == "win32": sock = ctx.socket(zmq.SUB) sock.connect("tcp://127.0.0.1:8939") else: sock = ctx.socket(zmq.SUB) sock.connect("ipc://8939") # On Linux, IPC is slightly faster than TCP sock.setsockopt_string(zmq.SUBSCRIBE, "") while True: msg = sock.recv() msg = orjson.loads(msg) print(msg) ``` **Asynchronous version:** ```python import zmq.asyncio import sys import orjson import asyncio ctx = zmq.asyncio.Context() if sys.platform == "win32": sock = ctx.socket(zmq.SUB) sock.connect("tcp://127.0.0.1:8939") else: sock = ctx.socket(zmq.SUB) sock.connect("ipc://8939") # On Linux, IPC is slightly faster than TCP sock.setsockopt_string(zmq.SUBSCRIBE, "") async def runner(): while True: msg = await sock.recv() msg = orjson.loads(msg) print(msg) asyncio.run(runner()) ``` **Synchronous version:** ```javascript const zmq = require('zeromq'); async function createClient() { const sock = new zmq.Subscriber(); if (process.platform === 'win32') { sock.connect('tcp://127.0.0.1:8939'); } else { sock.connect('ipc://8939'); // On Linux, IPC is slightly faster than TCP } sock.subscribe(''); // Subscribe to all messages for await (const [msg] of sock) { const data = JSON.parse(msg.toString()); console.log(data); } } createClient(); ``` **Promise-based version:** ```javascript const zmq = require('zeromq'); async function createAsyncClient() { const sock = new zmq.Subscriber(); if (process.platform === 'win32') { sock.connect('tcp://127.0.0.1:8939'); } else { sock.connect('ipc://8939'); // On Linux, IPC is slightly faster than TCP } sock.subscribe(''); // Subscribe to all messages while (true) { try { const [msg] = await sock.receive(); const data = JSON.parse(msg.toString()); console.log(data); } catch (error) { console.error('Error receiving message:', error); } } } createAsyncClient(); ``` **Synchronous version:** ```rust use zmq::{Context, SUB}; use serde_json::Value; fn main() { let context = Context::new(); let sock = context.socket(SUB).unwrap(); if cfg!(target_os = "windows") { sock.connect("tcp://127.0.0.1:8939").unwrap(); } else { sock.connect("ipc://8939").unwrap(); // On Linux, IPC is slightly faster than TCP } sock.set_subscribe(b"").unwrap(); // Subscribe to all messages loop { let msg = sock.recv_bytes(0).unwrap(); let data: Value = serde_json::from_slice(&msg).unwrap(); println!("{}", data); } } ``` **Asynchronous version:** ```rust use tokio_zmq::{prelude::*, Sub}; use serde_json::Value; use futures::StreamExt; #[tokio::main] async fn main() { let mut sock = Sub::new().unwrap(); if cfg!(target_os = "windows") { sock.connect("tcp://127.0.0.1:8939").unwrap(); } else { sock.connect("ipc://8939").unwrap(); // On Linux, IPC is slightly faster than TCP } sock.set_subscribe("").unwrap(); // Subscribe to all messages let mut stream = sock.stream(); while let Some(msg) = stream.next().await { match msg { Ok(multipart) => { if let Some(data) = multipart.get(0) { match serde_json::from_slice::(data) { Ok(parsed) => println!("{}", parsed), Err(e) => eprintln!("JSON parse error: {}", e), } } } Err(e) => eprintln!("Receive error: {}", e), } } } ``` **Synchronous version:** ```go package main import ( "encoding/json" "fmt" "log" "runtime" "github.com/pebbe/zmq4" ) func main() { ctx, err := zmq4.NewContext() if err != nil { log.Fatal(err) } defer ctx.Term() sock, err := ctx.NewSocket(zmq4.SUB) if err != nil { log.Fatal(err) } defer sock.Close() if runtime.GOOS == "windows" { err = sock.Connect("tcp://127.0.0.1:8939") } else { err = sock.Connect("ipc://8939") // On Linux, IPC is slightly faster than TCP } if err != nil { log.Fatal(err) } err = sock.SetSubscribe("") // Subscribe to all messages if err != nil { log.Fatal(err) } for { msg, err := sock.RecvBytes(0) if err != nil { log.Printf("Receive error: %v", err) continue } var data interface{} if err := json.Unmarshal(msg, &data); err != nil { log.Printf("JSON parse error: %v", err) continue } fmt.Println(data) } } ``` **Goroutine-based version:** ```go package main import ( "encoding/json" "fmt" "log" "runtime" "time" "github.com/pebbe/zmq4" ) func main() { ctx, err := zmq4.NewContext() if err != nil { log.Fatal(err) } defer ctx.Term() sock, err := ctx.NewSocket(zmq4.SUB) if err != nil { log.Fatal(err) } defer sock.Close() if runtime.GOOS == "windows" { err = sock.Connect("tcp://127.0.0.1:8939") } else { err = sock.Connect("ipc://8939") // On Linux, IPC is slightly faster than TCP } if err != nil { log.Fatal(err) } err = sock.SetSubscribe("") // Subscribe to all messages if err != nil { log.Fatal(err) } // Message processing goroutine go func() { for { msg, err := sock.RecvBytes(0) if err != nil { log.Printf("Receive error: %v", err) time.Sleep(100 * time.Millisecond) continue } var data interface{} if err := json.Unmarshal(msg, &data); err != nil { log.Printf("JSON parse error: %v", err) continue } fmt.Println(data) } }() // Keep main goroutine alive select {} } ``` **WebSockets method. Fast. No changes required. You just need to change the link on the bot side to `ws://127.0.0.1:9999`.** * Python * JavaScript * Rust * Go ```python import asyncio import websockets clients = set() async def client_handler(websocket): clients.add(websocket) try: await websocket.wait_closed() finally: clients.remove(websocket) async def relay(): print("Relay started.") while True: try: async with websockets.connect("wss://stream.pumpapi.io") as ws: async for msg in ws: for client in list(clients): asyncio.create_task(client.send(msg)) except Exception as e: print(e) await asyncio.sleep(0.2) print("Reconnecting...") async def main(): server = await websockets.serve(client_handler, "localhost", 9999) await relay() asyncio.run(main()) ``` ```javascript const WebSocket = require('ws'); const clients = new Set(); const server = new WebSocket.Server({ port: 9999, host: 'localhost' }); server.on('connection', (ws) => { clients.add(ws); ws.on('close', () => { clients.delete(ws); }); }); async function relay() { console.log('Relay started.'); while (true) { try { const ws = new WebSocket('wss://stream.pumpapi.io'); ws.on('message', (msg) => { for (const client of clients) { if (client.readyState === WebSocket.OPEN) { client.send(msg); } } }); await new Promise((resolve, reject) => { ws.on('close', resolve); ws.on('error', reject); }); } catch (e) { console.log(e); await new Promise(resolve => setTimeout(resolve, 200)); console.log('Reconnecting...'); } } } relay(); ``` ```rust use tokio_tungstenite::{connect_async, tungstenite::Message, WebSocketStream}; use tokio::net::{TcpListener, TcpStream}; use tokio_tungstenite::accept_async; use std::collections::HashSet; use std::sync::Arc; use tokio::sync::Mutex; use futures_util::{SinkExt, StreamExt}; use std::time::Duration; type Clients = Arc>>>; #[tokio::main] async fn main() { let clients: Clients = Arc::new(Mutex::new(HashSet::new())); let clients_clone = Arc::clone(&clients); tokio::spawn(async move { let listener = TcpListener::bind("127.0.0.1:9999").await.unwrap(); while let Ok((stream, _)) = listener.accept().await { let clients = Arc::clone(&clients_clone); tokio::spawn(async move { if let Ok(ws_stream) = accept_async(stream).await { { let mut clients_lock = clients.lock().await; clients_lock.insert(ws_stream); } } }); } }); relay(clients).await; } async fn relay(clients: Clients) { println!("Relay started."); loop { match connect_async("wss://stream.pumpapi.io").await { Ok((ws_stream, _)) => { let (mut write, mut read) = ws_stream.split(); while let Some(msg) = read.next().await { match msg { Ok(Message::Text(text)) => { let mut clients_lock = clients.lock().await; for client in clients_lock.iter_mut() { let _ = client.send(Message::Text(text.clone())).await; } } Ok(Message::Binary(data)) => { let mut clients_lock = clients.lock().await; for client in clients_lock.iter_mut() { let _ = client.send(Message::Binary(data.clone())).await; } } Err(e) => { println!("{}", e); break; } _ => {} } } } Err(e) => { println!("{}", e); tokio::time::sleep(Duration::from_millis(200)).await; println!("Reconnecting..."); } } } } ``` ```go package main import ( "fmt" "log" "net/http" "sync" "time" "github.com/gorilla/websocket" ) var clients = make(map[*websocket.Conn]bool) var clientsMutex sync.Mutex var upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true }, } func handleClient(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } clientsMutex.Lock() clients[conn] = true clientsMutex.Unlock() defer func() { clientsMutex.Lock() delete(clients, conn) clientsMutex.Unlock() conn.Close() }() for { _, _, err := conn.ReadMessage() if err != nil { break } } } func relay() { fmt.Println("Relay started.") for { conn, _, err := websocket.DefaultDialer.Dial("wss://stream.pumpapi.io", nil) if err != nil { fmt.Println(err) time.Sleep(200 * time.Millisecond) fmt.Println("Reconnecting...") continue } for { _, message, err := conn.ReadMessage() if err != nil { fmt.Println(err) conn.Close() break } clientsMutex.Lock() for client := range clients { go func(c *websocket.Conn) { c.WriteMessage(websocket.TextMessage, message) }(client) } clientsMutex.Unlock() } time.Sleep(200 * time.Millisecond) fmt.Println("Reconnecting...") } } func main() { http.HandleFunc("/", handleClient) go func() { log.Fatal(http.ListenAndServe(":9999", nil)) }() relay() } ``` **Why do you send all transactions through the Data Stream? Can my computer handle that?** Some users worry that we're sending too many transactions — but in reality, it’s not as much as it might sound. We send around 300-400 transactions per second, covering **all events across all pools** and **all solana and token transfers**, and each transaction is **very small in size**. Even older processors can easily handle this load. In fact, most modern servers, desktops, and laptops would be capable of processing **over 30,000** of these lightweight events per second without any special optimization. So there's no need to worry — your machine can handle it just fine. We designed it this way to ensure faster delivery and greater flexibility. Instead of requiring you to query us for updates or specific token transfers, you receive everything in real time — instantly, and with much less complexity on your side. **Where is your server located? I want the lowest possible latency!** Our server is located in **New York**, close to the Solana RPC infrastructure. This helps ensure the lowest possible latency for trading and transaction processing. If you're running infrastructure or bots, we recommend hosting them as close to New York as possible for the best performance. Even if you're not in New York — or your server isn't — don’t worry! Thanks to efficient message delivery and lightweight transactions, performance will still be **very fast** and more than sufficient for most use cases. Have a question? Ask us in our [Telegram group](https://t.me/pumpapi_devs) --- ## Fees ### PumpApi Fees PumpApi keeps pricing simple and transparent. | Operation | Fee | | ------------------ | --------------------------------- | | Data Stream | **FREE** | | Historical Replay | **FREE** | | Trade (buy/sell) | **0.25 %** of trade volume | | Create Token | **10 000 lamports** (0.00001 SOL) | | Claim Cashback | **10 000 lamports** (0.00001 SOL) | | Initiate migration | **10 000 lamports** (0.00001 SOL) | | Wrap SOL | **10 000 lamports** (0.00001 SOL) | | Get Token Info | **10 000 lamports** (0.00001 SOL) | | Burn Tokens | **10 000 lamports** (0.00001 SOL) | | Transfer | **10 000 lamports** (0.00001 SOL) | --- ## Get Token Info ### Get Token Info – API Use this endpoint to request the **current on‑chain token info** of any token by its mint address. > **Heads‑up!**
• We charge a fixed fee of 10,000 lamports.
• For real‑time prices you can use our **[WebSocket stream](/stream.md)**. #### Endpoint[​](#endpoint "Direct link to Endpoint") `POST https://api.pumpapi.io` #### Request Body[​](#request-body "Direct link to Request Body") | Field | Description | | ------------ | ----------------------------------------------------------------------------------------------- | | `privateKey` | Fee wallet | | `action` | Must be **"getTokenInfo"** | | `mint` | Mint address of the token | | `quoteMint` | You need to provide this if the token exists only in pools where the second token is not Solana | | `poolId` | Provide this to get info about a specific pool | The response JSON returns the current price in SOL per token, total supply, pool name, decimals, burned liquidity, and the reserves of Solana (or another quote token) and the token. If a token has multiple pools, we return data from the largest one. #### 📦 Code Examples[​](#-code-examples "Direct link to 📦 Code Examples") * Python * JavaScript * Rust * Go ```python import requests url = "http://api.pumpapi.io" data = { "privateKey": "your_private_key", # fee taken from this wallet "action": "getTokenInfo", "mint": "token_address", } response = requests.post(url, json=data) print(response.json()) # {"price":2.795899348462258e-8,"pool":"pump","vTokensInBondingCurve":1072999999.999999,"vSolInBondingCurve":30.000000009,"supply":1000000000,"timestamp":1753230239232} ``` ```javascript import axios from 'axios'; const data = { privateKey: "your_private_key", // fee taken from this wallet action: "getTokenInfo", mint: "token_address" }; axios.post('http://api.pumpapi.io', data) .then(res => console.log(res.data)) // {"price":2.795899348462258e-8,"pool":"pump","vTokensInBondingCurve":1072999999.999999,"vSolInBondingCurve":30.000000009,"supply":1000000000,"timestamp":1753230239232} .catch(err => console.error(err)); ``` ```rust use reqwest::Client; use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let res = client .post("http://api.pumpapi.io") .json(&json!({ "privateKey": "your_private_key", "action": "getTokenInfo", "mint": "token_address" })) .send() .await? .text() .await?; println!("{}", res); // {"price":2.795899348462258e-8,"pool":"pump","vTokensInBondingCurve":1072999999.999999,"vSolInBondingCurve":30.000000009,"supply":1000000000,"timestamp":1753230239232} Ok(()) } ``` ```go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "privateKey": "your_private_key", // fee taken from this wallet "action": "getTokenInfo", "mint": "token_address", } jsonData, _ := json.Marshal(data) resp, err := http.Post("http://api.pumpapi.io", "application/json", bytes.NewBuffer(jsonData)) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Println(result) // map[price:0.0123] } ``` --- ## Historical Replay ### Historical Replay Starting from **April 18, 2026**, we save everything our [Data Stream](/stream.md) emits, **every hour**. Perfect for **backtesting strategies**, replaying market conditions, research, or anything else you can think of. #### How it works[​](#how-it-works "Direct link to How it works") Every hour we flush all incoming events to a compressed archive named after the **UTC hour** it covers: ```text https://replay.pumpapi.io/YEAR/MONTH/DAY/HOUR.jsonl.zst ``` For example, the events between `2026-04-18 00:00:00 UTC` and `2026-04-18 01:00:00 UTC` live at: ```text https://replay.pumpapi.io/2026/04/18/01.jsonl.zst ``` We use **zstandard (zst) compression** so downloads are as fast as possible: * **~400 MB** per hour compressed * **~2 GB** in memory after decompression * **~1 second** to decompress one hour Each line in the decompressed file is a single JSON event — the exact same format you get from the live Data Stream. #### Browsing available archives[​](#browsing-available-archives "Direct link to Browsing available archives") You can browse what's saved directly in your browser: * `https://replay.pumpapi.io/2026/` — list all months in 2026 * `https://replay.pumpapi.io/2026/04/` — list all days in April 2026 * `https://replay.pumpapi.io/2026/04/18/` — list all hourly files for April 18 *** #### Example: Replay the last N hours[​](#example-replay-the-last-n-hours "Direct link to Example: Replay the last N hours") The snippets below take a `HOURS` variable and stream every event from that window. For example, if it's currently `12:45 UTC` and `HOURS = 2`, you'll replay every event between `10:00` and `12:00` UTC. * Python * JavaScript * Rust * Go ```python import asyncio from datetime import datetime, timezone, timedelta import orjson as json # or use the standard json module (orjson is faster) import aiohttp import zstandard as zstd HOURS = 2 # last 2 hours ALLOW_GAPS = False async def fetch(session, hour_dt): url = f"https://replay.pumpapi.io/{hour_dt:%Y/%m/%d/%H}.jsonl.zst" print(f"[fetch] {url}") async with session.get(url) as r: if r.status == 404: if not ALLOW_GAPS: raise RuntimeError(f"missing: {url}") return None r.raise_for_status() return await r.read() async def main(): now = datetime.now(timezone.utc).replace(minute=0, second=0, microsecond=0) hours = [now - timedelta(hours=i) for i in range(HOURS, 0, -1)] dctx = zstd.ZstdDecompressor() async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout()) as session: for hour_dt in hours: compressed = await fetch(session, hour_dt) if compressed is None: continue print('downloaded') decompressed = dctx.decompress(compressed) print('decompressed') for line in decompressed.splitlines(): event = json.loads(line.decode()) print(event) if __name__ == "__main__": asyncio.run(main()) ``` ```javascript import { ZstdInit } from '@oneidentity/zstd-js'; const HOURS = 2; // last 2 hours const ALLOW_GAPS = false; async function fetchHour(hourDt) { const y = hourDt.getUTCFullYear(); const m = String(hourDt.getUTCMonth() + 1).padStart(2, '0'); const d = String(hourDt.getUTCDate()).padStart(2, '0'); const h = String(hourDt.getUTCHours()).padStart(2, '0'); const url = `https://replay.pumpapi.io/${y}/${m}/${d}/${h}.jsonl.zst`; console.log(`[fetch] ${url}`); const res = await fetch(url); if (res.status === 404) { if (!ALLOW_GAPS) throw new Error(`missing: ${url}`); return null; } if (!res.ok) throw new Error(`HTTP ${res.status}: ${url}`); return new Uint8Array(await res.arrayBuffer()); } async function main() { const { ZstdSimple } = await ZstdInit(); const now = new Date(); now.setUTCMinutes(0, 0, 0); const hours = []; for (let i = HOURS; i > 0; i--) { hours.push(new Date(now.getTime() - i * 3600 * 1000)); } const decoder = new TextDecoder(); for (const hourDt of hours) { const compressed = await fetchHour(hourDt); if (compressed === null) continue; console.log('downloaded'); const decompressed = ZstdSimple.decompress(compressed); console.log('decompressed'); const text = decoder.decode(decompressed); for (const line of text.split('\n')) { if (!line) continue; const event = JSON.parse(line); console.log(event); } } } main().catch(console.error); ``` ```rust use chrono::{Duration, Timelike, Utc}; use reqwest::Client; use std::io::{BufRead, BufReader}; use zstd::stream::read::Decoder; const HOURS: i64 = 2; // last 2 hours const ALLOW_GAPS: bool = false; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let now = Utc::now() .with_minute(0).unwrap() .with_second(0).unwrap() .with_nanosecond(0).unwrap(); let hours: Vec<_> = (1..=HOURS) .rev() .map(|i| now - Duration::hours(i)) .collect(); for hour_dt in hours { let url = format!( "https://replay.pumpapi.io/{}.jsonl.zst", hour_dt.format("%Y/%m/%d/%H") ); println!("[fetch] {}", url); let res = client.get(&url).send().await?; if res.status().as_u16() == 404 { if !ALLOW_GAPS { return Err(format!("missing: {}", url).into()); } continue; } let compressed = res.error_for_status()?.bytes().await?; println!("downloaded"); let decoder = Decoder::new(&compressed[..])?; let reader = BufReader::new(decoder); println!("decompressed"); for line in reader.lines() { let line = line?; if line.is_empty() { continue; } let event: serde_json::Value = serde_json::from_str(&line)?; println!("{}", event); } } Ok(()) } ``` ```go package main import ( "bufio" "bytes" "encoding/json" "fmt" "io" "net/http" "time" "github.com/klauspost/compress/zstd" ) const ( HOURS = 2 // last 2 hours ALLOW_GAPS = false ) func fetchHour(hourDt time.Time) ([]byte, error) { url := fmt.Sprintf( "https://replay.pumpapi.io/%s.jsonl.zst", hourDt.Format("2006/01/02/15"), ) fmt.Printf("[fetch] %s\n", url) resp, err := http.Get(url) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode == 404 { if !ALLOW_GAPS { return nil, fmt.Errorf("missing: %s", url) } return nil, nil } if resp.StatusCode >= 400 { return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, url) } return io.ReadAll(resp.Body) } func main() { now := time.Now().UTC().Truncate(time.Hour) var hours []time.Time for i := HOURS; i > 0; i-- { hours = append(hours, now.Add(-time.Duration(i)*time.Hour)) } for _, hourDt := range hours { compressed, err := fetchHour(hourDt) if err != nil { panic(err) } if compressed == nil { continue } fmt.Println("downloaded") decoder, err := zstd.NewReader(bytes.NewReader(compressed)) if err != nil { panic(err) } fmt.Println("decompressed") scanner := bufio.NewScanner(decoder) scanner.Buffer(make([]byte, 1024*1024), 64*1024*1024) for scanner.Scan() { var event map[string]interface{} if err := json.Unmarshal(scanner.Bytes(), &event); err != nil { panic(err) } fmt.Println(event) } decoder.Close() } } ``` *** Backtesting When you backtest on replay data, remember that a **huge portion** of transactions on AMMs like pump.fun are sent through [Jito Bundles](/jito-bundles.md). A bundle must be treated as **one big atomic transaction** — you cannot insert your own transaction in the middle of one. Bundles are not explicitly labeled in the event stream, but you can detect them heuristically: * Every event has a **millisecond-precision timestamp**. Transactions inside the same bundle are executed simultaneously, so their timestamps are almost identical — typically within **1–3 ms** of each other. * Bundled transactions usually **interact with the same token**. A solid rule of thumb: treat any group of transactions that hit the **same token within ~3 ms** as a single bundled event. For certainty, you can cross-check any suspicious cluster on . Also, add a **slightly larger-than-usual latency buffer** to your simulation. Real execution will always be a bit slower than replay, and being conservative here prevents your backtest from looking more profitable than reality. `localTimestamp` In the replay archives you'll encounter an extra field that **does not exist** in the live Data Stream: `localTimestamp`. It's the time at which **our replay server** in Frankfurt am Main (Germany) received the transaction — which may be slightly later than what you'd observe on your end in real time. **You generally don't need it** for backtesting purposes, but it can be useful for checking whether your own server had good latency at a given moment. *** Need help? Join our [Telegram group](https://t.me/pumpapi_devs). --- ## Jito Bundles ### Jito Bundles **Jito Bundles** let you send **up to 5 transactions** in a single atomic package — **all or nothing**. Either every transaction lands, or none of them do. Unlike [Actions](/actions.md), the transactions inside a bundle are **independent** from each other on-chain. To an outside observer, they look like completely unrelated transactions — but they are guaranteed to execute together, in order, with **no possibility for anyone to sandwich or front-run between them**. #### Why use Jito Bundles?[​](#why-use-jito-bundles "Direct link to Why use Jito Bundles?") * **Token creators**: launch a token and buy it from multiple wallets in a way that looks like organic, unrelated buys — while staying 100% protected from snipers squeezing in between your transactions. * **Fast account cleanup**: sell everything across multiple transactions at once. * **Massive batch operations**: combine with [Actions](/actions.md) to pack multiple operations into each transaction — e.g. 4 sells per tx × 5 txs = **20 sells in one bundle**. For simple transfers, you can fit **~150 transfers in a single bundle**. #### Endpoint[​](#endpoint "Direct link to Endpoint") `POST https://api.pumpapi.io` *** #### Basic Structure[​](#basic-structure "Direct link to Basic Structure") Just send your usual requests wrapped inside a `transactions` array: ```json { "transactions": [ { /* tx1 */ }, { /* tx2 */ }, { /* tx3 */ }, { /* tx4 */ }, { /* tx5 */ } ] } ``` Each transaction in the array follows the exact same format as a normal [Trade API](/trade-api.md) call. Everything we support works inside bundles — including [Actions](/actions.md) (so you can nest actions inside each transaction) and `guaranteedDelivery` (continuous re-sending of your transaction, perfect for sells). Just place these flags **above** the `transactions` array. *** #### Jito Tip and Jito tip payer[​](#jito-tip-and-jito-tip-payer "Direct link to Jito Tip and Jito tip payer") You can specify `jitoTip` and its payer **above** the `transactions` array to set it globally for the whole bundle: ```json { "jitoTip": 0.00001, "transactions": [ { /* tx1 */ }, { /* tx2 */ } ] } ``` info * If `jitoTip` is not set at the top level, the value from the **last transaction** in the array will be used. * If it's not specified anywhere, we fall back to the **minimum Jito tip of `0.00001 SOL`** (the minimum Jito requires). * If `privateKey` (for lightning mode) or `publicKey` (for local mode) is not specified above the `transactions` block, the payer of the **last transaction** covers the Jito tip. priority Fee is ignored inside bundles Inside a Jito Bundle, `priorityFee` has **no effect** — bundles are routed through the Jito Block Engine, which doesn't care about Solana priority fees. **Only `jitoTip` determines how fast your bundle lands.** You can safely omit `priorityFee` from every transaction in the bundle. *** #### Example: Create a Token and Buy from 4 Other Wallets[​](#example-create-a-token-and-buy-from-4-other-wallets "Direct link to Example: Create a Token and Buy from 4 Other Wallets") This is the classic use case — launch a token, have the dev wallet buy on creation, and then buy from 4 additional wallets in separate transactions that look unrelated to outside observers. * Python * JavaScript * Rust * Go ```python import requests url = "https://api.pumpapi.io" data = { "jitoTip": 0.00001, "transactions": [ { "privateKey": "dev_wallet_private_key", "action": "create", "name": "PumpApi", "symbol": "PAPI", "description": "Fast API for Pump.fun, Raydium, Meteora", "imageURL": "https://pumpapi.io/img/pumpapi_logo.webp", "website": "https://pumpapi.io", "telegram": "https://t.me/YOUR_TG", "x": "https://x.com/realpumpapi", "mintRef": "0", # set a temporary reference for the new token "initialTradeAction": "buy", # dev buy "amount": 0.5, "denominatedInQuote": True, "slippage": 99, }, { "privateKey": "wallet_2_private_key", "action": "buy", "mintRef": "0", # reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": True, "slippage": 99, }, { "privateKey": "wallet_3_private_key", "action": "buy", "mintRef": "0", # reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": True, "slippage": 99, }, { "privateKey": "wallet_4_private_key", "action": "buy", "mintRef": "0", # reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": True, "slippage": 99, }, { "privateKey": "wallet_5_private_key", "action": "buy", "mintRef": "0", # reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": True, "slippage": 99, } ] } response = requests.post(url, json=data) print(response.json()) ``` ```javascript import axios from 'axios'; const data = { jitoTip: 0.0001, transactions: [ { privateKey: "dev_wallet_private_key", action: "create", name: "PumpApi", symbol: "PAPI", description: "Fast API for Pump.fun, Raydium, Meteora", imageURL: "https://pumpapi.io/img/pumpapi_logo.webp", website: "https://pumpapi.io", telegram: "https://t.me/YOUR_TG", x: "https://x.com/realpumpapi", mintRef: "0", // set a temporary reference for the new token initialTradeAction: "buy", // dev buy amount: 0.5, denominatedInQuote: true, slippage: 99, }, { privateKey: "wallet_2_private_key", action: "buy", mintRef: "0", // reuse the mintRef we set during create amount: 0.3, denominatedInQuote: true, slippage: 99, }, { privateKey: "wallet_3_private_key", action: "buy", mintRef: "0", // reuse the mintRef we set during create amount: 0.3, denominatedInQuote: true, slippage: 99, }, { privateKey: "wallet_4_private_key", action: "buy", mintRef: "0", // reuse the mintRef we set during create amount: 0.3, denominatedInQuote: true, slippage: 99, }, { privateKey: "wallet_5_private_key", action: "buy", mintRef: "0", // reuse the mintRef we set during create amount: 0.3, denominatedInQuote: true, slippage: 99, } ] }; axios.post("https://api.pumpapi.io", data) .then(response => console.log(response.data)) .catch(error => console.error(error)); ``` ```rust use reqwest::Client; use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let res = client .post("https://api.pumpapi.io") .json(&json!({ "jitoTip": 0.00001, "transactions": [ { "privateKey": "dev_wallet_private_key", "action": "create", "name": "PumpApi", "symbol": "PAPI", "description": "Fast API for Pump.fun, Raydium, Meteora", "imageURL": "https://pumpapi.io/img/pumpapi_logo.webp", "website": "https://pumpapi.io", "telegram": "https://t.me/YOUR_TG", "x": "https://x.com/realpumpapi", "mintRef": "0", // set a temporary reference for the new token "initialTradeAction": "buy", // dev buy "amount": 0.5, "denominatedInQuote": true, "slippage": 99, }, { "privateKey": "wallet_2_private_key", "action": "buy", "mintRef": "0", // reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": true, "slippage": 99, }, { "privateKey": "wallet_3_private_key", "action": "buy", "mintRef": "0", // reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": true, "slippage": 99, }, { "privateKey": "wallet_4_private_key", "action": "buy", "mintRef": "0", // reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": true, "slippage": 99, }, { "privateKey": "wallet_5_private_key", "action": "buy", "mintRef": "0", // reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": true, "slippage": 99, } ] })) .send() .await? .text() .await?; println!("{}", res); Ok(()) } ``` ```go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "jitoTip": 0.00001, "transactions": []map[string]interface{}{ { "privateKey": "dev_wallet_private_key", "action": "create", "name": "PumpApi", "symbol": "PAPI", "description": "Fast API for Pump.fun, Raydium, Meteora", "imageURL": "https://pumpapi.io/img/pumpapi_logo.webp", "website": "https://pumpapi.io", "telegram": "https://t.me/YOUR_TG", "x": "https://x.com/realpumpapi", "mintRef": "0", // set a temporary reference for the new token "initialTradeAction": "buy", // dev buy "amount": 0.5, "denominatedInQuote": true, "slippage": 99, }, { "privateKey": "wallet_2_private_key", "action": "buy", "mintRef": "0", // reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": true, "slippage": 99, }, { "privateKey": "wallet_3_private_key", "action": "buy", "mintRef": "0", // reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": true, "slippage": 99, }, { "privateKey": "wallet_4_private_key", "action": "buy", "mintRef": "0", // reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": true, "slippage": 99, }, { "privateKey": "wallet_5_private_key", "action": "buy", "mintRef": "0", // reuse the mintRef we set during create "amount": 0.3, "denominatedInQuote": true, "slippage": 99, }, }, } jsonData, _ := json.Marshal(data) resp, err := http.Post("https://api.pumpapi.io", "application/json", bytes.NewBuffer(jsonData)) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Println(result) } ``` *** #### Response Format[​](#response-format "Direct link to Response Format") ```json { "signatures": [ "signature1", "signature2", "signature3", "signature4", "signature5" ], "err": "" // "createdMints": ["created_mint_address"] ← only present if the bundle contained a "create" action // ...other extra fields may appear depending on which actions you performed inside the bundle } ``` * `signatures` — array of transaction signatures, one per transaction in the bundle (up to 5). * `err` — `""` on success, or the error message if the bundle failed. * `createdMints` — **only included when the bundle creates one or more tokens**. Contains the mint addresses of the newly created tokens. * Other extra fields may appear depending on which actions you performed inside the bundle. *** #### Actions vs. Jito Bundles[​](#actions-vs-jito-bundles "Direct link to Actions vs. Jito Bundles") | | Actions | Jito Bundles | | ------------ | --------------------------------------------------- | ------------------------------------------------ | | What it does | Combines multiple operations into **1 transaction** | Combines multiple transactions into **1 bundle** | | Use case | Multi-step logic in one atomic tx | Atomic ordering of separate txs | See the [Actions](/actions.md) page for details. And remember — the two can be **combined**: nest Actions inside each transaction of a Jito Bundle to pack even more operations into a single atomic package. *** #### Local (Unsigned) Transactions[​](#local-unsigned-transactions "Direct link to Local (Unsigned) Transactions") Local transactions are fully supported for Jito Bundles. Head over to the [Trade API](/trade-api.md) page for a local-transactions example and adapt it to the `transactions` array format shown above. Important Jito Bundles can **only** be submitted to the Jito Block Engine, which enforces strict rate limits for unauthorized users — typically only **~1 out of every 5 transactions** will be accepted for a new wallet. To raise these limits you must apply through the [Jito Discord](https://discord.gg/jito). **This is why we strongly recommend using the lightning version** (the default examples shown above on this page) — it's both faster and dramatically simpler, with no rate-limit headaches. *** Need help? Join our [Telegram group](https://t.me/pumpapi_devs). --- ## Legal ### Terms of Use **Last updated:** December 27, 2025 By accessing or using **PumpApi** (the “Service”) you agree to these Terms of Use (“Terms”).
If you do not agree with any part of the Terms, you must not use the Service. **PumpApi is not affiliated with, endorsed by, or formally connected to pump.fun, Raydium, PumpSwap or any other third‑party protocol.** *** #### 1. Eligibility & Compliance[​](#1-eligibility--compliance "Direct link to 1. Eligibility & Compliance") * You are solely responsible for complying with all laws and regulations that apply to you. * You must not use the Service if doing so is illegal where you live or if you are subject to any sanctions or restrictions. *** #### 2. Fees[​](#2-fees "Direct link to 2. Fees") PumpApi currently charges a **0.25 % trading fee**.
If we change the fee, we will notify users via our **Telegram channel**. The fee that applies to a transaction is the one displayed (or returned) at the time the request is submitted. *** #### 3. Risk Disclosure[​](#3-risk-disclosure "Direct link to 3. Risk Disclosure") * Trading cryptocurrencies and meme coins is highly volatile and involves significant risk. * You understand that blockchain transactions are irreversible and that network conditions, smart‑contract bugs, or other technical issues can cause unexpected losses or delays. * **PumpApi provides only software that forwards your instructions to public blockchain networks; we do not execute trades on your behalf nor hold custody of your assets.** *** #### 4. No Warranty[​](#4-no-warranty "Direct link to 4. No Warranty") The Service is provided **“as is” and “as available”** without warranties of any kind—express, implied, or statutory—including, but not limited to, warranties of merchantability, fitness for a particular purpose, accuracy, or availability. *** #### 5. Limitation of Liability[​](#5-limitation-of-liability "Direct link to 5. Limitation of Liability") To the maximum extent permitted by law, **PumpApi and its team are not liable for any direct, indirect, incidental, special, consequential, or exemplary damages** arising from or in connection with your use of the Service **— even if those losses result from bugs, outages, or errors on our side.**
Your sole remedy for dissatisfaction with the Service is to stop using it. *** #### 6. Changes to the Service or Terms[​](#6-changes-to-the-service-or-terms "Direct link to 6. Changes to the Service or Terms") * We may modify, suspend, or discontinue the Service at any time without liability. * We may update these Terms by posting a revised version. Continued use of the Service after changes become effective constitutes acceptance of the new Terms. *** #### 7. Contact[​](#7-contact "Direct link to 7. Contact") Questions? Email us at ****. *** **PumpApi** — Faster, cheaper, smarter meme‑coin trading. --- ## Migrate Pump Fun ### 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[​](#how-it-works "Direct link to 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[​](#buy-or-sell-on-migration "Direct link to Buy or Sell on Migration") If you also want to trade at migration time, you have two options: * **[Jito Bundles](/jito-bundles.md) (recommended)** — send `migrate` and the trade as **two separate transactions** inside one atomic bundle. Simpler, faster, and no CPI workarounds needed. * **[Actions](/actions.md)** — 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[​](#jito-bundles-approach "Direct link to Jito Bundles Approach") With [Jito Bundles](/jito-bundles.md), `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** — `buy` or `sell` (executes immediately after migration) ##### Example Request[​](#example-request "Direct link to Example Request") ```json { "jitoTip": 0.00001, "transactions": [ { "privateKey": "base58_private_key", "action": "migrate", "mint": "token_address" }, { "privateKey": "base58_private_key", "action": "buy", "mint": "token_address", "amount": 0.2, "denominatedInQuote": true, "slippage": 200 } ] } ``` That's it. No `wrapSol`, no `disableSolWrapper`, no CPI math. ##### Code Example[​](#code-example "Direct link to Code Example") The example below listens to the stream and fires a Jito Bundle (migrate + buy) the moment `tokensInPool == 0`. * Python * JavaScript * Rust * Go ```python 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"], }, { "privateKey": "base58_private_key", "action": "buy", # or "sell" "mint": event["mint"], "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()) ``` ```javascript 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, }, { privateKey: "base58_private_key", action: "buy", // or "sell" mint: event.mint, 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(); ``` ```rust 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> { 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"] }, { "privateKey": "base58_private_key", "action": "buy", // or "sell" "mint": event["mint"], "amount": 0.2, "denominatedInQuote": true, "slippage": 200 } ] })) .send() .await? .text() .await?; println!("{}", res); } } } Ok(()) } ``` ```go 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"], }, { "privateKey": "base58_private_key", "action": "buy", // or "sell" "mint": event["mint"], "amount": 0.2, "denominatedInQuote": true, "slippage": 200, }, }, }) } } } ``` #### Actions Approach[​](#actions-approach "Direct link to Actions Approach") With [Actions](/actions.md), `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. CPI Limit — Important 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)[​](#step-1--wrap-your-sol-separate-transaction "Direct link to Step 1 — Wrap your SOL (separate transaction)") Call `wrapSol` in advance to convert SOL into wSOL: ```python 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"`[​](#step-2--set-disablesolwrapper-true "Direct link to step-2--set-disablesolwrapper-true") Always pass `"disableSolWrapper": "true"` in your migrate request. Without it, the transaction will fail with an **Unknown program error**. note 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[​](#example-request-1 "Direct link to Example Request") ```json { "privateKey": "base58_private_key", "action": "migrate", "initialTradeAction": "buy", "disableSolWrapper": "true", "mint": "token_address", "amount": "0.01", "denominatedInQuote": "true", "slippage": "200", "priorityFee": "0.00019" } ``` ##### Code Example[​](#code-example-1 "Direct link to 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 ```python 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": "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()) ``` ```javascript 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: "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(); ``` ```rust 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> { 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": "200", "priorityFee": "0.00002001" })) .send() .await? .text() .await?; println!("{}", res); } } } Ok(()) } ``` ```go 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": "200", "priorityFee": "0.00002001", }) } } } ``` *** #### Endpoint[​](#endpoint "Direct link to Endpoint") `POST https://api.pumpapi.io` #### Local Transactions[​](#local-transactions "Direct link to Local Transactions") Local transactions are supported for both approaches above, but will be slower. Use the same logic as on the [Trade API](/trade-api.md) page. *** Need help? Join our [Telegram group](https://t.me/pumpapi_devs). --- ## Stream ### Data Stream – WebSocket API Use this endpoint to stream live: * Solana and tokens transfers * Pump.fun * PumpSwap * Raydium Launchpad (Bonk) * Raydium-CPMM * Meteora Launchpad (Bags, moonshot) * Meteora DAMM V1 * Meteora DAMM V2 Subscribing delivers every event the moment it happens — **transfers, token creations, buys, sells, migrations, pool creations, adding and removing liquidity.** #### Endpoint[​](#endpoint "Direct link to Endpoint") ```text wss://stream.pumpapi.io/ ``` **Filter on the client.** The server sends *all* events.
**One connection only.** Open a single WebSocket per client and [reuse it](/FAQ.md).
**Reconnect logic.** Connections can drop (for example, due to server-side updates or your network issues). You should implement automatic reconnection in your client. #### 📦 Code Examples[​](#-code-examples "Direct link to 📦 Code Examples") * Python * JavaScript * Rust * Go ```python import asyncio import websockets import orjson as json # or use the standard json module (orjson is faster) async def pumpapi_data_stream(): uri = "wss://stream.pumpapi.io/" async with websockets.connect(uri) as websocket: async for message in websocket: event = json.loads(message) print(event) # {'txType': 'buy', 'pool': 'pump', ...} asyncio.run(pumpapi_data_stream()) ``` ```javascript import WebSocket from 'ws'; // npm i ws const ws = new WebSocket('wss://stream.pumpapi.io/'); ws.on('message', data => { const event = JSON.parse(data); console.log(event); // { txType: 'create', pool: 'pump', ... } }); ws.on('error', console.error); ``` ```rust // [package] // name = "pumpapi_stream" // version = "0.1.0" // edition = "2024" // [dependencies] // tokio = { version = "1", features = ["macros", "rt-multi-thread"] } // tokio-tungstenite = { version = "0.29", features = ["rustls-tls-webpki-roots"] } // futures-util = "0.3" // serde_json = "1" // url = "2" // rustls = { version = "0.23", features = ["ring"] } use futures_util::StreamExt; use serde_json::Value; use tokio_tungstenite::{connect_async, tungstenite::Message}; #[tokio::main] async fn main() { rustls::crypto::ring::default_provider() .install_default() .expect("Failed to install crypto provider"); let (mut ws, _) = connect_async("wss://stream.pumpapi.io/") .await .expect("Failed to connect"); while let Some(msg) = ws.next().await { if let Ok(Message::Binary(bin)) = msg { let text = std::str::from_utf8(&bin).unwrap(); let event: Value = serde_json::from_str(text).unwrap(); println!("{}", event); } } } ``` ```go package main import ( "context" "encoding/json" "fmt" "log" "nhooyr.io/websocket" ) func main() { ctx := context.Background() c, _, err := websocket.Dial(ctx, "wss://stream.pumpapi.io/", nil) if err != nil { log.Fatal(err) } defer c.Close(websocket.StatusNormalClosure, "") for { _, data, err := c.Read(ctx) if err != nil { log.Fatal(err) } var event map[string]interface{} json.Unmarshal(data, &event) fmt.Println(event) } } ``` ##### Event Examples[​](#event-examples "Direct link to Event Examples") * Transfer * Create * Trade * Migration * Create Pool * Liquidity Change ```json { "signature": "3mGAAs4CkM86s3bN8Jkt2EZRGf7ZVkbgNk2pV3STrEof8nCdFg5kqWdFyohp23uFVwierCvdeuXBzy3QDwNdaX4L", "txType": "transfer", "txSigner": "YubQzu18FDqJRyNfG8JqHmsdbxhnoQqcKUHBdUkN6tP", "transfers": [ { "from": "YubQzu18FDqJRyNfG8JqHmsdbxhnoQqcKUHBdUkN6tP", "to": "2XxKhfVBna1Jjs5PdCQmjAXHg6NnFXoNVEHfEz2ZnnL8", "amount": 500.0, "isSolana": False, "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" }, { "from": "YubQzu18FDqJRyNfG8JqHmsdbxhnoQqcKUHBdUkN6tP", "to": "2XxKhfVBna1Jjs5PdCQmjAXHg6NnFXoNVEHfEz2ZnnL8", "amount": 0.00002, "isSolana": True, <----- is TRUE when mint is Sol or WSOL (wrapped sol) "mint": "So11111111111111111111111111111111111111111" } ], "postBalances": { "YubQzu18FDqJRyNfG8JqHmsdbxhnoQqcKUHBdUkN6tP": { "sol": 0.015701352 }, "2XxKhfVBna1Jjs5PdCQmjAXHg6NnFXoNVEHfEz2ZnnL8": { "sol": 2.628047189 } }, "priorityFee": 0.000005, "block": 388677942, "timestamp": 1766513012579 } ``` * Pump.Fun * Raydium Launchpad (Bonk) * Meteora Launchpad (Bags, moonshot) ```json { "signature": "58zv6eEs2Y9ARPt9VSdpo6h3A4sg2ijgNftk8vXGvjoHQEiMqgoL6mNnWX9uZ26WS6mtzWuXduf8vuhUwUKJ73Wk", "txType": "create", "poolId": "EzW8aPTiayL6zNw3rpPTDiPsbPFfNcn7TNB8BstbUYh9", "mint": "AvxohnS3SSJRfw4h9u2am5DTRrNv9HY5je7EdqpVSA2i", "txSigner": "3dxmSSoSbLpmyZZTJhGP4w9DUPLCrMyyUNpb6eL8e3Rf", "initialBuy": 97545454.545454, "solAmount": 3.0, "tokensInPool": 695554545.454546, "solInPool": 3.0, "vTokensInBondingCurve": 975454545.454546, "vSolInBondingCurve": 33.0, "price": 3.383038210624416e-8, "marketCapSol": 33.83038210624416, "poolFeeRate": 0.0125, "name": "CHUD", "symbol": "CHUD", "uri": "https://metadata.j7tracker.com/metadata/2456cf930d0a4a0e.json", "supply": 1000000000, "pool": "pump", "creatorFeeAddress": "3dxmSSoSbLpmyZZTJhGP4w9DUPLCrMyyUNpb6eL8e3Rf", "mayhemMode": False, "cashbackEnabled": True, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token-2022", "tokenExtensions": { "metadataPointer": {}, "tokenMetadata": {} }, "tradersInvolved": { "3dxmSSoSbLpmyZZTJhGP4w9DUPLCrMyyUNpb6eL8e3Rf": {} }, "postBalances": { "3dxmSSoSbLpmyZZTJhGP4w9DUPLCrMyyUNpb6eL8e3Rf": { "sol": 4.21 }, }, "decimals": 6, "priorityFee": 0.00016, "block": 401114006, "timestamp": 1771427621579 } ``` ```json { "signature": "3LweLX1CG8qV17EmUztAw6isAZdY8sk2ZmHjiGhC6oY6BvdYQousXDivsSPi2xCf99PDdVAgqDBfyiLniTcmHNqR", "txType": "create", "poolId": "FADuSqhV5nQqxCapi2PDvKnNnBBqg8DZEEvVGviELXrk", "mint": "HnXKZ5GRemRnKUP7wBcmrVcRYkFvQNKq8FtLj3gB1ray", "txSigner": "tAg2tgyHmkGTsmq8wBSKsGvUUgoH37cxmCfRUZSXdtB", "initialBuy": 151903987.89698, "solAmount": 5.0, "tokensInPool": 848096012.10302, "solInPool": 4.9475, "vTokensInBondingCurve": 921121617.699402, "vSolInBondingCurve": 34.948352951, "price": 3.7941084303598455e-8, "marketCapSol": 37.94108430359845, "poolFeeRate": 0.0105, "name": "arrest", "symbol": "aarrest", "uri": "https://ipfs.io/ipfs/bafkreih2bzlff4xrkehmezdzirl6ybawtth24niqtjc6qwnordbhydglaq", "supply": 1000000000, "pool": "raydium-launchpad", "platform": "custom", "launchpadConfig": "4Bu96XjU84XjPDSpveTVf6LYGCkfW5FK7SNkREWcEfV4", "migrationThreshholds": { "quote": 85.0 }, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "tAg2tgyHmkGTsmq8wBSKsGvUUgoH37cxmCfRUZSXdtB": {} }, "postBalances": { "tAg2tgyHmkGTsmq8wBSKsGvUUgoH37cxmCfRUZSXdtB": { "sol": 4.21 }, }, "decimals": 6, "priorityFee": 0.000025, "block": 401380627, "timestamp": 1771532401666 } ``` ```json { "signature": "4gDNN8nDdoi84oUW4vb7yKQhsR8kZ5wTiF92REqDiDREGijh7P6VhNeW3QHSozKWA1vCN3GVLjYZfYuetMLjDvtb", "txType": "create", "poolId": "BDLE4vLJ94mp9tEbTNRhf16vaNJ1cWfUrn5ePfx4EWpv", "mint": "CBy1rMkQAHH2Jq8fDsHYTQJBy8S83saHMHM4674TPMuX", "txSigner": "6Ed7RBZrKVAq2V67CpNUGJXp4Avnz25KjVzJCq7LaLYz", "initialBuy": 984498429.538417, "solAmount": 23.0, "tokensInPool": 13034155.851211, "solInPool": 23.0, "price": 1.7741917491987863e-6, "marketCapSol": 1774.1917491987863, "poolFeeRate": 0.0025, "name": "Tesla AI", "symbol": "Tesla AI", "uri": "https://ipfs.io/ipfs/bafkreigcmxnxj2gteuf6oti2orhztqxp4ngtesaayw2xnmbp6h4pxspgba", "supply": 1000000000, "pool": "meteora-launchpad", "lockedLiquidityAfterMigration": "10%", <--- Beware of values below 100%, as they could lead to a rug pull after the migration. "poolFeeRateAfterMigration": 0.001, "migrationThreshholds": { "quote": 30.0 }, "launchpadConfig": "E6MQpAxta1AQhsDii3trQrGVooqjkwKQ4LgwZ4SCVgo", "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "6Ed7RBZrKVAq2V67CpNUGJXp4Avnz25KjVzJCq7LaLYz": {} }, "postBalances": { "6Ed7RBZrKVAq2V67CpNUGJXp4Avnz25KjVzJCq7LaLYz": { "sol": 4.21 }, }, "decimals": 6, "priorityFee": 0.00005, "block": 400339537, "timestamp": 1771124275539 } ``` * Pump.Fun * Pump AMM * Raydium CPMM * Raydium Launchpad (Bonk) * Meteora Launchpad (Bags, moonshot) * Meteora DAMM V1 * Meteora DAMM V2 ```json { "signature": "91cQMmvA6hrQUuDbAtoKUG8vq3EC8NaNCAh3n4XSoCVoNQqdC9SgtL4zzGdMPXbN9gRivmEk3DLBwzXFzBuyTq7", "txType": "buy", "poolId": "FoZhom3zWdgAG2JPbMyZyhRnhrCYi3s5jXfZtoJG5JQ1", "mint": "J5ThJvvEctb6biaumE9bFi8QyShLKBwSmftsqxdCgmi9", "txSigner": "4N5vGo5R1cWHb5vktSR5VHhPkt7eHYM8JE7vFE9G9uux", "tokenAmount": 42640626.248501, "solAmount": 2.151563012, "tokensInPool": 497785441.584331, "solInPool": 11.392056839, "vTokensInBondingCurve": 777685441.584331, "vSolInBondingCurve": 41.392056839, "price": 5.322467751829646e-8, "marketCapSol": 53.22467751829646, "poolFeeRate": 0.0125, "pool": "pump", "creatorFeeAddress": "bwamJzztZsepfkteWRChggmXuiiCQvpLqPietdNfSXa", "mayhemMode": False, "cashbackEnabled": True, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token-2022", "tokenExtensions": { "metadataPointer": {}, "tokenMetadata": {} }, "tradersInvolved": { "4N5vGo5R1cWHb5vktSR5VHhPkt7eHYM8JE7vFE9G9uux": {} }, "postBalances": { "4N5vGo5R1cWHb5vktSR5VHhPkt7eHYM8JE7vFE9G9uux": { "sol": 4.21 }, }, "priorityFee": 0.000505, "block": 401114161, "timestamp": 1771427682998 } ``` * Pool created by Pump.Fun migration * Custom pool * Token-USDC pool (without SOL) ```json { "signature": "63NbM8aanc3y2NjNp18sEFwv65qW4qzYLfQod8gmEGrGhKiBiGBH7fsHZ7MWbXLNP6RGTCznP4fGQu3YboCkdkwD", "txType": "buy", "poolId": "BP7s98rTsd5xTr2CgSVd9Lcpie3KF5yAkmohUgAp1No4", "mint": "E9qgYkgok8aCFXWuvQiYM8krmDbeTuARLgrBjThWpump", "txSigner": "52oc72vjNbpUhF7jNE1pPAvc17JwBTyxybFp3u7PvetG", "tokenAmount": 321548.585603, "solAmount": 0.33830352, "tokensInPool": 135059178.605839, "solInPool": 142.435011367, "price": 1.0546118585741353e-6, "marketCapSol": 1054.6034417168921, "poolFeeRate": 0.012, "pool": "pump-amm", "poolCreatedBy": "pump", <--- pool created through a pump.fun migration. You can trust these pools — you will be able to sell. "burnedLiquidity": "100%", <--- means that no one has the ability to withdraw liquidity and perform a rug pull. But it doesn’t have to be 100% — even 30% of burned liquidity can be good, depending on the situation "creatorFeeAddress": "HRs5oryur4seP4ei1VUqV3j82HMGuvQrjsEzASHed8Ud", "mayhemMode": False, "cashbackEnabled": False, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token-2022", "tokenExtensions": { "metadataPointer": {}, "tokenMetadata": {} }, "tradersInvolved": { "52oc72vjNbpUhF7jNE1pPAvc17JwBTyxybFp3u7PvetG": {} }, "postBalances": { "52oc72vjNbpUhF7jNE1pPAvc17JwBTyxybFp3u7PvetG": { "sol": 4.21 }, }, "priorityFee": 0.000012511, "block": 388731952, "timestamp": 1766534221298 } ``` ```json { "signature": "3fYZhMxTRAkxnqfs6P9KShx7AuaxUEKVtCBsP45reqbvrwubc8xk6MCfXBWTVQ3ByPZBgNUnLeC28L5D1WZq9C9q", "txType": "sell", "poolId": "HF9dDDhUu5EjCNTTe2vqvGp8y2SzYHG65RV8Ta1RGSTf", "mint": "941B3sqimoBugHGRDikTaHRwCa5Bv98PEHPK1NFFEmFe", "txSigner": "GJFEs6vbiWF42Sm4ZirZwSzyH5g2NkhetasBfq56rYR8", "tokenAmount": 1182305.746851, "solAmount": 0.590348816, "tokensInPool": 825918325.932008, "solInPool": 411.979377831, "price": 4.988137021491824e-7, "marketCapSol": 498.81347768301646, "poolFeeRate": 0.003, "pool": "pump-amm", "poolCreatedBy": "custom", <--- Be careful with pools on pump-amm where poolCreatedBy is not "pump" "burnedLiquidity": "0%", <--- SCAM pool. 0% means that it’s possible to withdraw all the liquidity (remove 100% of the tokens and 100% SOL from the pool), so you will not be able sell bought tokens after that. "creatorFeeAddress": None, "mayhemMode": False, "cashbackEnabled": False, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "GJFEs6vbiWF42Sm4ZirZwSzyH5g2NkhetasBfq56rYR8": {}, "BhVaZzm4Qz7KFUsjWvqYMYWhC34jEMKdQbQK4ws9ycKX": {} }, "postBalances": { "GJFEs6vbiWF42Sm4ZirZwSzyH5g2NkhetasBfq56rYR8": { "sol": 4.21 }, "BhVaZzm4Qz7KFUsjWvqYMYWhC34jEMKdQbQK4ws9ycKX": { "sol": 4.21 }, }, "priorityFee": 0.0000126, "block": 388691516, "timestamp": 1766518353581 } ``` ```json { "signature": "2gAVP6wcY9xd9L1Gr4F3J2P8h8HS4akXci79Mj9D54vgfgvuZt7U1oX3KjQhHw4JfpvZ9Y1X1sYoAAs6B8xSnBJD", "txType": "sell", "poolId": "2uF4Xh61rDwxnG9woyxsVQP7zuA6kLFpb3NvnRQeoiSd", <--- you can open this address on solscan.io and see that there’s no SOL in this pool — only USDC and the token. To buy this token, you first need to buy the quoteToken (USDC) elsewhere. "mint": "pumpCmXqMfrsAkQ5r49WcJnRayYRqmXz6ae8H7H9Dfn", "quoteMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", <--- address of the second token involved (in this case USDC) "txSigner": "MfDuWeqSHEqTFVYZ7LoexgAK9dxk7cy4DFJWjWMGVWa", "tokenAmount": 1413344.36767, "quoteAmount": 2434.650564, <--- note that these pools use quoteAmount, not solAmount. "tokensInPool": 3722501951.582035, "quoteInPool": 6411226.583998, <--- note that these pools use quoteInPool, not solInPool. "price": 0.0017222896501836024, "marketCapQuote": 1722271139.078167, <--- note that these pools use marketCapQuote, not marketCapSol. "poolFeeRate": 0.003, "pool": "pump-amm", "poolCreatedBy": "custom", "burnedLiquidity": "0%", <--- in this particular case, 0% is not a problem because this poolId is the official PUMP-USDC pool from the pump.fun team. "creatorFeeAddress": None, "mayhemMode": False, "cashbackEnabled": False, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token-2022", "tokenExtensions": { "transferHook": {}, "metadataPointer": {}, "tokenMetadata": {} }, "tradersInvolved": { "MfDuWeqSHEqTFVYZ7LoexgAK9dxk7cy4DFJWjWMGVWa": {} }, "postBalances": { "MfDuWeqSHEqTFVYZ7LoexgAK9dxk7cy4DFJWjWMGVWa": { "sol": 4.21 }, }, "priorityFee": 0.000005, "block": 388734360, "timestamp": 1766535166075 } ``` * Pool created by Raydium Launchpad (Bonk) migration * Token-USDC pool (without SOL) ```json { "signature": "2CHuzsiFN94ptUneFuSUCn9miTCTnUX4AWSczCpzAFAa3Mb9RruLPuyTBRYYFXWKPNXSNMqAHJm3zGYuDE6aHcEw", "txType": "sell", "poolId": "4UN6WPJhfB9eoQq4XUwWiDj7NguW4iw9rx4iGBUguXcT", "mint": "6Tph3SxbAW12BSJdCevVV9Zujh97X69d5MJ4XjwKmray", "txSigner": "91WuBL56WNkLeXiFPQ1B1zDrHYNWo3KiC8Gp3FeJVCay", "tokenAmount": 142506.761471, "solAmount": 0.200875025, "tokensInPool": 143086855.207232, "solInPool": 201.996832141, "price": 1.4117078179435069e-6, "marketCapSol": 1411.489916606684, "poolFeeRate": 0.003, "pool": "raydium-cpmm", "poolCreatedBy": "raydium-launchpad", <---- pool created by raydium-launchpad (usually bonk), so you can trust it. "burnedLiquidity": "91%", "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "HU23r7UoZbqTUuh3vA7emAGztFtqwTeVips789vqxxBw": {} }, "postBalances": { "91WuBL56WNkLeXiFPQ1B1zDrHYNWo3KiC8Gp3FeJVCay": { "sol": 4.21 }, "HU23r7UoZbqTUuh3vA7emAGztFtqwTeVips789vqxxBw": { "sol": 4.21 }, }, "priorityFee": 0.000015, "block": 388738247, "timestamp": 1766536690846 } ``` ```json { "signature": "3DNxxfNzKEG7VLiKE8E39Ss3y2vtxxVabdgS4S3brj4M7HmeMwE8Qi11EnnyEngFPvi5zqv5GBzGXUAwxCBuDK4t", "txType": "sell", "poolId": "4aqQuRPDZBH29Y2SrpWm94mzVNjikwciorVTitQqJG9f", "mint": "J3NKxxXZcnNiMjKw9hYb2K4LUxgwB6t1FtPtQVsv3KFr", <---- one memecoin "quoteMint": "2GPJhV9jNrj7TaLYMRgWkcy6sTKLcwntv7nZ7qDyMRGM", <— another memecoin (usually it’s Solana or stablecoins, but this pool uses another volatile token). To buy this token, you first need to buy the quoteToken elsewhere. "txSigner": "3nMNd89AxwHUa1AFvQGqohRkxFEQsTsgiEyEyqXFHyyH", "tokenAmount": 44.84029903, "quoteAmount": 165.07403, <--- note that these pools use quoteAmount, not solAmount. "tokensInPool": 72739.71645621, "quoteInPool": 268287.940938, <--- note that these pools use quoteInPool, not solInPool. "price": 3.6883281102630074, "marketCapQuote": 378212218.3457152, <--- note that these pools use marketCapQuote, not marketCapSol. "poolFeeRate": 0.003, "pool": "raydium-cpmm", "poolCreatedBy": "custom", "burnedLiquidity": "0%", "mintAuthority": "BCD75RNBHrJJpW4dXVagL5mPjzRLnVZq4YirJdjEYMV7", "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "3nMNd89AxwHUa1AFvQGqohRkxFEQsTsgiEyEyqXFHyyH": {} }, "postBalances": { "91WuBL56WNkLeXiFPQ1B1zDrHYNWo3KiC8Gp3FeJVCay": { "sol": 4.21 }, }, "priorityFee": 0.000005, "block": 388739013, "timestamp": 1766536990765 } ``` ```json { "signature": "5kLZfmUnb7mDnXyNZXSVGWcUdj8BFkyLauycWMYpM6j2GPg9VFjgRnyGdqU1yij8wWahseVj5AwXeyBTw8m2oDpj", "txType": "buy", "poolId": "BnHRVmtz66soqmGifK7725TXzxUN2oBEhVDs2LjkhYN6", "mint": "rz9G7vCQBxPNrejMv4iVa3UyuxJasBAjjsmDnhnbonk", "quoteMint": "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB", <--- Address of the second token involved (in this case, USD1). To trade in this pool, you first need to buy the quoteMint (USD1) "txSigner": "HKHMrsfW9rDc1KrT1gU4AmC5A2hvVqwRMujN5eegMLX8", "tokenAmount": 0.227592, "quoteAmount": 0.000012, "tokensInPool": 239942135.99963, "quoteInPool": 10714.496586, "vTokensInBondingCurve": 312967741.595121, "vSolInBondingCurve": 15126.386725, "price": 0.00004833209533961698, "marketCapQuote": 48332.095339616986, "poolFeeRate": 0.013, "pool": "raydium-launchpad", "platform": "bonk", <--- can be "bonk", "raydium-launchlab", or "custom". Anyone can create a platform on the Raydium launchpad — Bonk is just one of them "launchpadConfig": "FfYek5vEz23cMkWsdJwG2oa6EphsvXSHrGpdALN4g6W1", "migrationThreshholds": { "quote": 12500.0 }, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "HKHMrsfW9rDc1KrT1gU4AmC5A2hvVqwRMujN5eegMLX8": {} }, "postBalances": { "HKHMrsfW9rDc1KrT1gU4AmC5A2hvVqwRMujN5eegMLX8": { "sol": 4.21 }, }, "priorityFee": 0.000006, "block": 388764798, "timestamp": 1766547117907 } ``` ```json { "signature": "5RKRyZ5E4EjezdhkpnvtPXJoR2hLpHispSrAEWebCAEjheni3D6UrciBWN3becVaaEnZBS69XcdnvvLDRENzUEJv", "txType": "buy", "poolId": "3DrHmGXh9LVFFxz5yGmgm8jagHijqhXvSQyjhus1etPa", "mint": "3DsdCFH1RGfaV1CUSJzbmYQ7vFSsVuEFK1tHRVAiBAGS", "txSigner": "13NZSDSMRvP75Y97UnDxD12LCTDtaXsmbyABSqyz8yDP", "tokenAmount": 2921696.077137405, "solAmount": 0.26, "tokensInPool": 469002275.1985069, "solInPool": 26.757985773, "price": 8.756358166765358e-8, "marketCapSol": 87.56358166765358, "poolFeeRate": 0.02, "pool": "meteora-launchpad", "lockedLiquidityAfterMigration": "100%", <--- Beware of values below 100%, as they could lead to a rug pull after the migration. "poolFeeRateAfterMigration": 0.02, <--- Check this: if it’s, for example, 0.1 (the maximum future fee allowed), you will lose 10% on each trade. "migrationThreshholds": { "quote": 85.0 }, "launchpadConfig": "A1z7xx4Vr24q2P6Hy7ee4Xo7RtQraM93M9FdTobmL3G9", "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "13NZSDSMRvP75Y97UnDxD12LCTDtaXsmbyABSqyz8yDP": {} }, "postBalances": { "13NZSDSMRvP75Y97UnDxD12LCTDtaXsmbyABSqyz8yDP": { "sol": 4.21 }, }, "priorityFee": 0.000055, "block": 400331273, "timestamp": 1771121062458 } ``` ```json { "signature": "4XRntfX8s4ryHi9XckbvPSFHeuhZ5AinoieybTRFuW39XW8ue9jheT4hDMj7eZnPt1Wc6XXkUHJNKt3H6VRCErXU", "txType": "buy", "poolId": "ERgpKaq59Nnfm9YRVAAhnq16cZhHxGcDoDWCzXbhiaNw", "mint": "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", "txSigner": "BCdcmLAuYx2RCGGCZhWuP9fgiZje2pBRrjLHr1qH4nZL", "tokenAmount": 0.111010032, "solAmount": 0.13997, "tokensInPool": 13603.81205579, "solInPool": 9693.056530216, "price": 1.2611303844898412, "marketCapSol": 13431882.291044032, "poolFeeRate": 0.0001, "pool": "meteora-damm-v1", "poolCreatedBy": "custom", "burnedLiquidity": "0%", "curveType": "StableSwap", "mintAuthority": "6iQKfEyhr3bZMotVkW6beNZz5CPAkiwvgV2CTje9pVSS", "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "BCdcmLAuYx2RCGGCZhWuP9fgiZje2pBRrjLHr1qH4nZL": {} }, "postBalances": { "BCdcmLAuYx2RCGGCZhWuP9fgiZje2pBRrjLHr1qH4nZL": { "sol": 4.21 }, }, "priorityFee": 0.000005, "block": 400345180, "timestamp": 1771126482369 } ``` ```json { "signature": "4XMDoJJt5HK97oWcqyjSKVw8pJY8McSy6KnEFiutuxWwkiqmmWwrrN2AtCBED77cz35gkitB6JatGD4cZzLFTXk5", "txType": "buy", "poolId": "Fy62YfCBu2z4hmLk3m68XrtBBLHtZXY1Si5rtVsrsRvs", "mint": "E9vNSVzuwWcmsFkbSVWXAhbPwETJv4aM8Lvs5ympump", "txSigner": "7zZ2vRvqyP5Ey9qy4s2593DTg8J4sNYWrmcNnmDE4F9B", "tokenAmount": 105775.084401, "solAmount": 0.610509458, "tokensInPool": 2944583.780026, "solInPool": 17.588974243, "price": 5.973331244963443e-6, "marketCapSol": 5973.331244963442, "poolFeeRate": 0.001, "pool": "meteora-damm-v2", "poolCreatedBy": "meteora-launchpad", "burnedLiquidity": "10%", "minPrice": 5.421214630269583e-23, "maxPrice": 1.844605071373595e16, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "7zZ2vRvqyP5Ey9qy4s2593DTg8J4sNYWrmcNnmDE4F9B": {} }, "postBalances": { "7zZ2vRvqyP5Ey9qy4s2593DTg8J4sNYWrmcNnmDE4F9B": { "sol": 4.21 }, }, "priorityFee": 0.000005, "block": 400350764, "timestamp": 1771128655157 } ``` * Pump AMM * Meteora DAMM V1 * Meteora DAMM V2 ```json { "signature": "4nownD6mfEWqPobc7TrfYk6VyTiqdyzkXqgSbJb2hNVxsXoNfAQADJBRP3JzuYX921NTyY9TLNVZnREdcbo3D4NP", "txType": "migrate", "poolId": "8xLk84gnavkayCu4o345bgC7Qk4oRNtU2AS1PaTqujwH", "mint": "3x2vP4L8D63AHh7DhLR2XuRuMT7CsMqDHwVKTdFepump", "txSigner": "EbpZp7vbpGXH4J22dJQAnLVoqTH688rQeJuaPWvF2sbz", "initialBuy": 0.0, "solAmount": 0.0, "tokensInPool": 206900000.0, "solInPool": 84.99036005, "price": 4.1077989391010155e-7, "marketCapSol": 410.7798906238624, "poolFeeRate": 0.0125, "supply": 999999992, "pool": "pump-amm", "poolCreatedBy": "pump", "burnedLiquidity": "100%", "creatorFeeAddress": "5cyRTqMkuDJeVC8Q1Bk1DnwAKFMEDX1ANG7ZbyA362F", "mayhemMode": False, "cashbackEnabled": False, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token-2022", "tokenExtensions": { "metadataPointer": {}, "tokenMetadata": {} }, "tradersInvolved": { "2pUhfahHvTXKmSKXEzHd77DJnRYH7cExRjfzGQaNqGyN": {} }, "postBalances": { "EbpZp7vbpGXH4J22dJQAnLVoqTH688rQeJuaPWvF2sbz": { "sol": 4.21 }, "2pUhfahHvTXKmSKXEzHd77DJnRYH7cExRjfzGQaNqGyN": { "sol": 4.21 }, }, "priorityFee": 0.001005, "block": 388775295, "timestamp": 1766551228263 } ``` ```json { "signature": "AgmvLnh4DR52FmPvGmPYLwmSihhU59iMe9F9TNXSsPLgV9JokAjzxrjAmQBPvXiv88noYD5ieWQzWtLefoafmEZ", "txType": "migrate", "poolId": "5DuwQd3MAiCcugJG24tq3TJ5MHCuWpZJywbkgMTYao6h", "mint": "DHuyFyr5vwMa9hC71i1nqg7kZ4PfxBG5FEm7TqrsxgUb", "txSigner": "CQdrEsYAxRqkwmpycuTwnMKggr3cr9fqY8Qma4J9TudY", "initialBuy": 0.0, "solAmount": 0.0, "tokensInPool": 23989059.724993, "solInPool": 11.976219064, "price": 4.992367021172813e-7, "marketCapSol": 499.2367021172813, "poolFeeRate": 0.0025, "supply": 1000000000, "pool": "meteora-damm-v1", "poolCreatedBy": "meteora-launchpad", "burnedLiquidity": "0%", "curveType": "ConstantProduct", "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM": {} }, "postBalances": { "CQdrEsYAxRqkwmpycuTwnMKggr3cr9fqY8Qma4J9TudY": { "sol": 4.21 }, "FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM": { "sol": 4.21 }, }, "priorityFee": 0.000005, "block": 400336821, "timestamp": 1771123217240 } ``` ```json { "signature": "4pqYzmMniFutZgzeKzVYcQxAQab872RLG5mGeTcAEBzZ9uLmELTpEXsrt3hsrHvSB8jYnW9HGRWxAYPZsypmdyBY", "txType": "migrate", "poolId": "GCv2qtGK2Qzi7df9dU6urKzo9WcsrU3fDjwkpddPYn7x", "mint": "CBy1rMkQAHH2Jq8fDsHYTQJBy8S83saHMHM4674TPMuX", "txSigner": "CQdrEsYAxRqkwmpycuTwnMKggr3cr9fqY8Qma4J9TudY", "initialBuy": 0.0, "solAmount": 0.0, "tokensInPool": 9980000.042297, "solInPool": 29.939999999, "price": 3e-6, "marketCapSol": 3000.0, "poolFeeRate": 0.001, "supply": 1000000000, "pool": "meteora-damm-v2", "poolCreatedBy": "meteora-launchpad", "burnedLiquidity": "10%", "minPrice": 5.421214630269583e-23, "maxPrice": 1.844605071373595e16, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM": {} }, "postBalances": { "CQdrEsYAxRqkwmpycuTwnMKggr3cr9fqY8Qma4J9TudY": { "sol": 4.21 }, "FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM": { "sol": 4.21 }, }, "priorityFee": 0.000016962, "block": 400343572, "timestamp": 1771125858449 } ``` * Pump AMM * Raydium CPMM * Meteora DAMM V1/V2 ```json { "signature": "3BiAqMmm2dX4GjLdajKQnSTMLTC3oQzRVVTEKvKeFu7jSAcE3kFVq7u3BpoSasx77U2rBTArRgqossfiGaTP1iDh", "txType": "createPool", "poolId": "8yEwjGT16re94WAahxrvMg79smn5B8rzZgPTNWFzkW3K", "mint": "DGvxoH8zEmatVbMMydMFDhsiofdgK2cFn6dPccNJ6hEg", "txSigner": "JBHqRGJ9rVGzHg3DqzbFkh235kjvEiEMhBMQsU4fiAon", "initialBuy": 0.0, <--- can be initialSell "solAmount": 0.0, "tokensInPool": 1000000000.0, "solInPool": 200.0, "price": 2e-7, "marketCapSol": 200.0000002, "poolFeeRate": 0.003, "supply": 1000000001, "pool": "pump-amm", "poolCreatedBy": "custom", <-- will always be "custom" in createPool events. If it’s "pump" or "raydium-launchpad", then it’s a migration event. But technically, these events are the same: migration = the memecoin launchpad creates the pool and burns liquidity; createPool = anyone else creates a pool. "burnedLiquidity": "0%", <-- a typical scam pool. They create a pool with a lot of liquidity, but they can remove it later because it isn’t burned. The higher the burnedLiquidity, the lower the risk of a rug pull by the liquidity providers. This is what happens after they get enough buyers Z2QxTU9Z9njczyF9akMJbzhRotDRbU1ieByXQhPn4MgW6BcdJUvqYkATG5EyWyRCuG3HKZd3ms7CRjLZ5TdGhep (they remove 100% of the liquidity — not the initial 200 SOL, but 100% of the current SOL reserves, so they pull 270 SOL out of the pool). "creatorFeeAddress": None, "mayhemMode": False, "cashbackEnabled": False, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "JBHqRGJ9rVGzHg3DqzbFkh235kjvEiEMhBMQsU4fiAon": {} }, "postBalances": { "JBHqRGJ9rVGzHg3DqzbFkh235kjvEiEMhBMQsU4fiAon": { "sol": 4.21 }, }, "priorityFee": 0.00003, "block": 388775128, "timestamp": 1766551163729 } ``` ```json { "signature": "4j6NuFZSfXehhVVe9fw9dqwohWXUqcHVv53uGq5ypyQoh6ZhJFVworjC7EVNCJugvyayNP241ibadi4hjYbALFVi", "txType": "createPool", "poolId": "JBAdtP9nYk95z44KRrb8gHffUUr4HVKkuXDKReZ5HyXg", "mint": "67BmAvfRHoxGPxbSMArCgmsLHfZnHFEhs7tiQSiXstY", "txSigner": "B2pbyEKzER21sJuKzTsAN6Ve2u6iaQ4fyS56Gf3zfRDL", "initialBuy": 0.0, "solAmount": 0.0, "tokensInPool": 90000000.0, "solInPool": 0.5, "price": 5.555555555555556e-9, "marketCapSol": 5.555555555555556, "poolFeeRate": 0.003, "supply": 1000000000, "pool": "raydium-cpmm", "poolCreatedBy": "custom", "burnedLiquidity": "0%", "mintAuthority": "B2pbyEKzER21sJuKzTsAN6Ve2u6iaQ4fyS56Gf3zfRDL", "freezeAuthority": "B2pbyEKzER21sJuKzTsAN6Ve2u6iaQ4fyS56Gf3zfRDL", "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "B2pbyEKzER21sJuKzTsAN6Ve2u6iaQ4fyS56Gf3zfRDL": {} }, "postBalances": { "B2pbyEKzER21sJuKzTsAN6Ve2u6iaQ4fyS56Gf3zfRDL": { "sol": 4.21 }, }, "priorityFee": 0.00002, "block": 388778410, "timestamp": 1766552453146 } ``` ```json { "signature": "1Xg2MAQ45srS1jEsimDe56cKxp6YhC2gKPq25zMgz9r11inJH8LFi7unX7443TXaGKV1FLsZvEVLyLyRN1DzsPN", "txType": "createPool", "poolId": "GBbDy9g2TrqS1n39D2CbfNmaUdMtzAahbpxvDirfmznH", "mint": "GuMGpj1ATXZHfBQzdPiQCu464icCGt9b2FX9YqrqBAGS", "txSigner": "5tnydcUMvUuc3a9UqmUHdyiiHN6u6EQynsK5M76zVfHo", "initialBuy": 0.0, "solAmount": 0.0, "tokensInPool": 96888.172006218, "solInPool": 0.54154636, "price": 5.589396549855622e-6, "marketCapSol": 5589.396393352518, "poolFeeRate": 0.5, <-- 50% fee per trade — that’s a scam. The fee can change over time; by the time you read this, it will be 10%. "supply": 999999972, "pool": "meteora-damm-v2", "poolCreatedBy": "custom", "burnedLiquidity": "0%", "minPrice": 5.421214630269582e-20, "maxPrice": 1.844605071373595e19, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "5tnydcUMvUuc3a9UqmUHdyiiHN6u6EQynsK5M76zVfHo": {} }, "postBalances": { "5tnydcUMvUuc3a9UqmUHdyiiHN6u6EQynsK5M76zVfHo": { "sol": 4.21 }, }, "priorityFee": 0.000046625, "block": 400339554, "timestamp": 1771124281945 } ``` * Add Liquidity * Remove Liquidity - Pump AMM - Raydium CPMM - Meteora DAMM V1 - Meteora DAMM V2 ```json { "signature": "4RcdMbUqHFCcMbZk3uCK8GyyAF5T8FtHkQq7ev1rwNvyw4W3Zr8ADkbqRBTVGGYT8EvLrYuu9WHnBznJgMYVADUm", "txType": "add", "poolId": "9HQoGtkzxsjDxCNRSLbGNtGGawvB2N1QBUnxksWHMgbn", "mint": "BV2CATw6dAcxxAoZ73P14DTXzbKTUZR41X7EXpKWpump", "txSigner": "2d6xLct3G6NiJ8kpPSqvW3KGCUXxPfhLsZkpJST1jQWG", "tokenAmount": 2227444.803763, "solAmount": 0.166734053, "tokensInPool": 561786808.85806, "solInPool": 42.052216573, "price": 7.485440368113883e-8, "marketCapSol": 74.85068094708055, "poolFeeRate": 0.0125, "pool": "pump-amm", "poolCreatedBy": "pump", "burnedLiquidity": "100%", "creatorFeeAddress": None, "mayhemMode": False, "cashbackEnabled": False, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "2d6xLct3G6NiJ8kpPSqvW3KGCUXxPfhLsZkpJST1jQWG": {} }, "postBalances": { "2d6xLct3G6NiJ8kpPSqvW3KGCUXxPfhLsZkpJST1jQWG": { "sol": 4.21 }, }, "priorityFee": 0.0000051, "block": 388781273, "timestamp": 1766553569793 } ``` ```json { "signature": "5vg11Cwm4ajx4fgWHQKbu7PyKZj6CTktETBUmbCeVGDzWA1XE248c759b8iACXJNLBUkfH5B1gENMADg8d4D5Jbh", "txType": "add", "poolId": "25oAdGCecRGkEP6xi6u9AQoXPP4qRZkLindjRZMVfpR1", "mint": "Augv5FR4mErhX8ge1aYBcos47E4D8jjMDcdggktcENp8", "txSigner": "9n54dhmAUnG2Tcdpnax4j2BDcZ1tj94uARZJekuH4hPQ", "tokenAmount": 0.00002, "solAmount": 0.01949851, "tokensInPool": 0.001825, "solInPool": 1.830396541, "price": 1002.9570087671233, "marketCapSol": 21061094.227100823, "poolFeeRate": 0.003, "pool": "raydium-cpmm", "poolCreatedBy": "custom", "burnedLiquidity": "0%", "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "9n54dhmAUnG2Tcdpnax4j2BDcZ1tj94uARZJekuH4hPQ": {} }, "postBalances": { "9n54dhmAUnG2Tcdpnax4j2BDcZ1tj94uARZJekuH4hPQ": { "sol": 4.21 }, }, "priorityFee": 0.00002, "block": 388917961, "timestamp": 1766607265405 } ``` ```json { "signature": "5E4d1U2g7m4ywzQ3sqDCHNDo6DWeZkGwEniyWtspqAwYkmKhSUVybgLmdemRKS85WMkFULomU4gjJR4xH7xGwcJ5", "txType": "add", "poolId": "HSemLnpwZ3fk9iNNMPUCXs6NSQoi7y3N941ExYz9ANBq", "mint": "9223LqDuoJXyhCtvi54DUQPGS8Xf29kUEQRr7Sfhmoon", "txSigner": "GEY2VzZkkpNmwX8aRoLi74E6dR5NUaMKNTzqZ2f7wVMW", "tokenAmount": 6455.543579235, "solAmount": 0.863042007, "tokensInPool": 22783593.656629577, "solInPool": 3045.993730175, "price": 0.00013369241815321245, "marketCapSol": 123314.52253589759, "poolFeeRate": 0.01, "pool": "meteora-damm-v1", "poolCreatedBy": "custom", "burnedLiquidity": "30%", "curveType": "ConstantProduct", "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "GEY2VzZkkpNmwX8aRoLi74E6dR5NUaMKNTzqZ2f7wVMW": {} }, "postBalances": { "GEY2VzZkkpNmwX8aRoLi74E6dR5NUaMKNTzqZ2f7wVMW": { "sol": 4.21 }, }, "priorityFee": 0.000246648, "block": 400338772, "timestamp": 1771123976775 } ``` ```json { "signature": "3EvVjD2h2pdk75v9HujmDmWGJc6iq1p8DeR3s3VUsvpqnVgDygBANCJDuaJRic12twnWb3Xzaqe3jbw3qrZtLUSd", "txType": "add", "poolId": "gyhSWHoCF37x8hHras8Uu5xdczS76qubkQtJuxstdJe", "mint": "HbYgynsDksniFLXNa3BkCTiaEBzQMW5oty4FhTHPpump", "txSigner": "8LWBRWyhQtqNZozDWuaiYVvFEk8aY1bBVxZm3FF4ZLYy", "tokenAmount": 2706.339287, "solAmount": 0.188476828, "tokensInPool": 2706.339286, "solInPool": 0.188476827, "price": 0.00006964271936271334, "marketCapSol": 69642.70898594815, "poolFeeRate": 0.060820011276, "pool": "meteora-damm-v2", "poolCreatedBy": "custom", "burnedLiquidity": "0%", "minPrice": 5.421214630269583e-23, "maxPrice": 1.844605071373595e16, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token-2022", "tokenExtensions": { "metadataPointer": {}, "tokenMetadata": {} }, "tradersInvolved": { "8LWBRWyhQtqNZozDWuaiYVvFEk8aY1bBVxZm3FF4ZLYy": {} }, "postBalances": { "8LWBRWyhQtqNZozDWuaiYVvFEk8aY1bBVxZm3FF4ZLYy": { "sol": 4.21 }, }, "priorityFee": 0.0001106, "block": 400342500, "timestamp": 1771125438883 } ``` * Pump AMM * Raydium CPMM * Meteora DAMM V1 * Meteora DAMM V2 ```json { "signature": "5zXxY5qU4N7DGyCri9BvFo5JncuzQEoG6nDENyMSTR3hEuyJjJu6qjxi8bpUAkjhMGEgvBBLp2oAFPbbnLsEXVmW", "txType": "remove", "poolId": "CGrZd8oMudu4MUQ5f6U8yDsyy3HuRZQ3pyf8vfXQhWHw", "mint": "DjFxES1DPXMpM6T8uyGGiDSD6wNuU6PgiZo4cKCp5m1s", "txSigner": "4JC5G8UT4aS4Kp7btmcXMpDmhrE6RnFVyqMHEaqPWUh9", "tokenAmount": 719110792.99595, "solAmount": 118.986806369, "tokensInPool": 0.007801, "solInPool": 2e-9, "price": 2.5637738751442124e-7, "marketCapSol": 256.37738751442123, "poolFeeRate": 0.003, "pool": "pump-amm", "poolCreatedBy": "custom", "burnedLiquidity": "100%", "creatorFeeAddress": None, "mayhemMode": False, "cashbackEnabled": False, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "4JC5G8UT4aS4Kp7btmcXMpDmhrE6RnFVyqMHEaqPWUh9": {} }, "postBalances": { "4JC5G8UT4aS4Kp7btmcXMpDmhrE6RnFVyqMHEaqPWUh9": { "sol": 4.21 }, }, "priorityFee": 0.000005007, "block": 401632295, "timestamp": 1771631430774 } ``` ```json { "signature": "5xqf14tscxYzW289huZCEnLoLycva2GWgNNjzv2sdr2ef54N38GZNBT6eKTsJ7vQTthcBQRdAJ6fojjZpwzPzXNn", "txType": "remove", "poolId": "5ypKmP8avEyja9L5wwBiCb7fFVNsow34BZMEutnvHCg5", "mint": "3pAFFgZDNXHxFNTKA1oQhb1xc13zvZcxHs97kSuTL6sc", "txSigner": "281oNgtPvWuDydiD53rV5x2C2pqCAjp9Y7ef2AwPCkbY", "tokenAmount": 684176482.1057463, "solAmount": 3.29323859, "tokensInPool": 0.001442371, "solInPool": 1e-9, "price": 6.933029019579567e-7, "marketCapSol": 693.3029019579567, "poolFeeRate": 0.003, "pool": "raydium-cpmm", "poolCreatedBy": "custom", "burnedLiquidity": "100%", "mintAuthority": "11111111111111111111111111111111", "freezeAuthority": "11111111111111111111111111111111", "tokenProgram": "spl-token-2022", "tokenExtensions": { "metadataPointer": {}, "tokenMetadata": {} }, "tradersInvolved": { "281oNgtPvWuDydiD53rV5x2C2pqCAjp9Y7ef2AwPCkbY": {} }, "postBalances": { "281oNgtPvWuDydiD53rV5x2C2pqCAjp9Y7ef2AwPCkbY": { "sol": 4.21 }, }, "priorityFee": 0.00002, "block": 388922045, "timestamp": 1766608876866 } ``` ```json { "signature": "nbMSuM43o881zf3532dc1VwoephsPVQET7qHWbtVkKsvUf8MPsnKmxGZXYvJv8eJWV2ri3GbnZw9XNdVybZ2ghP", "txType": "remove", "poolId": "4uaMaLTZzDu6sS6HrpeqJzyRNEJg8pEA4JsrhFijkE4T", "mint": "8tsjtkX1W1S8HgTGfZcgT89KSbwsvKunuGsgjbyLrzVY", "txSigner": "7XrX7jF4zzkKQDaDsbuC3pj83JDzRY3RPZk3yWvU5s82", "tokenAmount": 1745042.086581, "solAmount": 13.04651297, "tokensInPool": 2036334.162997, "solInPool": 0.171096754, "price": 8.402194350468797e-8, "marketCapSol": 84.02194350468797, "poolFeeRate": 0.0025, "pool": "meteora-damm-v1", "poolCreatedBy": "meteora-launchpad", "burnedLiquidity": "0%", "curveType": "ConstantProduct", "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "7XrX7jF4zzkKQDaDsbuC3pj83JDzRY3RPZk3yWvU5s82": {} }, "postBalances": { "7XrX7jF4zzkKQDaDsbuC3pj83JDzRY3RPZk3yWvU5s82": { "sol": 4.21 }, }, "priorityFee": 0.000545, "block": 400340374, "timestamp": 1771124606807 } ``` ```json { "signature": "hGJg8XvEhZD5f58Ax3yRhnXsHKkdzvTK5LjtKA4dGYAbqhATukV1rArPHSPDkUMJoDzKvzF25zky9o36iUn6wnC", "txType": "remove", "poolId": "tGTx97Xrp8tEgsK3nWBujLPB6T4DgMEtMaCaUs2fiha", "mint": "3VrfrhvXwD8XuDCDc6X9uPTHBsXw1teQdid2jY8wpump", "txSigner": "2YKdzgVQi4eZ5tE8RYr545TAwtnNPCP2mfrERAV36Y1g", "tokenAmount": 3326528.497957, "solAmount": 14.551444434, "tokensInPool": 990369614.27755, "solInPool": 0.000603407, "price": 6.092811216043966e-13, "marketCapSol": 0.0006092811216043966, "poolFeeRate": 0.001, "pool": "meteora-damm-v2", "poolCreatedBy": "meteora-launchpad", "burnedLiquidity": "10%", "minPrice": 5.421214630269583e-23, "maxPrice": 1.844605071373595e16, "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token", "tokenExtensions": {}, "tradersInvolved": { "2YKdzgVQi4eZ5tE8RYr545TAwtnNPCP2mfrERAV36Y1g": {} }, "postBalances": { "2YKdzgVQi4eZ5tE8RYr545TAwtnNPCP2mfrERAV36Y1g": { "sol": 4.21 }, }, "priorityFee": 0.00001, "block": 400343275, "timestamp": 1771125739619 } ``` #### Glossary[​](#glossary "Direct link to Glossary") | Field | Description | | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `signature` | Transaction signature on the Solana blockchain. | | `txType` | Transaction type — one of `transfer`, `create`, `buy`, `sell`, `migrate`, `createPool`, `add`, `remove`. | | `poolId` | is the unique pool address. By using it, you can see the pool reserves. Tip 1: You can get the Solana price without any external API. Just check if poolId equals Gf7sXMoP8iRw4iiXmJ1nq4vxcRycbGXy5RL8a8LnTd3v. This is the largest SOL–USDC pool on pump-amm, so you can read the Solana price from it. Tip 2: You can pass this address to the trade API using 'poolId': 'address' to trade a token from a specific pool. This is useful for arbitrage. If you do not pass this parameter, we automatically choose the best pool. This means you can even trade USDC at the best price using our trading API, without visiting any websites. | | `mint` | Token mint address. | | `quoteMint` | The second token used in the trade. We send this parameter only if quote token is NOT Solana. By default, quoteMint is Solana, so we do not send it. For example, in some pools you can buy a token only with USDC, USDT, or another token. In these cases, we send quoteMint, and its address. | | `txSigner` | The public key of the account that sent the transaction and paid the priorityFee. Important: if you build a copy trader or something similar, use tradersInvolved, not txSigner. This is because sometimes traders send a transaction where txSigner is a different account, but the trade uses funds from the account you track. In this case, the account you track will be in the tradersInvolved dictionary. | | `tokenAmount` | Amount of `mint` tokens involved in the transaction. | | `solAmount` | The amount of SOL involved in the transaction. This is returned only when the second (quote) token in the trading pair is Solana. If the quote token is something else (e.g., USDT or USDC), you will receive quoteAmount instead. | | `quoteAmount` | The amount of the `quoteMint` token involved in the transaction. This is returned only when the second token in the trading pair is not Solana. | | `tokensInPool` | Total amount of the main token (mint) currently in the liquidity pool. This shows how many tokens are available for trading on the token side. | | `solInPool` | Total amount of SOL currently locked in the liquidity pool. This field is present only when SOL is the quote token. It represents SOL liquidity available for trades. | | `quoteInPool` | Total amount of the quote token (quoteMint) locked in the liquidity pool. This field is present only when the quote token is NOT SOL (e.g. USDC, USDT). It represents liquidity on the quote side. | | `vTokensInBondingCurve` | Virtual token reserves in the bonding curve pool. Reflects liquidity available on the token side. This field is present only when the pool is `pump` or `raydium-launchpad`. | | `vSolInBondingCurve` | Virtual SOL reserves in the bonding curve pool. Reflects liquidity available on the SOL side. This field is present only when the pool is `pump` or `raydium-launchpad` and when quote token is Solana. If it's anything else (like USDC/USDT/etc.) you will get vQuoteInBondingCurve instead. | | `vQuoteInBondingCurve` | Virtual Quote reserves in the bonding curve pool. Reflects liquidity available on the Quote side. This field is present only when the pool is `pump` or `raydium-launchpad` and when quote token is NOT Solana (like USDC/USDT/etc.). If it's Solana you will get vSolInBondingCurve instead | | `price` | Price in quoteMint (usually SOL) including this transaction’s impact. | | `marketCapSol` | Market capitalization in SOL. We send this only when quote mint is Solana. | | `marketCapQuote` | Market capitalization in Quote. We send this only when quote mint is NOT Solana. (like USDC, USDT, etc.) | | `pool` | Liquidity source. Before migration, the value is either `pump` or `raydium-launchpad`. After migration, `pump` → `pump-amm`, `raydium-launchpad` → `raydium-cpmm`, `meteora-launchpad` → `meteora-damm-v1`/ `meteora-damm-v2`. | | minPrice | Available only in Meteora DAMM V2. Indicates the minimum price configured for the pool. | | maxPrice | Available only in Meteora DAMM V2. Indicates the maximum price configured for the pool. | | curveType | Present only in Meteora DAMM V1. Indicates the formula used for price calculation. Can be `constantProduct` (the most common one, based on the quote-reserve/base-reserve ratio) or `StableSwap` (ideal for stablecoin-to-stablecoin pools, keeping the price close to 1:1 even when reserves differ). | | poolFeeRate | Indicates the current fee rate in the pool. Can range from 0 to 1, where 0.1 represents 10% and 0.001 represents 1%. | | `poolCreatedBy` | indicates who created the pool on pump-amm, raydium-cpmm, meteora-damm-v1, or meteora-damm-v2. If the pool was created by migration, the value is `pump`, `raydium-launchpad`, or `meteora-launchpad`. Pools migrated from pump and raydium-launchpad are considered trusted, but pools migrated from meteora-launchpad can be risky because Meteora allows config creators to choose how much liquidity share the pool creator receives after migration. Always check `lockedLiquidityAfterMigration` before relying on such pools. If the pool was created manually, the value is `custom`. Be cautious with these pools as well. | | `lockedLiquidityAfterMigration` | Present only in Meteora Launchpad. Shows what percentage of liquidity will be locked after migration. Beware of values below 100%, as the pool or launchpad creator can withdraw the liquidity after the migration (rug pull). | | `poolFeeRateAfterMigration` | Present only in Meteora Launchpad. Indicates what the poolFeeRate will be after migration. Meteora Launchpad allows values up to 0.1 (10%) | | `migrationThreshholds` | Present only in Meteora Launchpad and Raydium Launchpad. Indicates the required `solInPool` or `quoteInPool` value for a migration to occur. Pump.fun always uses a fixed requirement of 85 SOL. | | `burnedLiquidity` | shows what percent of liquidity is burned (for example, `"99%"`). Liquidity means someone adds Solana (or another quote token) and tokens into the pool. Normally, liquidity providers earn fees from trades and can withdraw their share at any time. If one person owns 100% of the liquidity, they can withdraw 100% and drain the pool, even after others buy the token. Pools with 0% burned liquidity are very risky because the owner can rug pull, and you will not be able to sell the token. Launchpads like pump.fun and partly raydium-launchpad burn the right to withdraw liquidity after migration. So if a pool has 20–30% or more burned liquidity, it is a good sign, because this part will always stay in the pool for trading. | | `cashbackEnabled` | indicates whether you are accumulating cashback for trading in this pool. Such pools exist on pump.fun and pumpswap. To claim your accumulated cashback, call [claimCashback](/claim-cashback.md) | | `creatorFeeAddress` | It shows the address that receives the creator fees on pump.fun and pumpSwap if `cashbackEnabled` is `False`. Important: this address can change, since the real token creator has the right to modify it. Often, this address is the same as the one that originally created the token. The value may be `None` because not all pools have creator fees enabled. | | `mayhemMode` | Indicates whether “mayhem mode” is active for `pump` or `pump-amm` pools. `true` means mayhem mode is on, `false` means it’s off. (Other pool types don’t have this field.) | | `launchpadConfig` | The address where the settings for the Meteora or Raydium launchpad are stored. Anyone can create their own config on these launchpad. | | `mintAuthority` | Address that has permission to mint new tokens. This **must always be `None`** — otherwise it’s a red flag for a potential *scam*. Only stablecoins usually have a non-`None` value. [More info](/tutorials/detect_scam_tokens.md) | | `freezeAuthority` | Address that can freeze token accounts. This **must always be `None`** — a non-`None` value can indicate a *honeypot* or *scam*. Only stablecoins may have this set for regulatory reasons. [More info](/tutorials/detect_scam_tokens.md) | | `tokenProgram` | Token program used to create the token — either `spl-token` (legacy) or `spl-token-2022` (newer standard). [More info](/tutorials/detect_scam_tokens.md) | | `tokenExtensions` | Token extensions are only present in `spl-token-2022` tokens. Be cautious — some extensions can be used by scammers! (This does **not** apply to tokens from pump.fun, Bonk, or other trusted launchpads.) Check our [guide](/tutorials/detect_scam_tokens.md) to learn which extensions are safe. | | `tradersInvolved` | Addresses that **actually executed the trade** (spent funds). This field is critical for copy trading and analytics. A transaction may be **signed and paid for** by one address (`txSigner`), while the **trade itself is executed** using funds from another address. In such cases, `txSigner` shows the fee-payer, but `tradersInvolved` contains the real trader. Always track accounts by `tradersInvolved`, not `txSigner`, or you may miss trades. [Visualization](https://prnt.sc/0y5d2_tMLucE) | | `postBalances` | Shows SOL balances of accounts involved in the transaction. Includes: the `txSigner`, all `tradersInvolved`, and — in the case of SOL transfers — both the sender and the receiver. In the case of token transfers, only the SOL balance of the sender is shown; the SOL balance of the account that received the tokens is **not** included. | | `type` | It appears only in transfer events. It shows which method was used to make the transfer — for example, `token_account_closure` or `withdraw_from_nonce` | | `isSolana` | Only in transfer events. Indicates whether the `mint` represents Solana. `True` if the mint is **native SOL** or **WSOL (Wrapped SOL)**; otherwise `False`. | | `priorityFee` | Shows how much was paid to send this transaction. Minimum is 0.000005 SOL (base network fee) plus an additional tip to increase the likelihood that a validator includes the transaction. | | `block` | Block number in which the transaction was included. New blocks are produced every 400 ms | | `timestamp` | Blockchain timestamp of transaction. | --- ## Trade Api ### Buy and Sell – API Use this to buy and sell tokens.
**Supported pools** * `Pump.fun` * `Raydium LaunchPad (including bonk)` * `Meteora LaunchPad (including Bags.fm, moonshot)` * `PumpSwap` * `Raydium CPMM` * `Meteora DAMM V1` * `Meteora DAMM V2` #### Endpoint[​](#endpoint "Direct link to Endpoint") `POST https://api.pumpapi.io` * Local transactions (No private key required) * Lightning transactions⚡ (EASIER AND FASTER) #### How Local Transactions Work[​](#how-local-transactions-work "Direct link to How Local Transactions Work") Local transactions keep your private key **100% on your machine**. The flow is: 1. You send us a request with only your `publicKey` (no private key). 2. Our server builds the transaction and returns it to you **unsigned**, as raw bytes. 3. You sign the transaction locally on your machine. 4. You broadcast it to the Solana network yourself (via any RPC). This method is for users who prioritize **key security** over speed and simplicity. If you want the **fastest and simplest** path — where we sign and broadcast on your behalf — switch to the **⚡ Lightning transactions** tab above (recommended). *** #### Request Body[​](#request-body "Direct link to Request Body") | Field | Description | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `publicKey` | Your public key (the wallet address) | | `action` | `"buy"` or `"sell"` | | `mint` | Mint address of the token | | `quoteMint` | **Optional. Not required.** It works only if such pools exist. By default, the quote mint is `So11111111111111111111111111111111111111112`, so you don’t need to set it — we handle it automatically. But if you trade in pools where Solana is not the second token (for example, a Memecoin–USDC pair), then quoteMint should be the USDC address (or the address of any other quote token). | | `poolId` | **Optional. Not required.** provide this if you need to trade token from the specific pool. | | `amount` | Amount to trade. Use `'100%'` to sell all and get a 0.002 sol refund from the network | | `denominatedInQuote` | `"true"` if amount is in SOL (or any other quote token), `"false"` for token amount | | `slippage` | Slippage in percent (recommended: 20) | | `priorityFee` | Priority fee in SOL | | `partnerAddress` | Optional. Run your own service and want to receive a fee from your users? Set this field. The fee defined in `partnerFeeRatio` + `partnerFeeFixed` will be sent to this address.
You can also use it if your strategy requires sending funds somewhere after the operation. | | `partnerFeeRatio` | Optional. Percentage of the trade you want to send to `partnerAddress`. Example: `0.005` = 0.5%, `0.01` = 1%. | | `partnerFeeFixed` | Optional. A fixed amount (in SOL) you want to send to `partnerAddress` for the operation. Example: `0.0001`. | | `mintRef` | Optional. When using Jito Bundles or Actions and creating tokens, you don’t yet know the token address assigned to you (unless you provide mintPrivateKey). To handle this, within a single request you can set "mintRef": "any value" (the default is "0") and reuse it across related transactions inside the same Jito Bundle or Actions. When buying the token, specify "mintRef": "the value you set earlier", and the backend will understand which token you’re referring to. Works within a single request; a second request requires providing the mint address. | *** * Python * JavaScript * Rust * Go ```python import requests from solders.transaction import VersionedTransaction from solders.keypair import Keypair from solders.message import to_bytes_versioned from solders.commitment_config import CommitmentLevel from solders.rpc.requests import SendVersionedTransaction from solders.rpc.config import RpcSendTransactionConfig response = requests.post(url="https://api.pumpapi.io", json={ "publicKey": "your_public_key", "action": "buy", # or sell "mint": "token_address", "amount": 0.01, # When you're selling, you can pass "100%" to sell everything "denominatedInQuote": "true", "slippage": 20, "priorityFee": 0.0001, }) keypairs = [ Keypair.from_base58_string("your_private_key"), # stays on your computer # Keypair.from_base58_string("your_private_key_2"), # If you are using actions, add all private keys involved in the transaction here. ] tx = VersionedTransaction.from_bytes(response.content) required_signers = list(tx.message.account_keys)[:tx.message.header.num_required_signatures] signatures = list(tx.signatures) for keypair in keypairs: if keypair.pubkey() in required_signers: signer_index = required_signers.index(keypair.pubkey()) signatures[signer_index] = keypair.sign_message(to_bytes_versioned(tx.message)) tx = VersionedTransaction.populate(tx.message, signatures) commitment = CommitmentLevel.Confirmed config = RpcSendTransactionConfig(preflight_commitment=commitment) txPayload = SendVersionedTransaction(tx, config) response = requests.post( url="https://api.mainnet-beta.solana.com/", # it's better to use Helius RPC endpoint headers={"Content-Type": "application/json"}, data=SendVersionedTransaction(tx, config).to_json() ) txSignature = response.json()['result'] print(f'Transaction: https://solscan.io/tx/{txSignature}') ``` ```javascript import { Connection, Keypair, VersionedTransaction } from "@solana/web3.js"; import bs58 from "bs58"; const response = await fetch("https://api.pumpapi.io", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ publicKey: "your_public_key", action: "buy", // or sell mint: "token_address", amount: 0.01, // When you're selling, you can pass "100%" to sell everything denominatedInQuote: "true", slippage: 20, priorityFee: 0.0001, }), }); const keypairs = [ Keypair.fromSecretKey(bs58.decode("your_private_key")), // stays on your computer // Keypair.fromSecretKey(bs58.decode("your_private_key_2")), // If you are using actions, add all private keys involved in the transaction here. ]; const tx = VersionedTransaction.deserialize( new Uint8Array(await response.arrayBuffer()) ); const requiredSigners = tx.message.staticAccountKeys.slice( 0, tx.message.header.numRequiredSignatures ); const matchingKeypairs = keypairs.filter((kp) => requiredSigners.some((pk) => pk.equals(kp.publicKey)) ); tx.sign(matchingKeypairs); const commitment = "confirmed"; const web3Connection = new Connection( "https://api.mainnet-beta.solana.com/", // it's better to use Helius RPC endpoint commitment ); const txSignature = await web3Connection.sendTransaction(tx, { preflightCommitment: commitment, }); console.log(`Transaction: https://solscan.io/tx/${txSignature}`); ``` ```rust use base64::{engine::general_purpose::STANDARD, Engine as _}; use reqwest::Client; use serde_json::json; use solana_sdk::{ signature::{Keypair, Signer}, transaction::VersionedTransaction, }; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let response = client .post("https://api.pumpapi.io") .json(&json!({ "publicKey": "your_public_key", "action": "buy", // or sell "mint": "token_address", "amount": 0.01, // When you're selling, you can pass "100%" to sell everything "denominatedInQuote": "true", "slippage": 20, "priorityFee": 0.0001, })) .send() .await?; let tx_bytes = response.bytes().await?; let keypairs = vec![ Keypair::from_base58_string("your_private_key"), // stays on your computer // Keypair::from_base58_string("your_private_key_2"), // If you are using actions, add all private keys involved in the transaction here. ]; let mut tx: VersionedTransaction = bincode::deserialize(&tx_bytes)?; let required_signers = &tx.message.static_account_keys()[..tx.message.header().num_required_signatures as usize]; let message_bytes = tx.message.serialize(); for keypair in &keypairs { if let Some(signer_index) = required_signers .iter() .position(|pubkey| pubkey == &keypair.pubkey()) { tx.signatures[signer_index] = keypair.sign_message(&message_bytes); } } let commitment = "confirmed"; let rpc_payload = json!({ "jsonrpc": "2.0", "id": 1, "method": "sendTransaction", "params": [ STANDARD.encode(bincode::serialize(&tx)?), { "encoding": "base64", "preflightCommitment": commitment } ] }); let response = client .post("https://api.mainnet-beta.solana.com/") // it's better to use Helius RPC endpoint .header("Content-Type", "application/json") .json(&rpc_payload) .send() .await?; let tx_signature: serde_json::Value = response.json().await?; println!( "Transaction: https://solscan.io/tx/{}", tx_signature["result"].as_str().unwrap_or("") ); Ok(()) } ``` ```go package main import ( "bytes" "encoding/base64" "encoding/json" "fmt" "io" "log" "net/http" "github.com/gagliardetto/solana-go" ) func main() { payload := map[string]any{ "publicKey": "your_public_key", "action": "buy", // or sell "mint": "token_address", "amount": 0.01, // When you're selling, you can pass "100%" to sell everything "denominatedInQuote": "true", "slippage": 20, "priorityFee": 0.0001, } body, err := json.Marshal(payload) if err != nil { log.Fatal(err) } response, err := http.Post("https://api.pumpapi.io", "application/json", bytes.NewBuffer(body)) if err != nil { log.Fatal(err) } defer response.Body.Close() rawTx, err := io.ReadAll(response.Body) if err != nil { log.Fatal(err) } keypairs := []solana.PrivateKey{ solana.MustPrivateKeyFromBase58("your_private_key"), // stays on your computer // solana.MustPrivateKeyFromBase58("your_private_key_2"), // If you are using actions, add all private keys involved in the transaction here. } tx := new(solana.VersionedTransaction) if err := tx.UnmarshalBinary(rawTx); err != nil { log.Fatal(err) } _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { for i := range keypairs { if keypairs[i].PublicKey().Equals(key) { return &keypairs[i] } } return nil }) if err != nil { log.Fatal(err) } txBytes, err := tx.MarshalBinary() if err != nil { log.Fatal(err) } commitment := "confirmed" rpcPayload := map[string]any{ "jsonrpc": "2.0", "id": 1, "method": "sendTransaction", "params": []any{ base64.StdEncoding.EncodeToString(txBytes), map[string]any{ "encoding": "base64", "preflightCommitment": commitment, }, }, } rpcBody, err := json.Marshal(rpcPayload) if err != nil { log.Fatal(err) } response, err = http.Post( "https://api.mainnet-beta.solana.com/", // it's better to use Helius RPC endpoint "application/json", bytes.NewBuffer(rpcBody), ) if err != nil { log.Fatal(err) } defer response.Body.Close() var result struct { Result string `json:"result"` } if err := json.NewDecoder(response.Body).Decode(&result); err != nil { log.Fatal(err) } fmt.Printf("Transaction: https://solscan.io/tx/%s\n", result.Result) } ``` *** #### Response Format[​](#response-format "Direct link to Response Format") Local transactions return the **raw, unsigned transaction bytes** — not JSON. You deserialize them, sign locally, and broadcast yourself. | Request type | Response | | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | | **Single transaction** (regular `buy` / `sell` / `create` / Actions) | One raw `VersionedTransaction` in the response body | | **[Jito Bundle](/jito-bundles.md)** (multiple transactions inside `"transactions": [...]`) | An array of raw base64 `VersionedTransaction` txs — one per transaction in the bundle (up to 5) | tip Want cleaner code and faster execution? Use our ⚡ Lightning transactions *** #### ⚡ Lightning Transaction[​](#-lightning-transaction "Direct link to ⚡ Lightning Transaction") **Lightning transaction** is a method of sending transactions where we broadcast the transaction on your behalf.
This approach allows for: * 🚀 **Maximum transaction speed** * 🧩 **Minimal code complexity** By handling the transaction process internally, you don’t have to construct the transaction manually — we do it for you. > To use this feature, simply provide a wallet `privateKey` — it’s needed to sign and broadcast the transaction on your behalf. #### Request Body[​](#request-body-1 "Direct link to Request Body") | Field | Description | | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `privateKey` | Private key in Base58 format | | `action` | `"buy"` or `"sell"` | | `mint` | Mint address of the token | | `quoteMint` | **Optional. Not required.** It works only if such pools exist. By default, the quote mint is Solana -> `So11111111111111111111111111111111111111112`, so you don’t need to set it — we handle it automatically. But if you trade in pools where Solana is not the second token (for example, a Memecoin–USDC pair), then quoteMint should be the USDC address (or the address of any other quote token). | | `poolId` | **Optional. Not required.** provide this if you need to trade token from the specific pool. | | `amount` | Amount to trade. Use `'100%'` to sell all and get a 0.002 sol refund from the network | | `denominatedInQuote` | `"true"` if amount is in SOL (or any other quote token), `"false"` for token amount | | `slippage` | Slippage in percent (recommended: 20) | | `priorityFee` | Optional extra fee (in SOL) to speed up your transaction and increase its chance of landing.

There are two modes of operation:

**1️⃣ Automatic Jito split (≥ 0.00023 SOL):**
If the value is **0.00023 SOL or higher**, PumpAPI automatically splits it:
• **90% → `jitoTip`** (used by ~90% of validators)
• **10% → `priorityFee`** (for non-Jito validators)

**2️⃣ No split (< 0.00023 SOL):**
If the value is **less than 0.00023 SOL**, *no split occurs*. The full amount is treated as `priorityFee`, and the transaction is sent via **SQWOS** (fast non-Jito route).

Minimum allowed value: **10,000 lamports (0.00001 SOL)**.

**Dynamic options:** you can also let PumpAPI auto-adjust fees dynamically based on recent network data.
• `'auto'` / `'auto-95'` → ~95% success (~0.015 SOL ≈ $3–4)
• `'auto-75'` → ~75% success (~0.00113 SOL ≈ $0.2–0.3)
• `'auto-50'` → ~50% success (~0.00022 SOL ≈ $0.04)

In this mode, only the `priorityFee` is adjusted automatically — **`jitoTip` is not included or estimated**. If you want to use Jito alongside dynamic fees, you can still **set `jitoTip` manually** (see below).

⚠️ **Note:** During heavy network congestion, fees for ~75/95% success can rise significantly — even **$10 per transaction**. The values above reflect the state at the time of writing. To save costs and have big chances to land the transaction, consider using the **minimum fee (0.00001 SOL)** together with **`guaranteedDelivery: true`** (see below). | | `maxPriorityFee` | **Optional.** Sets the maximum `priorityFee` (in SOL) for `auto` / `auto-*` modes — it will never go above this value (example: `0.01`). | | `jitoTip` | Optional. Use this if you don’t want automatic priorityFee split.
Example: `'jitoTip': 0.0002`.
Minimum required to join Jito auction: **0.0002 SOL**. Anything below that is ignored, and the transaction is sent without Jito participation. When `jitoTip` is provided, your entire `priorityFee` remains intact (not split). | | `guaranteedDelivery` | **Optional experimental feature** `"true"` tells the server to rebroadcast the transaction for up to 10 seconds and respond with `confirmed: true` if it appears on-chain within that time. Otherwise, you receive `confirmed: false`. ⚠️ This affects response time: if you want an immediate reply (without confirmation of success), set this to false! | | `partnerAddress` | Optional. Run your own service and want to receive a fee from your users? Set this field. The fee defined in `partnerFeeRatio` + `partnerFeeFixed` will be sent to this address.
You can also use it if your strategy requires sending funds somewhere after the operation. | | `partnerFeeRatio` | Optional. Percentage of the trade you want to send to `partnerAddress`. Example: `0.005` = 0.5%, `0.01` = 1%. | | `partnerFeeFixed` | Optional. A fixed amount (in SOL) you want to send to `partnerAddress` for the operation. Example: `0.0001`. | | `mintRef` | Optional. When using Jito Bundles or Actions and creating tokens, you don’t yet know the token address assigned to you (unless you provide mintPrivateKey). To handle this, within a single request you can set "mintRef": "any value" (the default is "0") and reuse it across related transactions inside the same Jito Bundle or Actions. When buying the token, specify "mintRef": "the value you set earlier", and the backend will understand which token you’re referring to. Works within a single request; a second request requires providing the mint address. | *** * Python * JavaScript * Rust * Go ```python import requests url = "https://api.pumpapi.io" data = { "privateKey": "your_private_key", "action": "buy", "mint": "token_address", "amount": 0.01, "denominatedInQuote": "true", "slippage": 20, "priorityFee": 0.0001, } response = requests.post(url, json=data) print(response.json()) ``` ```javascript import axios from 'axios'; const data = { privateKey: "your_private_key", action: "buy", mint: "token_address", amount: 0.01, denominatedInQuote: "true", slippage: 20, priorityFee: 0.0001 }; axios.post('https://api.pumpapi.io', data) .then(response => console.log(response.data)) .catch(error => console.error(error)); ``` ```rust use reqwest::Client; use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let res = client.post("https://api.pumpapi.io") .json(&json!({ "privateKey": "your_private_key", "action": "buy", "mint": "token_address", "amount": 0.01, "denominatedInQuote": "true", "slippage": 20, "priorityFee": 0.0001 })) .send() .await? .text() .await?; println!("{}", res); Ok(()) } ``` ```go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "privateKey": "your_private_key", "action": "buy", "mint": "token_address", "amount": 0.01, "denominatedInQuote": "true", "slippage": 20, "priorityFee": 0.0001, } jsonData, _ := json.Marshal(data) resp, err := http.Post("https://api.pumpapi.io", "application/json", bytes.NewBuffer(jsonData)) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Println(result) } ``` *** #### Response Format[​](#response-format-1 "Direct link to Response Format") | Request type | Response | | ------------------------------------------------- | ------------------------------------------- | | **Single transaction** | `{"signature": "...", "err": ""}` | | **[Jito Bundle](/jito-bundles.md)** (up to 5 txs) | `{"signatures": ["...", "..."], "err": ""}` | `err` is an **empty string `""` on success**, or the error message on failure. Extra fields may appear depending on the action (e.g. `createdMints` when creating tokens). *** Need help? Join our [Telegram group](https://t.me/pumpapi_devs). --- ## Transfer ### Transfer Use this to **send native SOL or SPL tokens** to another wallet. * If `mint` is **not provided**, the API sends **native SOL**. * If `mint: "token_address"` or `"mintRef": "value"` is provided, the API sends the specified **token** instead. * You can also pass `memo` as an optional field. It lets you attach a message that will be visible in Solana explorers. `amount` can be either a numeric value (for example `0.000001`) or a percentage string such as `"100%"` (transfer the full balance). The example below shows a **native SOL transfer**.
Uncomment the `"mint": "token_address"` line if you want to transfer tokens instead. > **Local transactions are supported**
For a local-transaction example, visit the **[Trade API](/trade-api.md)** page and reuse the same local-transaction code snippets — the flow works the same way here.
The code snippets below show **lightning transactions**. #### Endpoint[​](#endpoint "Direct link to Endpoint") `POST https://api.pumpapi.io` #### Request Body[​](#request-body "Direct link to Request Body") | Field | Description | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------ | | `privateKey` | Base58 Private key of the wallet that will sign the transfer transaction | | `action` | Must be **"transfer"** | | `to` | Recipient wallet address. Example: `pump22QQQnff5qmAxW7yg6VEKU3C7Mj8C4TDaXJZt9Q` | | `amount` | Amount to send. You can pass a numeric value such as `0.000001` or a percentage string such as `"100%"` to transfer the full balance | | `mint` | Optional. Token mint address. If omitted, native SOL is sent | | `mintRef` | Optional. Temporary token reference for [token creations](/create-token-pump-fun.md) | | `memo` | Optional. A memo string that will be visible in Solana explorers | #### 📦 Code Examples[​](#-code-examples "Direct link to 📦 Code Examples") * Python * JavaScript * Rust * Go ```python import requests url = "https://api.pumpapi.io" data = { "privateKey": "base58_private_key", "action": "transfer", "to": "recipient_public_key", "amount": 0.000001, # You can also use a percentage, for example "100%" to transfer everything # "mint": "token_address", # Add this if you want to transfer tokens # "memo": "Hello from PumpAPI", # Optional memo visible in Solana explorers } response = requests.post(url, json=data) print(response.json()) ``` ```javascript import axios from "axios"; const url = "https://api.pumpapi.io"; const data = { privateKey: "base58_private_key", action: "transfer", to: "recipient_public_key", amount: 0.000001, // You can also use a percentage, for example "100%" to transfer everything // mint: "token_address", // Add this if you want to transfer tokens // memo: "Hello from PumpAPI", // Optional memo visible in Solana explorers }; axios .post(url, data) .then((res) => console.log(res.data)) .catch((err) => console.error(err)); ``` ```rust use reqwest::Client; use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let res = client .post("https://api.pumpapi.io") .json(&json!({ "privateKey": "base58_private_key", "action": "transfer", "to": "recipient_public_key", "amount": 0.000001 // "mint": "token_address", // Add this if you want to transfer tokens // "memo": "Hello from PumpAPI" // Optional memo visible in Solana explorers })) .send() .await? .text() .await?; println!("{}", res); Ok(()) } ``` ```go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { url := "https://api.pumpapi.io" data := map[string]any{ "privateKey": "base58_private_key", "action": "transfer", "to": "recipient_public_key", "amount": 0.000001, // You can also use a percentage, for example "100%" to transfer everything // "mint": "token_address", // Add this if you want to transfer tokens // "memo": "Hello from PumpAPI", // Optional memo visible in Solana explorers } jsonData, _ := json.Marshal(data) resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) if err != nil { panic(err) } defer resp.Body.Close() var result any _ = json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%v\n", result) } ``` --- ## Tutorials ### Detecting Scam Pools In this guide, we'll teach you how to identify scam liquidity pools and protect yourself from rug pulls. Important Note Even if a token passes all security checks from our [token scam detection guide](/tutorials/detect_scam_tokens.md), you can still lose money if you're trading in the wrong pool. Always verify the pool before trading! #### Anyone Can Create a Pool[​](#anyone-can-create-a-pool "Direct link to Anyone Can Create a Pool") Here's something many traders don't realize: **anyone can create a liquidity pool for any token**. Even if a token was originally launched on pump.fun, nothing prevents someone from: 1. Buying that token 2. Creating their own pool on PumpSwap, Raydium, Meteora, or any other DEX 3. Waiting for unsuspecting traders to buy from their malicious pool This means you might accidentally analyze and trade a legitimate token, but in a scam pool created by a bad actor. Always Check the Pool Before trading, always verify which pool you're interacting with. The token might be fine, but the pool could be a trap. #### Pool Verification[​](#pool-verification "Direct link to Pool Verification") ##### Checking Pool Origin[​](#checking-pool-origin "Direct link to Checking Pool Origin") Let's focus on the most common case: you only want to trade tokens related to pump.fun, raydium launchpad, meteora launchpad ecosystem. Here's how to verify the pool using our API response: | Pool Type | What to Check | Safe Condition | | --------------------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `pump` | Nothing extra needed | ✅ Always safe (bonding curve) | | `pump-amm` | Check `poolCreatedBy` | ✅ Safe if `poolCreatedBy` = `"pump"` | | `raydium-cpmm` | Check `poolCreatedBy` | ✅ Safe if `poolCreatedBy` = `"raydium-launchpad"` | | `meteora-damm-v1/meteora-damm-v2` | Check `poolCreatedBy` | ✅ Safe if `poolCreatedBy` = `"meteora-launchpad"` and `"burnedLiquidity"` = 100% (meteora launchpad allows to choose creator’s liquidity share, so they can remove liquidity if it's less than 100%) | | Any pool | Check `poolCreatedBy` | ⚠️ Be careful if `poolCreatedBy` = `"custom"` | Quick Rule If you want a simple rule: avoid any pool where `poolCreatedBy` equals `"custom"` unless you know exactly what you're doing. ##### Example: Safe Pool[​](#example-safe-pool "Direct link to Example: Safe Pool") ```json { "pool": "pump-amm", "poolCreatedBy": "pump", // ✅ Created by pump.fun migration - SAFE "burnedLiquidity": "100%" } ``` ##### Example: Suspicious Pool[​](#example-suspicious-pool "Direct link to Example: Suspicious Pool") ```json { "pool": "pump-amm", "poolCreatedBy": "custom", // ⚠️ Created manually by someone - BE CAREFUL "burnedLiquidity": "0%" } ``` #### Understanding Liquidity and Rug Pulls[​](#understanding-liquidity-and-rug-pulls "Direct link to Understanding Liquidity and Rug Pulls") ##### What is Liquidity?[​](#what-is-liquidity "Direct link to What is Liquidity?") A liquidity pool is essentially a smart contract holding two assets (e.g., SOL and a token). When you buy a token, you add SOL to the pool and remove tokens. When you sell, you do the opposite. **Liquidity providers** are people who deposit both assets into the pool. In return, they receive LP (Liquidity Provider) tokens representing their share of the pool. They earn trading fees but can also withdraw their liquidity at any time. ##### How Rug Pulls Work[​](#how-rug-pulls-work "Direct link to How Rug Pulls Work") Here's a step-by-step example of a classic liquidity rug pull: **Step 1: Scammer creates a pool** ```text Pool initial state: ├── SOL: 200 ├── Tokens: 20,000,000 └── Scammer owns: 100% of LP tokens ``` **Step 2: Victim buys tokens** ```text Victim sends: 70 SOL Victim receives: 10,000,000 tokens Pool after purchase: ├── SOL: 270 ├── Tokens: 10,000,000 └── Scammer still owns: 100% of LP tokens ``` **Step 3: Scammer withdraws all liquidity** ```text Scammer withdraws 100% of LP tokens and receives: ├── SOL: 270 (including victim's 70 SOL!) └── Tokens: 10,000,000 Pool after rug pull: ├── SOL: 0 └── Tokens: 0 ``` **Result:** The victim is left with 10,000,000 tokens that cannot be sold because there's no liquidity in the pool. The scammer walks away with 270 SOL (200 initial + 70 from victim). #### The burnedLiquidity Parameter[​](#the-burnedliquidity-parameter "Direct link to The burnedLiquidity Parameter") ##### What Does It Mean?[​](#what-does-it-mean "Direct link to What Does It Mean?") `burnedLiquidity` shows what percentage of LP tokens have been permanently destroyed (burned). Burned LP tokens can never be used to withdraw liquidity. | burnedLiquidity | Meaning | | --------------- | ----------------------------------------------------------------------- | | `"100%"` | All liquidity is permanently locked. Nobody can rug pull. | | `"50%"` | Half of liquidity is locked. Even if all providers withdraw, 50% stays. | | `"0%"` | No liquidity is locked. Pool can be completely drained. | Rule of Thumb Higher `burnedLiquidity` = Safer pool. When 100% is burned, the pool will always have liquidity for trading, no matter what. ##### Why Do People Provide Liquidity?[​](#why-do-people-provide-liquidity "Direct link to Why Do People Provide Liquidity?") Liquidity providers earn a percentage of every trade that happens in the pool. This can be a legitimate way to earn passive income. However, scammers abuse this system by creating pools with the intention of withdrawing all liquidity after others have bought in. When pump.fun migrates a token to pump-amm, it burns the LP tokens, which is why `poolCreatedBy: "pump"` pools are safe. #### When 0% burnedLiquidity is Acceptable[​](#when-0-burnedliquidity-is-acceptable "Direct link to When 0% burnedLiquidity is Acceptable") Not every pool with `"0%"` burned liquidity is a scam. Here are legitimate scenarios: ##### 1. Old, Established Pools[​](#1-old-established-pools "Direct link to 1. Old, Established Pools") Some older pools with well-known tokens have multiple liquidity providers. The ownership is diluted across many participants, so no single entity controls enough to drain the pool. ##### 2. Stablecoin and Major Token Pools[​](#2-stablecoin-and-major-token-pools "Direct link to 2. Stablecoin and Major Token Pools") If you're buying USDC, USDT, or other major tokens, the `burnedLiquidity` parameter matters less. Why? Because even if something happens to one pool, you can always sell these tokens in hundreds of other pools. ##### 3. You Don't Plan to Sell in This Pool[​](#3-you-dont-plan-to-sell-in-this-pool "Direct link to 3. You Don't Plan to Sell in This Pool") If you're using a pool purely for buying and plan to sell elsewhere (for example, arbitrage), pool safety matters less. Just be aware of the risks. #### Check poolFeeRate Carefully[​](#check-poolfeerate-carefully "Direct link to Check poolFeeRate Carefully") When working with Meteora Launchpad, Meteora DAMM V1, and Meteora DAMM V2, always verify the `poolFeeRate` parameter. Meteora allows pool creators to set fees up to 99%. All trading fees go to LP providers — which often means the pool creator. This makes fee verification critical. * `poolFeeRate = 0.0025` → 0.25% fee (normal and reasonable) * `poolFeeRate = 0.5` → 50% fee (extremely dangerous) Also check `poolFeeRateAfterMigration` for Meteora Launchpad pools. A token might look fine at launch but migrate to a pool with a 10% fee. You don’t want to discover that only when trying to sell. #### Best Practices for Pool Verification[​](#best-practices-for-pool-verification "Direct link to Best Practices for Pool Verification") When checking pools through our API, follow this checklist: 1. ✅ Check `pool` type 2. ✅ If `pool` is `pump-amm`, verify `poolCreatedBy` is `"pump"` 3. ✅ If `pool` is `raydium-cpmm`, verify `poolCreatedBy` is `"raydium-launchpad"` 4. ✅ If `pool` is `meteora-damm-v1` or `meteora-damm-v2`, verify `poolCreatedBy` is `"meteora-launchpad"` and `burnedLiquidity` is `100%` and `"poolFeeRate"` is less than `0.001` 5. ✅ Avoid pools where `poolCreatedBy` is `"custom"` unless you have good reasons 6. ✅ Check `burnedLiquidity` — prefer pools with high percentages 7. ✅ If `burnedLiquidity` is `"0%"`, only proceed if you understand the risks Simplest Check If you don't want to deal with complex verification logic, just use this simple rule: ```python if trade_event.get('poolCreatedBy') in ('pump', 'raydium-launchpad'): # Safe to trade ``` This single check filters out all manually created pools, leaving only pools created by trusted launchpads (pump.fun, raydium-launchpad). #### Summary[​](#summary "Direct link to Summary") | Check | Safe✅ | Risky🚨 | | --------------- | ------------------------------------------------ | --------------------------------- | | Pool type | `pump`, `raydium-launchpad`, `meteora-launchpad` | — | | poolCreatedBy | `"pump"`, `"raydium-launchpad"` | `"custom"`, `"meteora-launchpad"` | | burnedLiquidity | 50%+ | 0% | Good luck! --- ### Detecting Scam Tokens In this guide, we'll teach you how to use our API to identify scam tokens that have been added to Pump AMM and Raydium CPMM pools. Important Note This guide is specifically for tokens in Pump AMM and Raydium CPMM pools. For pump.fun and Bonk pools, this verification is not necessary because these launchpads are designed to ensure trust - you can be confident that no one will block your tokens or steal your money. The only risk on such launchpads is price volatility (you may lose money, but this is not a technical scam). #### What is Technical Scam?[​](#what-is-technical-scam "Direct link to What is Technical Scam?") We're talking about **technical scam** - when you lose money not because of price fluctuations, but because of the technical capabilities built into Solana tokens. This is fundamentally different from market risk. #### Understanding Token Programs[​](#understanding-token-programs "Direct link to Understanding Token Programs") There are two token programs from Solana: * **spl-token** - the older but still popular version * **spl-token-2022** - the newer version with extended capabilities Every token you see on the Solana network belongs to one of these programs, and you can check this on Solscan. Older pump.fun tokens were issued on `spl-token`, and now there's a transition period to `spl-token-2022`. Key Concept Every memecoin belongs to one of these token programs. There can be no other option. (Technically, you can create your own program for token creation, but no one will recognize it - all pools only support SPL programs, and you cannot insert a token with custom code into a pool, meaning it cannot be traded). #### Example Trade Event[​](#example-trade-event "Direct link to Example Trade Event") Here's what a trading event from our Datastream looks like: ```json { "signature": "3UdtzNBG132b6nLveihyxMws5GRajx7htHPvKwEuVv63oGvQkX4mb35GNEDQupLxGd2LyrWGtEy7gFKs4aaP9WJN", "txType": "buy", "poolId": "EJUWjjFF9RcDM6NdRH84aWhzFBCybYtcKZVSbspLtWh9", "mint": "EhzVcKKmGjLk6pD5gLT6ZrTg62bMgPgTSCXXmANnSyQA", "txSigner": "9U4xnxR5XEk2abTyVgG6rKR6qYmoLtiRbf7yjbs5vHaW", "tokenAmount": 1860.672874774, "solAmount": 0.047396068, "tokensInPool": 13552473.051321916, "solInPool": 344.400289535, "price": 0.00002541235745172037, "marketCapSol": 3471.1024169955463, "pool": "raydium-cpmm", "poolCreatedBy": "custom", "burnedLiquidity": "100%", "mintAuthority": None, "freezeAuthority": None, "tokenProgram": "spl-token-2022", "tokenExtensions": { "transferFeeConfig": {}, <----- transferFeeConfig is VERY DANGEROUS, and probably the most common scam in the spl-token-2022 "metadataPointer": {}, "tokenMetadata": {} }, "tradersInvolved": { "9U4xnxR5XEk2abTyVgG6rKR6qYmoLtiRbf7yjbs5vHaW": {} }, "block": 388975146, "timestamp": 1766629721399 } ``` #### SPL-Token Security Checks[​](#spl-token-security-checks "Direct link to SPL-Token Security Checks") For `spl-token`, there are only 2 types of potential technical scams: ##### 1. mintAuthority[​](#1-mintauthority "Direct link to 1. mintAuthority") A legitimate token, after minting tokens (for example, 1 billion new tokens), should disable `mintAuthority` by setting it to `None` (this is what launchpads do, and you can trust them). **⚠️ Warning:** If the `mintAuthority` field contains an address instead of `None`, it means that more tokens can be minted at any time, diluting your holdings. ##### 2. freezeAuthority[​](#2-freezeauthority "Direct link to 2. freezeAuthority") A legitimate token should NOT have `freezeAuthority` (value should be `None`). If there's an address in this field, the token is most likely a **SCAM** and can freeze your tokens at any moment. Exception for Stablecoins This is normal only for stablecoins like USDC, USDT, and others, because regulators require them to have the ability to block funds in accounts. Therefore, theoretically, any of your USDC can be frozen at any moment. For example, here's a [frozen account transaction attempt](https://solscan.io/tx/2NFRdteSJzWEyQqhLEuC1HT3TZqELBDc3h9QwENVJC48vXYw1cgRKkbDq17jAHghuHMHHEpZSeuaQ1rzznZ6A1eq) we found from the USDC freeze authority. Open Program Logs and search the page for "account is frozen" and you'll see that the transaction fails not because of insufficient funds, but because the funds are blocked. This is where the possibilities for abuse on `spl-token` end. #### SPL-Token-2022 and Token Extensions[​](#spl-token-2022-and-token-extensions "Direct link to SPL-Token-2022 and Token Extensions") `spl-token-2022` offers all the same features as `spl-token`, but adds **token extensions** on top. These extensions can bring both benefits and harm. Below is a table ranking token extensions from safe and harmless to dangerous. If a token is not from a well-known company and has a dangerous extension, the probability of it being a scam is almost 100%. #### Token Extensions Safety Reference[​](#token-extensions-safety-reference "Direct link to Token Extensions Safety Reference") ##### ✅ Safe Extensions[​](#-safe-extensions "Direct link to ✅ Safe Extensions") These extensions are cosmetic or security-enhancing and do not affect your ability to trade: | Extension | Description | Safety Level | | ------------------------- | ----------------------------------------------------------------------------------------- | ------------ | | **metadataPointer** | Points to token metadata | ✅ Safe | | **tokenMetadata** | Contains token metadata information | ✅ Safe | | **groupPointer** | Points to token group | ✅ Safe | | **groupMemberPointer** | Points to group member | ✅ Safe | | **tokenGroup** | Groups related tokens | ✅ Safe | | **tokenGroupMember** | Member of a token group | ✅ Safe | | **scaledUiAmount** | Changes UI display only | ✅ Safe | | **interestBearingConfig** | Cosmetic interest rate display | ✅ Safe | | **mintCloseAuthority** | Authority to close mint account (they can't close it if anybody still have token balance) | ✅ Safe | ##### 🚨 Dangerous Extensions[​](#-dangerous-extensions "Direct link to 🚨 Dangerous Extensions") These extensions can prevent you from selling or result in loss of funds: | Extension | Description | Danger Level | | --------------------------------- | --------------------------------------------------- | ------------ | | **memoTransfer** | Requires memo for transfers | 🚨 High Risk | | **transferFeeConfig** | Charges fees on transfers | 🚨 High Risk | | **transferHook** | Custom code runs on transfers | 🚨 High Risk | | **permanentDelegate** | Permanent control over tokens | 🚨 High Risk | | **defaultAccountState** | Can set accounts to frozen by default | 🚨 High Risk | | **nonTransferable** | Tokens cannot be transferred | 🚨 High Risk | | **confidentialTransferMint** | Confidential transfers (incompatible with pools) | 🚨 High Risk | | **confidentialMintBurn** | Confidential minting (incompatible with pools) | 🚨 High Risk | | **confidentialTransferFeeConfig** | Confidential transfer fee (incompatible with pools) | 🚨 High Risk | | **pausableConfig** | Can pause all transfers | 🚨 High Risk | | **cpiGuard** | Your program will stop working | 🚨 High Risk | #### Best Practices[​](#best-practices "Direct link to Best Practices") When checking tokens through our API, always verify: 1. ✅ `mintAuthority` is `None` 2. ✅ `freezeAuthority` is `None` (unless it's a known stablecoin) 3. ✅ If `spl-token-2022`, check `tokenExtensions` array 4. ✅ Avoid tokens with dangerous extensions unless from trusted sources Stay safe and always verify token before trading! --- ## Wrap Sol ### Wrap SOL The wrapSol action converts native SOL into Wrapped SOL (wSOL) — an SPL token version of SOL. If you need to convert it back, just buy or sell any token on pump-amm. This will automatically close the wSOL token account and return everything back to SOL. *** #### Endpoint[​](#endpoint "Direct link to Endpoint") `POST https://api.pumpapi.io` #### Request Fields[​](#request-fields "Direct link to Request Fields") | Field | Description | | ------------- | -------------------------------------- | | `privateKey` | Your wallet private key (base58) | | `action` | Must be `"wrapSol"` | | `amount` | Amount of SOL to wrap (e.g. `"0.5"`) | | `priorityFee` | Priority fee in SOL (e.g. `"0.00002"`) | *** #### Code Examples[​](#code-examples "Direct link to Code Examples") * Python * JavaScript * Go * Rust ```python import aiohttp import asyncio async def wrap_sol(): url = "https://api.pumpapi.io" data = { "privateKey": "base58_private_key", "action": "wrapSol", "amount": "0.5", "priorityFee": "0.00002", } async with aiohttp.ClientSession() as session: async with session.post(url, json=data) as response: result = await response.json() print(result) asyncio.run(wrap_sol()) ``` ```javascript const response = await fetch("https://api.pumpapi.io", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ privateKey: "base58_private_key", action: "wrapSol", amount: "0.5", priorityFee: "0.00002", }), }); const result = await response.json(); console.log(result); ``` ```go package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { payload := map[string]string{ "privateKey": "base58_private_key", "action": "wrapSol", "amount": "0.5", "priorityFee": "0.00002", } body, _ := json.Marshal(payload) resp, err := http.Post("https://api.pumpapi.io", "application/json", bytes.NewBuffer(body)) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Println(result) } ``` ```rust use reqwest::Client; use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let payload = json!({ "privateKey": "base58_private_key", "action": "wrapSol", "amount": "0.5", "priorityFee": "0.00002" }); let res = client .post("https://api.pumpapi.io") .json(&payload) .send() .await?; println!("{}", res.text().await?); Ok(()) } ``` *** Need help? Join our [Telegram group](https://t.me/pumpapi_devs). ---