A pump.fun bot is a Solana program client that detects new token launches or trade events, applies a strategy, and submits signed transactions before competing bots. The hard parts are not the strategy logic but event detection reliability, transaction construction, fee and slippage handling, and honest measurement of end-to-end latency. This article gives a mental model, runnable detection and execution code, a measurement procedure you can reproduce, and troubleshooting for the failures that separate paper results from live behavior. It stays methodology-focused; for a concrete RPC endpoint choice, use RPC Assistant.
Scope and the direct answer
A pump.fun bot is software that watches the pump.fun on-chain program on Solana, decides whether a token or trade meets a rule, and submits a signed transaction to buy or sell. The term covers several distinct designs: launch snipers that act on token creation, copy-trading bots that mirror a wallet, volume bots that generate trades on a token, and exit bots that manage positions after entry. They share the same infrastructure primitives: an event source, a strategy, a transaction builder, a signing wallet, and an RPC path to the cluster.
The practical answer to "how do I build one" is to separate detection from execution and measure each stage independently. Most disappointing live results come from a detection path that is slower or noisier than assumed, or from a fill model that ignores competition, priority fees, and slippage. Treat the bot as a latency-sensitive distributed system, not a script.
- Detection: WebSocket subscription, Geyser stream, or polling of program accounts.
- Decision: deterministic rules over parsed events, with explicit rejection reasons.
- Execution: transaction construction, compute budget, priority fee, signing, send, confirm.
- Measurement: timestamped stages from event receipt to confirmed slot.
Assumptions and prerequisites
This guide assumes you can run Python 3.10+ or Node.js 18+, hold a Solana keypair you control, and have access to an RPC endpoint that supports the methods you need. You should understand Solana accounts, instructions, and transaction confirmation. You do not need a specific provider; the code uses placeholders such as RPC_URL and PRIVATE_KEY.
Before writing strategy code, confirm your endpoint supports the subscriptions and methods you plan to use. WebSocket subscriptions, getProgramAccounts with filters, sendTransaction with skipPreflight, and getSignatureStatuses behave differently across providers and rate limits. For endpoint selection, see the RPC Assistant guidance on the best Solana RPC provider and compare plans on the RPC pricing page.
- A funded wallet with a small SOL balance for fees and test trades.
- An RPC URL with WebSocket support if you use subscriptions.
- A local clock source you trust for latency measurement.
- A logging sink that records every decision and rejection.
Mental model: the bot as a pipeline with observable stages
Think of the bot as five stages: ingest, parse, decide, build, and land. Ingest receives raw notifications. Parse decodes them into token mints, amounts, and slots. Decide applies your rules and produces an intent. Build constructs and signs a transaction. Land sends it and waits for confirmation. Each stage has a timestamp and a failure mode.
The pipeline view prevents a common mistake: optimizing the strategy while ignoring ingest lag. A rule that fires 200 ms after the event is useless if a competing bot fires at 50 ms. It also makes measurement honest, because you can attribute latency to a stage instead of reporting a single end-to-end number that hides where time went.
- Ingest lag: time from cluster event to your process receiving it.
- Parse cost: CPU time to decode and validate.
- Decision cost: strategy evaluation, usually negligible.
- Build cost: transaction assembly and signing.
- Land cost: network send, leader scheduling, confirmation.
Detection: subscribing to pump.fun program activity
The most direct detection method is a WebSocket subscription to logs mentioning the pump.fun program ID. You receive notifications as the cluster processes transactions, then filter for the instruction or log pattern that indicates a new token or a trade. This is simpler than Geyser but depends on your provider's WebSocket reliability and rate limits.
The following Python sketch subscribes to logs and prints candidate signatures. Replace RPC_URL and the program ID placeholder with values you have verified. It does not place trades; it is a detection harness you can extend.
import asyncio, json, time
import websockets
RPC_WS = "RPC_URL".replace("https", "wss")
PUMP_PROGRAM = "PUMP_PROGRAM_ID"
async def main():
async with websockets.connect(RPC_WS, ping_interval=20) as ws:
sub = {
"jsonrpc": "2.0", "id": 1, "method": "logsSubscribe",
"params": [{"mentions": [PUMP_PROGRAM]}, {"commitment": "processed"}]
}
await ws.send(json.dumps(sub))
while True:
msg = json.loads(await ws.recv())
if msg.get("method") != "logsNotification":
continue
value = msg["params"]["result"]["value"]
sig = value.get("signature")
logs = value.get("logs", [])
ts = time.time()
print(json.dumps({"ts": ts, "sig": sig, "log_count": len(logs)}))
asyncio.run(main())Execution: building and sending a transaction
Execution requires constructing the correct instruction for the pump.fun program, setting a compute budget, attaching a priority fee, signing, and sending. The exact instruction layout depends on the program's current interface; use the program's IDL or a maintained client library rather than hardcoding account orders. The example below shows the send-and-confirm loop with skipPreflight and status polling, which you can wrap around any instruction builder.
Set a compute unit limit and price deliberately. Too low and the transaction fails; too high and you overpay. Priority fees are market-driven and change with congestion, so treat them as a configurable parameter and log the value used for every attempt.
import time, base64
import requests
RPC_URL = "RPC_URL"
def send_and_confirm(signed_tx_b64: str, timeout_s: float = 30.0) -> dict:
payload = {
"jsonrpc": "2.0", "id": 1, "method": "sendTransaction",
"params": [signed_tx_b64, {"encoding": "base64", "skipPreflight": True,
"maxRetries": 3}]
}
r = requests.post(RPC_URL, json=payload, timeout=10).json()
if "error" in r:
return {"stage": "send", "error": r["error"]}
sig = r["result"]
deadline = time.time() + timeout_s
while time.time() < deadline:
status = requests.post(RPC_URL, json={
"jsonrpc": "2.0", "id": 2, "method": "getSignatureStatuses",
"params": [[sig], {"searchTransactionHistory": False}]
}, timeout=10).json()
val = status.get("result", {}).get("value", [None])[0]
if val and val.get("confirmationStatus") in ("confirmed", "finalized"):
return {"stage": "confirmed", "sig": sig, "slot": val.get("slot")}
if val and val.get("err"):
return {"stage": "failed", "sig": sig, "err": val["err"]}
time.sleep(0.5)
return {"stage": "timeout", "sig": sig}Measurement: a reproducible latency procedure
Do not report a single latency number. Record a timestamp at each pipeline stage and compute the distribution over a fixed sample. Run the procedure on the same machine, network, and endpoint for the whole sample, and record the endpoint, commitment level, and time window alongside the results.
Procedure: (1) start the detection harness and log ingest timestamps; (2) for each detected event, log decision, build, send, and confirmation timestamps; (3) collect at least 200 events; (4) compute p50, p90, and p99 for each stage and for end-to-end; (5) repeat on a second endpoint and compare. The table below is the template to fill with your own measurements; do not copy numbers from elsewhere.
- | Stage | p50 (ms) | p90 (ms) | p99 (ms) | Notes |
- | Ingest | | | | endpoint, commitment |
- | Parse + decide | | | | CPU-bound |
- | Build + sign | | | | keypair type |
- | Send to confirmed | | | | priority fee used |
- | End-to-end | | | | sample size |
Verification: what a healthy run looks like
A healthy detection run prints a steady stream of signatures with plausible log counts and no gaps longer than your endpoint's expected notification interval. A healthy execution run shows confirmed transactions with slots close to the send time and a low rate of preflight or simulation errors. If you see long gaps, check WebSocket keepalive and provider rate limits. If you see many failures, inspect the error codes before changing strategy.
For each trade attempt, log the intent, the signed transaction signature, the send response, and the final status. This log is the only reliable basis for debugging and for comparing paper results with live results.
- Detection: no unexplained gaps; parse errors near zero.
- Execution: confirmation rate and error codes tracked per attempt.
- Latency: stage distributions recorded, not just averages.
- Cost: priority fees and compute units logged per transaction.
Troubleshooting common failures
Most failures fall into a few categories. Blockhash expiry happens when a transaction is built but not landed quickly; rebuild with a fresh blockhash. Compute budget exhaustion appears as a simulation error; raise the limit or simplify the instruction. Slippage and account errors appear when the pool state changed between build and land; re-read state and rebuild. Rate limiting appears as HTTP 429 or dropped WebSocket messages; reduce subscription scope or move to a dedicated endpoint.
Paper-trading discrepancies deserve special attention. A paper fill model that assumes you get the create price ignores that other bots may buy in the same slot. If your live entries are consistently worse than paper, the model is the problem, not the network. Measure the gap between your intended price and your realized price across many trades before concluding that latency is the cause.
- Blockhash not found: rebuild immediately before send.
- Compute exceeded: raise limit, verify instruction accounts.
- Slippage exceeded: widen tolerance or reduce size.
- 429 / dropped subscription: check provider limits, consider dedicated node.
- Persistent paper-vs-live gap: audit the fill model first.
Limitations, tradeoffs, and responsible operation
Latency has a floor set by physics and by the cluster's leader schedule; no bot can guarantee first position. Strategies that depend on being first are competitive and fragile. Volume and comment bots raise separate ethical and platform-policy questions, and automated trading can violate venue rules or local law. Treat any bot as high-risk software that can lose funds through bugs, key compromise, or adverse market moves.
Operationally, choose between shared and dedicated infrastructure based on measured reliability, not marketing claims. A dedicated node can reduce contention and give you more control over subscriptions; a shared endpoint is cheaper for low-volume experimentation. Compare the tradeoffs on the dedicated node page and the RPC pricing page, and keep secrets out of logs.
- No guaranteed ordering; design for partial failure.
- Priority fees are a recurring cost that scales with attempts.
- Key management is a security boundary; use isolated wallets.
- Measure before optimizing; intuition about latency is often wrong.
Next steps
Start with the detection harness, confirm your endpoint delivers notifications reliably, then add a dry-run execution path that builds and simulates transactions without sending. Only after simulation is clean should you send small live trades and begin the measurement procedure. Keep a changelog of strategy and infrastructure changes so latency shifts can be attributed.
For endpoint selection and network details, use RPC Assistant and the OnFinality network pages rather than guessing. Review the Solana network guide, the API service overview, and the dedicated node options, and revisit the measurement table after any infrastructure change.
- Build detection, then simulation, then live execution.
- Record stage timestamps from the first run.
- Re-measure after changing endpoints or fee settings.
- Keep strategy and infrastructure changes separate.