π₯Accelerated getProgramAccounts
Fast getProgramAccounts on Solana - Response times under 10ms
getProgramAccounts is one of the most expensive calls on Solana. On a standard RPC node, it scans raw account states on every request - slow by design, and worse under load. The root cause is structural: standard nodes have no index over account data, so every call triggers the same full scan regardless of how specific your filter is.
This is exactly what Shyft solves - Introducing Accelerated getProgramAccounts.
Shyft's Accelerated getProgramAccounts (or Accelerated gPA) resolves the most common queries in under 10ms, no code changes required.
Accelerated getProgramAccounts is available on all paid plans. Already live on Shyft RPC across all regions. No API changes - same method, same parameters, dramatically faster responses.
Why the standard getProgramAccounts is slow?
getProgramAccounts is slow?A vanilla Solana validator has no index over account data. When you call getProgramAccounts with a memcmp filter, the node walks every account owned by that program and compares bytes at the given offset - one by one, on-the-fly, every time. For large programs like Raydium or the Token Program, which own millions of accounts, this can take anywhere from hundreds of milliseconds to tens of seconds.

How it works
Standard Solana RPC nodes handle everything - block replay, vote processing, account state, and RPC queries - in one tightly coupled stack. When a getProgramAccounts call comes in, it competes for resources with everything else the validator is doing, and scans raw account state on every request.
Shyft's approach is different. The accounts engine runs on entirely separate hardware, decoupled from the RPC layer. It has one job: maintain a fast, structured view of account data for the programs and offsets that matter most, and serve read requests against it.
The single-process architecture
The entire engine runs as a single unified process - no external database connections, no network hops between components. Three responsibilities, one process:
gRPC ingestion: Streams account updates directly from the validator layer as they happen on-chain.
RocksDB indexing: Writes account state into an embedded key-value store, keyed by program + offset.
Stateless JSON-RPC: Serves
getProgramAccountsreads directly from RocksDB - no external calls.

