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 on denominated_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)

Optional

  • slippage_percent (float, default 10) - 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, default 0.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, default True) - Whether amount represents 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 successful
  • signature (str) - Transaction signature (hash/ID)
  • transactionId (str) - Duplicate of signature for compatibility
  • explorer_url (str) - Direct link to view the transaction on Solscan
  • error (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, default 5.0) - Slippage tolerance percentage.
  • priority_fee (float, optional, default 0.005) - Priority fee in SOL paid to validators, same semantics as in buy_token().
  • pool (str, optional, default "auto") - Exchange/pool selection, using the same options as buy_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 successful
  • signature (str) - Transaction signature if successful
  • error (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.001 SOL) 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

  1. Always handle errors - check the success field on every call and branch accordingly.
  2. Log all transactions - record token, amount, and signature/error for every trade for audit purposes.
  3. Use appropriate slippage - roughly 5% for established tokens, 12% for moderate volatility, 20-25% for meme coins.
  4. Monitor transaction status - use the returned explorer_url (buy) or signature (sell) to confirm on Solscan.
  5. Implement rate limiting between trades to avoid hammering the API and to manage risk.
  6. 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

  1. The transaction may still be confirming - wait 30-60 seconds.
  2. You may need to manually add the token to your wallet using the mint address.
  3. 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.

External references: