Buying and Selling Tokens
Buying and Selling Tokens
The buy_token() and sell_token() methods let you trade Solana tokens for SOL through the PumpPortal trading API. These methods handle the complete transaction lifecycle, from building the transaction with your private key to submitting it to the Solana blockchain.
Warning: These methods execute real blockchain transactions with real money. Always test with small amounts first and verify all parameters carefully before executing trades.
Prerequisites
1. Install required dependencies
pip install solders
The buy_token() and sell_token() methods require the solders library for Solana transaction handling.
2. Authentication setup
from axiomtradeapi.client import AxiomTradeClient
# Initialize client with authentication
client = AxiomTradeClient()
# If using environment variables (.env file):
# AXIOM_ACCESS_TOKEN=your_access_token
# AXIOM_REFRESH_TOKEN=your_refresh_token
# Or login manually:
# client.login("username", "password")
See the Authentication overview for details.
3. Obtain your private key
Your wallet private key should be in base58 format. You can export it from:
- Phantom wallet (Settings -> Show Private Key)
- Solflare wallet (Settings -> Export Private Key)
- Other Solana wallets with export functionality
Store private keys in environment variables or a secure key management system — never commit them to version control.
Buying Tokens
Method signature
def buy_token(
self,
private_key: str,
token_mint: str,
amount: float,
slippage_percent: float = 10,
priority_fee: float = 0.005,
pool: str = "auto",
denominated_in_sol: bool = True,
rpc_url: str = "https://api.mainnet-beta.solana.com/"
) -> Dict[str, Union[str, bool]]Parameters
Required
private_key(str) - Your wallet’s private key encoded as a base58 string (not hex, not array format). Never commit private keys to version control or share them.token_mint(str) - The token contract (mint) address you want to purchase, base58 encoded. Token addresses can be found on Solscan, DEX Screener, or from new pair WebSocket events.amount(float) - The amount to trade. Interpretation depends ondenominated_in_sol:- If
denominated_in_sol=True: amount of SOL to spend (e.g.0.1= 0.1 SOL) - If
denominated_in_sol=False: amount of tokens to receive (e.g.1000= 1000 tokens)
- If
Optional
slippage_percent(float, default10) - Maximum price slippage tolerance as a percentage. Lower values (1-5%) may cause transactions to fail on volatile tokens; higher values (15-50%) are more likely to execute but risk more price impact. Use 10-15% for most tokens, 20-50% for highly volatile meme coins.priority_fee(float, default0.005) - Priority fee in SOL paid to Solana validators to process your transaction faster. Normal times: 0.001-0.005 SOL; high congestion: 0.01-0.05 SOL; critical/time-sensitive: 0.1+ SOL.pool(str, default"auto") - Specific exchange/pool to trade on:"auto"- automatically selects the best pool (recommended)"pump"- pump.fun exchange"raydium"- Raydium DEX"pump-amm"- pump.fun AMM"launchlab"- LaunchLab"raydium-cpmm"- Raydium CPMM"bonk"- Bonk Swap
denominated_in_sol(bool, defaultTrue) - Whetheramountrepresents SOL to spend (True) or tokens to receive (False).rpc_url(str, default"https://api.mainnet-beta.solana.com/") - Solana RPC endpoint used for transaction submission. Premium RPCs (Helius, QuickNode, Triton, etc.) offer faster processing and higher reliability than the default public endpoint, e.g."https://mainnet.helius-rpc.com/?api-key=YOUR_KEY".
Return value
Success response:
{
"success": True,
"signature": "5nh7w9kM2pqW...transaction_signature",
"transactionId": "5nh7w9kM2pqW...transaction_signature",
"explorer_url": "https://solscan.io/tx/5nh7w9kM2pqW..."
}
Error response:
{
"success": False,
"error": "Error description message"
}
Fields:
success(bool) - Whether the transaction was successfulsignature(str) - Transaction signature (hash/ID)transactionId(str) - Duplicate of signature for compatibilityexplorer_url(str) - Direct link to view the transaction on Solscanerror(str) - Error message if the transaction failed
Basic usage examples
Simple token purchase - spend a fixed amount of SOL:
from axiomtradeapi.client import AxiomTradeClient
import os
client = AxiomTradeClient()
private_key = os.getenv("SOLANA_PRIVATE_KEY")
token_mint = "CzLSujWBLFsSjncfkh59rUFqvafWcY5tzedWJSuypump"
result = client.buy_token(
private_key=private_key,
token_mint=token_mint,
amount=0.1, # Spend 0.1 SOL
slippage_percent=10
)
if result["success"]:
print(f"Purchase successful: {result['signature']}")
print(f"View on Solscan: {result['explorer_url']}")
else:
print(f"Purchase failed: {result['error']}")
Higher slippage for volatile tokens:
result = client.buy_token(
private_key=private_key,
token_mint="DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
amount=0.05,
slippage_percent=25, # Higher slippage for volatile tokens
priority_fee=0.01 # Higher priority fee for faster execution
)
Purchase a specific token amount instead of a SOL amount:
result = client.buy_token(
private_key=private_key,
token_mint="8z5VqjqYqF3zK8jM9nL3rT6wU4xY2zA1bC5dE8fG9hJ",
amount=1000000, # Want to receive 1M tokens
denominated_in_sol=False, # Amount is in TOKENS, not SOL
slippage_percent=15
)Advanced examples
Trading bot with retry logic:
import os
import time
from axiomtradeapi.client import AxiomTradeClient
from typing import Optional
class TradingBot:
def __init__(self):
self.client = AxiomTradeClient()
self.private_key = os.getenv("SOLANA_PRIVATE_KEY")
self.max_retries = 3
def buy_with_retry(
self,
token_mint: str,
amount_sol: float,
max_slippage: float = 15
) -> Optional[str]:
"""Buy token with automatic retry logic. Returns signature or None."""
for attempt in range(1, self.max_retries + 1):
result = self.client.buy_token(
private_key=self.private_key,
token_mint=token_mint,
amount=amount_sol,
slippage_percent=max_slippage,
priority_fee=0.005 * attempt # Increase fee with each retry
)
if result["success"]:
return result["signature"]
if "slippage" in result['error'].lower():
max_slippage += 5 # Increase slippage for next attempt
elif "insufficient funds" in result['error'].lower():
return None
if attempt < self.max_retries:
time.sleep(2 ** attempt) # Exponential backoff
return None
Dollar-cost averaging (DCA):
import time
from datetime import datetime
from axiomtradeapi.client import AxiomTradeClient
class DCAStrategy:
def __init__(self, private_key: str):
self.client = AxiomTradeClient()
self.private_key = private_key
self.purchase_history = []
def execute_dca(
self,
token_mint: str,
amount_per_purchase: float,
num_purchases: int,
interval_seconds: int
):
for i in range(1, num_purchases + 1):
result = self.client.buy_token(
private_key=self.private_key,
token_mint=token_mint,
amount=amount_per_purchase,
slippage_percent=12
)
if result["success"]:
self.purchase_history.append({
"timestamp": datetime.now(),
"amount_sol": amount_per_purchase,
"signature": result["signature"]
})
if i < num_purchases:
time.sleep(interval_seconds)
# Usage: buy 0.1 SOL worth every hour, 5 times
dca = DCAStrategy(os.getenv("SOLANA_PRIVATE_KEY"))
dca.execute_dca(
token_mint="CzLSujWBLFsSjncfkh59rUFqvafWcY5tzedWJSuypump",
amount_per_purchase=0.1,
num_purchases=5,
interval_seconds=3600
)
Pre-transaction safety checks:
from axiomtradeapi.client import AxiomTradeClient
import os
class SafeTrader:
def __init__(self):
self.client = AxiomTradeClient()
self.private_key = os.getenv("SOLANA_PRIVATE_KEY")
def safe_buy(
self,
token_mint: str,
amount_sol: float,
min_balance_sol: float = 0.01 # Keep minimum balance for fees
) -> dict:
# 1. Check SOL balance
balance = self.client.GetBalance()
if not balance:
return {"success": False, "error": "Failed to fetch balance"}
current_sol = balance['sol']
# 2. Verify sufficient funds
total_needed = amount_sol + min_balance_sol + 0.01
if current_sol < total_needed:
return {
"success": False,
"error": f"Insufficient balance. Need {total_needed} SOL, have {current_sol} SOL"
}
# 3. Validate token address format
if len(token_mint) < 32 or len(token_mint) > 44:
return {"success": False, "error": "Invalid token address format"}
# 4. Execute trade
return self.client.buy_token(
private_key=self.private_key,
token_mint=token_mint,
amount=amount_sol,
slippage_percent=15
)
Premium RPC for faster execution on time-sensitive trades:
result = client.buy_token(
private_key=private_key,
token_mint=token_mint,
amount=0.5,
slippage_percent=20, # Higher slippage for launch volatility
priority_fee=0.1, # High priority fee for immediate execution
rpc_url="https://mainnet.helius-rpc.com/?api-key=YOUR_HELIUS_KEY"
)
WebSocket-triggered automated buying:
from axiomtradeapi.client import AxiomTradeClient
import asyncio
import os
class TokenSniper:
def __init__(self):
self.client = AxiomTradeClient()
self.private_key = os.getenv("SOLANA_PRIVATE_KEY")
self.buy_amount = 0.05 # SOL per token
def should_buy(self, token_data: dict) -> bool:
initial_liquidity = token_data.get('initial_liquidity_sol', 0)
lp_burned = token_data.get('lp_burned', False)
dev_holds = token_data.get('dev_holds_percent', 100)
return (
initial_liquidity >= 1.0 and
lp_burned == True and
dev_holds < 20
)
async def handle_new_pair(self, token_data: dict):
token_mint = token_data.get('token_address')
if not self.should_buy(token_data):
return
result = self.client.buy_token(
private_key=self.private_key,
token_mint=token_mint,
amount=self.buy_amount,
slippage_percent=20, # Higher for new launches
priority_fee=0.02 # High priority for early entry
)
async def start_sniping(self):
async for message in self.client.stream_new_pairs():
await self.handle_new_pair(message)
See the WebSocket Integration guide for details on streaming new pairs.
Selling Tokens
sell_token() is the complement to buy_token() - it sells tokens from your wallet for SOL.
Method signature
def sell_token(
self,
private_key: str,
token_mint: str,
amount_tokens: float,
slippage_percent: float = 5.0,
priority_fee: float = 0.005,
pool: str = "auto"
) -> Dict[str, Union[str, bool]]Parameters
private_key(str, required) - Your wallet’s private key in base58 format.token_mint(str, required) - The mint address of the token you want to sell.amount_tokens(float, required) - Amount of tokens to sell. You can also pass"100%"to sell your entire balance of the token.slippage_percent(float, optional, default5.0) - Slippage tolerance percentage.priority_fee(float, optional, default0.005) - Priority fee in SOL paid to validators, same semantics as inbuy_token().pool(str, optional, default"auto") - Exchange/pool selection, using the same options asbuy_token()("auto","pump","raydium","pump-amm","launchlab","raydium-cpmm","bonk").
Return value
{
"success": True,
"signature": "5nh7w9kM2pqW...transaction_signature",
"error": None
}
success(bool) - Whether the transaction was successfulsignature(str) - Transaction signature if successfulerror(str) - Error message if the transaction failed
Example
from axiomtradeapi.client import AxiomTradeClient
import os
client = AxiomTradeClient()
private_key = os.getenv("SOLANA_PRIVATE_KEY")
token_mint = "token_contract_address"
# Sell 500 tokens
sell_result = client.sell_token(
private_key=private_key,
token_mint=token_mint,
amount_tokens=500,
slippage_percent=5.0,
priority_fee=0.005,
pool="auto"
)
if sell_result["success"]:
print(f"Sell successful: https://solscan.io/tx/{sell_result['signature']}")
else:
print(f"Sell failed: {sell_result['error']}")
A common pattern is to buy, wait, then sell part of the resulting position:
buy_result = client.buy_token(
private_key=private_key,
token_mint=token_mint,
amount=0.1
)
# Later, sell all tokens acquired
sell_result = client.sell_token(
private_key=private_key,
token_mint=token_mint,
amount_tokens="100%"
)Security Guidelines
Private key security
Do:
- Store private keys in environment variables
- Use secure key management services (AWS Secrets Manager, HashiCorp Vault)
- Encrypt private keys at rest
- Use hardware wallets for large amounts
- Regularly rotate keys
- Use separate wallets for trading bots vs. main holdings
Don’t:
- Hard-code private keys in source code
- Commit private keys to Git repositories
- Share private keys via email, chat, or any communication channel
- Store private keys in plain text files
- Reuse the same private key across multiple applications
import os
from dotenv import load_dotenv
load_dotenv() # Load from .env file (add .env to .gitignore)
private_key = os.getenv("SOLANA_PRIVATE_KEY")
if not private_key:
raise ValueError("SOLANA_PRIVATE_KEY not found in environment variables")Financial safety
- Always test first with a small amount (e.g.
0.001SOL) before scaling up. - Set maximum trade-size limits in your bot logic and reject anything above them.
- Maintain a whitelist of verified tokens if your bot buys programmatically, and require confirmation before trading unlisted tokens.
Best Practices
- Always handle errors - check the
successfield on every call and branch accordingly. - Log all transactions - record token, amount, and signature/error for every trade for audit purposes.
- Use appropriate slippage - roughly 5% for established tokens, 12% for moderate volatility, 20-25% for meme coins.
- Monitor transaction status - use the returned
explorer_url(buy) or signature (sell) to confirm on Solscan. - Implement rate limiting between trades to avoid hammering the API and to manage risk.
- Try alternate pools when
"auto"fails - if the error mentions liquidity, retry with a specific pool such as"raydium".
Troubleshooting
“solders library not installed”
pip install solders“Insufficient funds”
Check your balance before trading, and ensure it covers the trade amount plus priority fee plus network fees (~0.000005 SOL):
balance = client.GetBalance()
total_needed = amount + priority_fee + 0.001
if balance['sol'] < total_needed:
print(f"Need {total_needed} SOL, have {balance['sol']} SOL")“Slippage tolerance exceeded”
The token price moved beyond your slippage tolerance during execution. Increase slippage_percent (and optionally priority_fee for faster execution).
“Transaction failed to confirm”
Use a premium RPC endpoint for better reliability, or increase priority_fee.
“Invalid private key format”
The private key must be a base58 string, not an array or hex string. Export it from Phantom via Settings -> Show Private Key.
“PumpPortal API error: 400”
Usually caused by an invalid mint address, non-positive amount, an unreasonable slippage value, or a token not supported on PumpPortal. Validate these before sending the request, and try pool="auto" or a different pool if a specific one returns “pool not found”.
Transaction succeeded but tokens don’t appear in wallet
- The transaction may still be confirming - wait 30-60 seconds.
- You may need to manually add the token to your wallet using the mint address.
- Verify on Solscan that the transaction actually succeeded.
Debug mode
import logging
logging.basicConfig(level=logging.DEBUG)
result = client.buy_token(...)
This logs the parameters sent to PumpPortal, the raw request/response data, and full stack traces for any errors.
Related
- Checking Your Balance - verify your SOL balance before trading
- WebSocket Integration - stream new token launches to trade on
- Building Trading Bots - strategies that combine buying, selling, and risk management
- Error Handling
- Troubleshooting
External references: