A bot that handles ten trades a minute is not a smaller version of one that handles a thousand. Different things are binding, and they start binding in a fairly predictable sequence.
★The order matters because optimising a limit you have not reached yet produces no improvement at all.★
The Order They Arrive In
| Order | Limit | Symptom |
|---|---|---|
| 1 | ★RPC rate limit★ | ★429s, usually from polling★ |
| 2 | Confirmation polling | Requests scale with in-flight count |
| 3 | ★Account write contention★ | ★Throughput flat regardless of sends★ |
| 4 | Your own CPU | Event loop lag, GC pauses |
| 5 | ★Fee budget★ | ★Economics, not a technical wall★ |
★Almost every team optimises their own code first and hits number one anyway.★ The first two are request-count problems, and they arrive long before anything about your process matters.
Rate Limits Arrive Through Polling
The surprise is that sends are rarely what exhausts a quota. ★Confirmation polling is.★
100 in-flight transactions
× poll once per slot
× 30 seconds until resolution
= ★7,500 requests★ for 100 sends
Two fixes, in order of impact:
// ★1. Batch the polling — up to 256 signatures per call.★
const { value } = await connection.getSignatureStatuses(pendingSignatures);
// ★2. Drop preflight — it doubles your send request count.★
await connection.sendRawTransaction(raw, { skipPreflight: true, maxRetries: 0 });
★Batching confirmation is the single highest-leverage change at this stage★, because it collapses the dominant request source by two orders of magnitude.
Then separate your read path from your send path. A heavy getProgramAccounts scan and a time-critical send competing for one quota means the scan wins and the send fails — at exactly the moment volume is highest.
If your request budget is under control and transactions still miss, a free BoltTx key is one line to test the submission path.
Write Contention Is the Ceiling That Does Not Move
★Past the request-count problems, this is the wall that does not move.★
Solana executes transactions in parallel unless they write the same account. Transactions writing the same account are serialised — so a design funnelling everything through one account has a hard ceiling regardless of how much you send.
★one shared account★ → throughput ≈ one write per slot
★per-user accounts★ → throughput scales with parallelism
The diagnostic is simple: ★if throughput is flat no matter how much you send, and you are not seeing 429s, look for a shared writable account.★
For a trading bot this often appears as everyone trading the same hot pool, which is not something you control. What you do control is your own state accounts, and funnelling those through one account is a self-inflicted version of the same ceiling.
Your Process Becomes Relevant Later
Only after the first three do local resources matter — and when they do, it is rarely raw speed:
// ★Event loop lag is the metric, not CPU percentage.★
let last = Date.now();
setInterval(() => {
const lag = Date.now() - last - 100;
if (lag > 50) metrics.eventLoopLag.observe(lag);
last = Date.now();
}, 100);
★A garbage collection pause can exceed a slot, and it happens under load when your queues are deepest.★ That is the argument for a language without GC pauses — not raw execution speed, which is microseconds against network time measured in slots.
Before rewriting anything: move JSON parsing off the hot path, stop allocating in tight loops, and check whether one slow handler is blocking everything behind it.
Fee Budget Is a Ceiling Too
★At scale, the binding constraint is often economic rather than technical.★
Ten times the volume is ten times the fees, plus a higher failure rate as you compete more often. A strategy profitable at low volume can be unprofitable at high volume while every technical metric looks healthy.
metrics.record({ outcome, feeSpent, netPerAttempt }); // ★per attempt, not per success★
★If net profit per attempt shrinks as volume grows, you have found your real ceiling★ — and no amount of infrastructure work raises it.
Scale in the Right Order
1. Measure which limit you are actually hitting. 429 rate, throughput curve, event loop lag, net per attempt. ★Guessing here wastes the most time.★
2. Batch confirmation polling. Usually the largest single win.
3. Separate reads from sends. Removes an entire class of interference.
4. Shard your own writable state. Only if the throughput curve is flat.
5. Fix your process. GC, blocking handlers, allocation.
6. Re-check the economics. ★If per-attempt profit is falling, stop scaling.★
★Skipping step one is the most common and most expensive mistake.★ A rewrite in a faster language does nothing for a bot that is rate limited, and sharding state does nothing for a bot whose problem is polling.
What Landing Looks Like
Real transactions through our delivery nodes: median confirmation 336ms — under one slot.
★At volume the tail determines queue behaviour.★ A system where most transactions land quickly but a meaningful share take much longer accumulates a backlog during exactly the periods when it is busiest.
Where BoltTx Fits
We handle submission. Your architecture, batching, and state design stay in your code.
Submissions route through our own delivery nodes in four regions with stake-weighted routing and no public mempool exposure, so transactions are not observable in transit before they land. ★Because the send path is separate from whatever you use for reads, a heavy scan cannot exhaust the quota your sends depend on★ — which removes the second bottleneck on the list by construction.
You sign locally. We never hold funds, never sign, and never modify transaction contents. The tip travels inside the transaction, paid on chain from your own wallet, and reverts with the transaction if it fails, because that is how Solana handles atomic transactions. You pay only on transactions that reach the chain.
Get a free API key. No monthly fee:
const sender = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");
FAQ
What limits a Solana bot first when scaling? RPC rate limits, almost always from confirmation polling rather than from sends. Local performance and write contention only matter after that is solved.
Why does confirmation polling consume so many requests? Because it scales with in-flight transactions times poll frequency times duration. A hundred transactions polled individually can be thousands of requests for a hundred sends.
How do I reduce confirmation requests?
Batch them. getSignatureStatuses accepts up to 256 signatures per call, which collapses the dominant request source by two orders of magnitude.
Does skipPreflight help with rate limits? Yes, it halves your send request count by removing the simulation round trip. It also avoids simulating against a slot you will not land in.
Should reads and sends use the same endpoint? No. A heavy scan and a time-critical send competing for one quota means the scan wins and the send fails, at exactly the moment volume peaks.
Why is my throughput flat no matter how much I send? Likely write contention. Transactions writing the same account serialise, so a shared account caps throughput regardless of submission volume.
How do I detect write contention? Flat throughput with no 429 responses. If you are not rate limited and sending more changes nothing, look for an account every transaction writes.
When does my own code become the bottleneck? After rate limits and contention are solved. Even then it is usually GC pauses or a blocking handler rather than raw execution speed.
Should I rewrite my bot in Rust to scale? Not for raw speed, which is negligible against network time. The real argument is the absence of GC pauses, and only after you have confirmed that is what is binding.
How do I measure event loop lag? Schedule a timer at a fixed interval and record how late it actually fires. That surfaces blocking handlers and GC pauses better than CPU percentage does.
Does scaling volume reduce profitability? It can. Ten times the volume is ten times the fees plus a higher failure rate from more competition. Track net profit per attempt to see it.
What metric shows the economic ceiling? Net profit per attempt, not per success. If it shrinks as volume grows, you have found a limit that no infrastructure work will raise.
How many concurrent sends should I run? Bounded by your endpoint's rate limit rather than by Solana. Raise it until 429s appear, then back off — and use a worker pool rather than chunked batches.
Does adding wallets increase throughput? Not against a contended account, since contention is per account rather than per wallet. It also multiplies your fees without multiplying your odds.
What is the first thing to measure when scaling? Which limit is actually binding: 429 rate, throughput curve, event loop lag, and net per attempt. Guessing here wastes more time than any other mistake.
Why does my backlog grow during congestion? Because the landing tail lengthens while your inflow stays constant. Alert on drain rate rather than queue depth, since a deep queue that is draining is fine.