How acceleration is triggered
When a getProgramAccounts request arrives at the JSON-RPC layer, the engine checks two things: is this program in the accelerated set, and does the memcmp offset match a keyed index for that program? If both match, the result is served directly from RocksDB. If either condition is not met, the request falls through to the standard RPC path transparently - same response, just without the speed benefit.
No code changes required. Acceleration is applied at the RPC layer automatically. Your existing getProgramAccounts calls work as-is - the engine intercepts matching requests before they ever touch raw ledger state.
On-demand Acceleration: New program + offset combinations can be added in real time
The engine currently covers a pre-defined set of programs and offsets selected based on query patterns across the Shyft network. But new indexes are not a deployment - they are created on demand. If you regularly query a program or offset not yet in the accelerated set, reach out to the Shyft team. The index is created without downtime or service interruption, and acceleration applies from that point forward.
Accelerated programs & offsets
The following program + offset combinations are accelerated. Calls that match are served from the Shyft accounts engine.
Pump.fun AMM
pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA
0,43,75
Pump.fun
6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P
0
Raydium CLMM
CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK
0, 73, 105
Raydium AMM v4
675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8
400, 432
Raydium CPMM
CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C
168, 200
Meteora DLMM
LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo
0, 88, 120
Meteora DAMM v2
cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG
168, 200
Meteora DAMM v1
Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB
40, 72
Orca Whirlpool
whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc
0, 101, 181
Jupiter Lend/Borrow
jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi
0
Drift
dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN
0, 136
PancakeSwap CLMM
HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq
0, 73, 105
Address Lookup Table
AddressLookupTab1e1111111111111111111111111111
22
Solana Stake Program
Stake11111111111111111111111111111111111111
12
Need a program added? New indexes are created on demand without downtime. Reach out to the Shyft team with the program address and the offsets you query regularly.
How the Engine Handles Multiple Filters
Most real-world getProgramAccounts calls don't use a single filter. A trading bot querying Raydium CLMM pools might filter by token_mint_a at offset 73 and by token_mint_b at offset 107 simultaneously - narrowing the result to pools containing a specific trading pair. Here's how the engine handles it:
When one of your filters hits an accelerated offset:
The engine scans your filter set, identifies the accelerated offset, and resolves that filter first - regardless of where you placed it in the array. The accelerated lookup runs against the purpose-built index and returns a narrow result set immediately. Every remaining filter is then evaluated in-memory against that already-narrow set. Filter order in your request does not matter - the engine finds the fast path automatically.
Even though the accelerated offset 73 is second in the array, the engine resolves it first. The result is every CLMM pool where token_mint_a is WSOL - filtered further in-memory to those where token_mint_b is USDC. Two filters, one fast path.
When none of your filters hit an accelerated offset:
The engine falls through to the standard scan path β filters evaluated in order, full account set, no short-circuit. The request completes correctly, just without the speed benefit.
For programs in the accelerated set, including at least one filter at an accelerated offset is all it takes β the engine handles the rest.
Quick Start
A working example for getProgramAccounts Pump.fun AMM at offset 43 - in cURL, JavaScript & Rust - the fastest way to see the latency difference yourself.
Filter order does not matter. The engine automatically identifies the accelerated offset from whichever filters you pass and resolves it first - returning a narrow result set from the purpose-built index. Any additional filters are then applied against that already-narrow result set in memory.
Latency results
Tests were run from the AMS region, 10 requests at 1 req/s using Hey with a persistent TCP connection. Four programs were tested, each at one of their accelerated offsets.
Raydium CLMM
73
8.7ms
7.8ms
15.2ms
Pump.fun AMM
43
8.3ms
7.9ms
11.7ms
Address Lookup Table
22
10.9ms
10.7ms
15.4ms
Orca Whirlpool
101
8.2ms
7.8ms
12.4ms
What to expect in production: With a persistent TCP connection and repeated calls, the majority of accelerated gPA queries resolve between 10-15ms. A cold first request from a fresh TCP handshake adds approximately 5-10ms.
Tests used Hey, which keeps the TCP connection open after the first request - representative of real application behaviour where connections are reused.
Frequently Asked Questions
How do I know if my request was accelerated?
You don't need to - and that's intentional. If your request matches an accelerated program + offset, it resolves faster. If it doesn't, it falls through to the standard path. The response format is identical in both cases.
What exactly is the "offset" in a memcmp filter, and how was it determined?
Every account on Solana stores its data as a raw byte buffer. The structure of that buffer - which fields live at which byte positions - is defined by the program that owns the account. This layout is typically described in the program's IDL (Interface Definition Language).
The offset in a memcmp filter tells the RPC node: "start reading at byte N in the account data buffer, and compare those bytes against my value." So offset: 73 on Raydium CLMM means "compare starting at byte 73 of each pool account's data." Byte 73 in a CLMM pool account is where the token_mint_a field begins - so filtering there lets you find all pools involving a specific token.
You derive the correct offset by reading the program's IDL or inspecting its account struct definitions in the source code. For Anchor-based programs, each field has a known size and they stack sequentially - so you sum the byte sizes of all fields before the one you want. Shyft identified the most commonly queried fields across each protocol and accelerated those specific offsets.
Practical tip: If you're using a protocol SDK (e.g. @raydium-io/raydium-sdk or @orca-so/whirlpools-sdk), the SDK usually constructs the correct memcmp filter for you - you don't need to calculate the offset manually.
What do you pass as the "bytes" value in the filter?
The bytes value is whatever you're filtering for at that offset - encoded as base58 (default) or base64 depending on the encoding you specify. It is not a fixed value; it's the specific thing you're looking up.
In most DeFi use cases, the field at an accelerated offset is a public key - a token mint address, a pool authority, or a user wallet. So bytes is the base58 or base64 encoded public key you're searching for.
Empty results? The most common cause is passing a base64-encoded value when encoding is set to base58, or vice versa. Double-check the encoding field matches how you've encoded the bytes.
What happens if I pass multiple memcmp filters at different offsets?
Filter order does not matter. The engine automatically identifies the accelerated offset from whichever filters you pass and resolves it first - returning a narrow result set from the purpose-built index. Any additional filters are then applied against that already-narrow result set in memory.
Last updated