Address Lookup Tables
Address Lookup Table queries through getProgramAccounts and getProgramAccountsV2, answered from an in-memory index, with plan rates, account layout and filters.
Address Lookup Tables are found and read in bulk with getProgramAccounts or getProgramAccountsV2 on the Address Lookup Table program.
Access
Queries go through getProgramAccounts or getProgramAccountsV2 with AddressLookupTab1e1111111111111111111111111 as the first parameter. The endpoint and the API key are the same as every other Solana RPC call. There is no separate method, hostname or add-on.
| Plan | Availability |
|---|---|
| Blade Runner, Nexus | Served |
| Free, Operator | HTTP 403, code -32600, message getProgramAccounts requires higher tier. The getProgramAccountsV2 message names getProgramAccountsV2 |
Answers come from an in-memory index, not a disk scan. No filter is required, and the 256 MB scan-results cap of a disk scan does not apply. Batch requests (a JSON array of calls) are not served: one call per HTTP request.
Rate limits
| Plan | Calls per second per API key |
|---|---|
| Blade Runner | 25/s |
| Nexus | 75/s |
| Free, Operator | — |
The budget is shared per API key. Address Lookup Table queries count together with every other getProgramAccounts and getProgramAccountsV2 call on that key, whichever program they address. A Nexus key sending 50 calls per second against other programs has 25 calls per second left for table queries.
There is no cap on open requests; only the rate is counted. The plan's total requests-per-second limit also applies. See Plans & Limits.
Over the rate, the response is HTTP 429 with code -32005 and the message Rate limit exceeded (gpa: 25/sec) on Blade Runner or Rate limit exceeded (gpa: 75/sec) on Nexus. It carries no Retry-After header and no rate-limit headers. The window is one whole second. See Gateway errors.
One known table
A table whose address is already known is read with getAccountInfo or getMultipleAccounts with encoding: "jsonParsed". That is a plain account read: it is not an index query and does not count against the getProgramAccounts budget.
getProgramAccounts on the Address Lookup Table program is for finding tables by content (authority, size, addresses, status) or for walking all tables.
Account layout
| Offset | Size | Field | Notes |
|---|---|---|---|
| 0 | 4 | type | u32 little-endian; 1 = lookup table |
| 4 | 8 | deactivationSlot | u64 little-endian; 18446744073709551615 (all bytes 0xFF) while the table is active |
| 12 | 8 | lastExtendedSlot | u64 little-endian |
| 20 | 1 | lastExtendedSlotStartIndex | u8 |
| 21 | 1 | authority present | 1 = has an authority, 0 = frozen |
| 22 | 32 | authority | public key |
| 54 | 2 | padding | |
| 56 | 32 × N | addresses | N stored addresses, at most 256 |
Account size is 56 + 32 × N bytes. A table with 4 addresses is 184 bytes.
Filters
| Goal | Filter |
|---|---|
| Tables controlled by an authority | memcmp at offset 22, bytes = the authority public key (base58) |
| Tables with an authority set (not frozen) | memcmp at offset 21, bytes: "2" (base58 of the single byte 0x01) |
| Active tables only | memcmp at offset 4, bytes: "jpXCZedGfVQ" (base58 of eight 0xFF bytes) |
| Tables holding exactly N addresses | dataSize: 56 + 32 × N |
| Tables whose address at index i is X | memcmp at offset 56 + 32 × i, bytes = X |
memcmp compares at one fixed offset, so a table that holds an address at an unknown position matches only a filter for that position. There is no "contains" filter.
Filters combine with AND. memcmp bytes may be base58 (default) or base64 via memcmp.encoding.
Examples
getProgramAccounts on the tables controlled by one authority, with no account data returned:
curl "https://rpc-newyork.constant-k.com/?api-key=YOUR_API_KEY" \
-X POST -H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getProgramAccounts",
"params": [
"AddressLookupTab1e1111111111111111111111111",
{
"filters": [
{ "memcmp": { "offset": 22, "bytes": "DStdNqtygUnfNwkHNrEhsPWwN7hqXrYrMJ8JMkLvidjT" } }
],
"dataSlice": { "offset": 0, "length": 0 }
}
]
}'dataSlice with length: 0 returns no data, so the answer carries only the table addresses and account metadata. The response is trimmed to one account:
[
{
"account": {
"data": ["", "base64"],
"executable": false,
"lamports": 2171520,
"owner": "AddressLookupTab1e1111111111111111111111111",
"rentEpoch": 18446744073709551615,
"space": 184
},
"pubkey": "1AjuJovi8byYxtUPyFna86jaRipDgVeX4TCXbkWNZxg"
}
]getProgramAccountsV2 returns one page of tables with the layout decoded:
curl "https://rpc-newyork.constant-k.com/?api-key=YOUR_API_KEY" \
-X POST -H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getProgramAccountsV2",
"params": [
"AddressLookupTab1e1111111111111111111111111",
{
"encoding": "jsonParsed",
"filters": [{ "dataSize": 184 }],
"limit": 1,
"withContext": true
}
]
}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"context": { "slot": 457692691 },
"value": {
"accounts": [
{
"account": {
"data": {
"parsed": {
"info": {
"addresses": [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"SysvarRent111111111111111111111111111111111",
"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1",
"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
],
"authority": "DStdNqtygUnfNwkHNrEhsPWwN7hqXrYrMJ8JMkLvidjT",
"deactivationSlot": "18446744073709551615",
"lastExtendedSlot": "306248204",
"lastExtendedSlotStartIndex": 0
},
"type": "lookupTable"
},
"program": "address-lookup-table",
"space": 184
},
"executable": false,
"lamports": 2171520,
"owner": "AddressLookupTab1e1111111111111111111111111",
"rentEpoch": 18446744073709551615,
"space": 184
},
"pubkey": "1AjuJovi8byYxtUPyFna86jaRipDgVeX4TCXbkWNZxg"
}
],
"paginationKey": "1AjuJovi8byYxtUPyFna86jaRipDgVeX4TCXbkWNZxg"
}
}
}paginationKey is the cursor for the next page. It is passed back as paginationKey on the next call, and is null on the last page.
Parameters
| Parameter | Values | Notes |
|---|---|---|
encoding | base64 (default), base64+zstd, base58, jsonParsed | base58 covers data up to 128 bytes after dataSlice; larger data returns -32602 with base58 encoding is only supported for data under 129 bytes; use base64 |
dataSlice | { offset, length } | length: 0 returns no data, only addresses and account metadata |
filters | dataSize, memcmp | Optional; every filter must match |
withContext | true, false | Adds context.slot |
minContextSlot | slot number | If the index has not reached it, -32016 with Minimum context slot has not been reached |
commitment | any level | Accepted, no effect. See the note below |
limit | 1 to 10,000, default 1,000 | getProgramAccountsV2 only. Outside the range: -32602 with limit must be between 1 and 10000 |
paginationKey | account address | getProgramAccountsV2 only; the cursor from the previous page |
changedSinceSlot | slot number | getProgramAccountsV2 only; returns only tables changed since that slot, for incremental sync |
commitment is accepted and ignored
Answers are at confirmed commitment. A finalized request gets confirmed data.
Result size and freshness
getProgramAccounts answers up to 1 GB of matched account data. Above that it returns -32012 with scan aborted: The accumulated scan results exceeded the limit. getProgramAccountsV2 pages through any size, so walking every table uses it with limit up to 10,000 and paginationKey. Results are narrowed with filters and dataSlice.
If the index falls behind the chain, a getProgramAccountsV2 call with changedSinceSlot returns -32005 with data.numSlotsBehind until the index catches up.
Errors
| HTTP | Code | Message | Cause |
|---|---|---|---|
403 | -32600 | getProgramAccounts requires higher tier | Free or Operator plan. The getProgramAccountsV2 message names getProgramAccountsV2 |
429 | -32005 | Rate limit exceeded (gpa: 25/sec), Rate limit exceeded (gpa: 75/sec) | Over the plan's calls per second |
200 | -32602 | base58 encoding is only supported for data under 129 bytes; use base64, limit must be between 1 and 10000 | An invalid parameter |
200 | -32012 | scan aborted: The accumulated scan results exceeded the limit | Matched account data over 1 GB. getProgramAccounts only |
200 | -32016 | Minimum context slot has not been reached | minContextSlot is above the slot the index has reached |
200 | -32005 | Error data carries numSlotsBehind | The index is behind the chain. Only with changedSinceSlot |
See Errors for every code and its fix.