Solana RPC
getProgramAccountsV2
getProgramAccountsV2 是增强版的 getProgramAccounts,使用基于游标的分页和 changedSinceSlot 更新以查询大型 Solana 账户集。
POST
/
getProgramAccountsV2
curl --request POST \
--url 'https://mainnet.helius-rpc.com/?api-key=' \
--header 'Content-Type: application/json' \
--data '
{
"jsonrpc": "2.0",
"id": "1",
"method": "getProgramAccountsV2",
"params": [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
{
"encoding": "base64",
"limit": 1000
}
]
}
'import requests
url = "https://mainnet.helius-rpc.com/?api-key="
payload = {
"jsonrpc": "2.0",
"id": "1",
"method": "getProgramAccountsV2",
"params": [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
{
"encoding": "base64",
"limit": 1000
}
]
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
jsonrpc: '2.0',
id: '1',
method: 'getProgramAccountsV2',
params: [
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
{encoding: 'base64', limit: 1000}
]
})
};
fetch('https://mainnet.helius-rpc.com/?api-key=', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://mainnet.helius-rpc.com/?api-key=",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'jsonrpc' => '2.0',
'id' => '1',
'method' => 'getProgramAccountsV2',
'params' => [
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
[
'encoding' => 'base64',
'limit' => 1000
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://mainnet.helius-rpc.com/?api-key="
payload := strings.NewReader("{\n \"jsonrpc\": \"2.0\",\n \"id\": \"1\",\n \"method\": \"getProgramAccountsV2\",\n \"params\": [\n \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n {\n \"encoding\": \"base64\",\n \"limit\": 1000\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://mainnet.helius-rpc.com/?api-key=")
.header("Content-Type", "application/json")
.body("{\n \"jsonrpc\": \"2.0\",\n \"id\": \"1\",\n \"method\": \"getProgramAccountsV2\",\n \"params\": [\n \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n {\n \"encoding\": \"base64\",\n \"limit\": 1000\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://mainnet.helius-rpc.com/?api-key=")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"jsonrpc\": \"2.0\",\n \"id\": \"1\",\n \"method\": \"getProgramAccountsV2\",\n \"params\": [\n \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n {\n \"encoding\": \"base64\",\n \"limit\": 1000\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"jsonrpc": "2.0",
"id": "1",
"result": {
"accounts": [
{
"pubkey": "CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY",
"account": {
"lamports": 15298080,
"owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"data": [
"2R9jLfiAQ9bgdcw6h8s44439",
"base64"
],
"executable": false,
"rentEpoch": 28,
"space": 165
}
}
],
"paginationKey": "8WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
}
}{
"jsonrpc": "2.0",
"error": {
"code": -32602,
"message": "Invalid params",
"data": {}
},
"id": "1"
}{
"jsonrpc": "2.0",
"error": {
"code": -32001,
"message": "Unauthorized",
"data": {}
},
"id": "1"
}{
"jsonrpc": "2.0",
"error": {
"code": -32005,
"message": "Too many requests",
"data": {}
},
"id": "1"
}{
"jsonrpc": "2.0",
"error": {
"code": -32603,
"message": "内部错误",
"data": {}
},
"id": "1"
}{
"jsonrpc": "2.0",
"error": {
"code": -32002,
"message": "服务不可用",
"data": {}
},
"id": "1"
}{
"jsonrpc": "2.0",
"error": {
"code": -32003,
"message": "网关超时",
"data": {}
},
"id": "1"
}概述
getProgramAccountsV2 是标准 getProgramAccounts 方法的增强版本,专为需要有效查询特定Solana程序拥有的大量账户集的应用程序而设计。此方法引入了基于光标的分页和增量更新功能。
V2 中的新功能:
- 基于游标的分页:配置请求上限为每次1到10,000个账户
- 增量更新:使用
changedSinceSlot仅获取最近修改的账户 - 更好的性能:防止超时,并减少大数据集的内存使用
- 向后兼容:支持所有现有的
getProgramAccounts参数 - 可选的
withContext:true在result.context下添加slot和apiVersion;省略或false则不包含
主要优势
可扩展查询
通过有效分页处理拥有数百万个账户的程序
实时同步
使用
changedSinceSlot 进行增量更新和实时数据同步防止超时
以前超时的大型查询现在通过分页可靠运行
内存高效
分块处理数据而不是一次性加载所有内容到内存
分页最佳实践
重要的分页行为:分页结束仅在没有返回账户时指示。由于过滤,API返回的账户可能少于限制 - 始终继续分页,直到
paginationKey 是 null。基本分页模式
let allAccounts = [];
let paginationKey = null;
do {
const response = await fetch(`https://mainnet.helius-rpc.com/?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: '1',
method: 'getProgramAccountsV2',
params: [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
{
encoding: 'base64',
filters: [{ dataSize: 165 }],
limit: 5000,
...(paginationKey && { paginationKey })
}
]
})
});
const data = await response.json();
allAccounts.push(...data.result.accounts);
paginationKey = data.result.paginationKey;
} while (paginationKey);
增量更新
// Get only accounts modified since slot 150000000
const incrementalUpdate = await fetch(`https://mainnet.helius-rpc.com/?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: '1',
method: 'getProgramAccountsV2',
params: [
programId,
{
encoding: 'jsonParsed',
limit: 1000,
changedSinceSlot: 150000000
}
]
})
});
性能提示
最佳限制大小:对于大多数用例,每次请求 1,000-5,000 个账户的限制提供了性能和可靠性的最佳平衡。
- 从较小的限制开始(1000),并根据网络性能增加
- 使用合适的编码:便捷使用
jsonParsed,性能使用base64 - 应用过滤器在分页前减少数据集大小
- 存储
paginationKey以便查询中断时恢复 - 监控响应时间并相应调整限制
withContext(可选)
布尔值在程序配置对象(params[1])上。只有 result 的形状发生变化,不改变过滤器、限制或分页。
// Omitted or false
{ "jsonrpc": "2.0", "id": "1", "result": { "accounts": [], "paginationKey": null } }
// true — snapshot metadata plus page under `result.value`
{ "jsonrpc": "2.0", "id": "1", "result": {
"context": { "slot": 411895550, "apiVersion": "3.1.9" },
"value": { "accounts": [], "paginationKey": null }
}}
从 getProgramAccounts 迁移
从原方法迁移非常简单 - 只需替换方法名称并添加分页参数:{
"jsonrpc": "2.0",
"id": "1",
- "method": "getProgramAccounts",
+ "method": "getProgramAccountsV2",
"params": [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
{
"encoding": "base64",
"filters": [{ "dataSize": 165 }],
+ "limit": 5000
}
]
}
相关方法
getProgramAccounts
无分页的原始方法
getTokenAccountsByOwnerV2
用于令牌账户查询的 V2 方法
请求参数
string
必填
要查询账户的 Solana 程序公钥(地址),作为 base-58 编码字符串。
string
请求的承诺级别。
confirmedfinalizedprocessed
number
请求可以评估的最小槽。
boolean
当
true 时,返回 result.context(快照元数据:slot, apiVersion)并在 result.value 下嵌套 accounts 和 paginationKey。当 false 或省略时,这些字段直接出现在 result 上(例如 result.accounts)。应用相同的过滤器和限制。string
返回账户数据的编码格式。
jsonParsedbase58base64base64+zstd
object
请求账户数据的切片。
number
返回的字节数。
number
开始读取的字节偏移量。
number
每个请求返回的最大账户数量(1-10,000)。
string
Base-58编码的分页游标,用于获取后续页面。使用先前响应中的paginationKey。
number
仅返回在此槽位编号处或之后修改的账户。对增量更新很有用。
array
强大的过滤系统,可高效查询特定的Solana账户数据模式。
请求体
application/json
JSON-RPC 协议版本。
可用选项:
2.0 示例:
"2.0"
请求的唯一标识符。
示例:
"1"
要调用的 RPC 方法名称。
可用选项:
getProgramAccountsV2 示例:
"getProgramAccountsV2"
增强分页方法的参数。
要查询账户的 Solana 程序公钥(地址),以 base-58 编码的字符串形式。
示例:
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
此页面对您有帮助吗?
⌘I
getProgramAccountsV2
curl --request POST \
--url 'https://mainnet.helius-rpc.com/?api-key=' \
--header 'Content-Type: application/json' \
--data '
{
"jsonrpc": "2.0",
"id": "1",
"method": "getProgramAccountsV2",
"params": [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
{
"encoding": "base64",
"limit": 1000
}
]
}
'import requests
url = "https://mainnet.helius-rpc.com/?api-key="
payload = {
"jsonrpc": "2.0",
"id": "1",
"method": "getProgramAccountsV2",
"params": [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
{
"encoding": "base64",
"limit": 1000
}
]
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
jsonrpc: '2.0',
id: '1',
method: 'getProgramAccountsV2',
params: [
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
{encoding: 'base64', limit: 1000}
]
})
};
fetch('https://mainnet.helius-rpc.com/?api-key=', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://mainnet.helius-rpc.com/?api-key=",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'jsonrpc' => '2.0',
'id' => '1',
'method' => 'getProgramAccountsV2',
'params' => [
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
[
'encoding' => 'base64',
'limit' => 1000
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://mainnet.helius-rpc.com/?api-key="
payload := strings.NewReader("{\n \"jsonrpc\": \"2.0\",\n \"id\": \"1\",\n \"method\": \"getProgramAccountsV2\",\n \"params\": [\n \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n {\n \"encoding\": \"base64\",\n \"limit\": 1000\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://mainnet.helius-rpc.com/?api-key=")
.header("Content-Type", "application/json")
.body("{\n \"jsonrpc\": \"2.0\",\n \"id\": \"1\",\n \"method\": \"getProgramAccountsV2\",\n \"params\": [\n \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n {\n \"encoding\": \"base64\",\n \"limit\": 1000\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://mainnet.helius-rpc.com/?api-key=")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"jsonrpc\": \"2.0\",\n \"id\": \"1\",\n \"method\": \"getProgramAccountsV2\",\n \"params\": [\n \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n {\n \"encoding\": \"base64\",\n \"limit\": 1000\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"jsonrpc": "2.0",
"id": "1",
"result": {
"accounts": [
{
"pubkey": "CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY",
"account": {
"lamports": 15298080,
"owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"data": [
"2R9jLfiAQ9bgdcw6h8s44439",
"base64"
],
"executable": false,
"rentEpoch": 28,
"space": 165
}
}
],
"paginationKey": "8WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
}
}{
"jsonrpc": "2.0",
"error": {
"code": -32602,
"message": "Invalid params",
"data": {}
},
"id": "1"
}{
"jsonrpc": "2.0",
"error": {
"code": -32001,
"message": "Unauthorized",
"data": {}
},
"id": "1"
}{
"jsonrpc": "2.0",
"error": {
"code": -32005,
"message": "Too many requests",
"data": {}
},
"id": "1"
}{
"jsonrpc": "2.0",
"error": {
"code": -32603,
"message": "内部错误",
"data": {}
},
"id": "1"
}{
"jsonrpc": "2.0",
"error": {
"code": -32002,
"message": "服务不可用",
"data": {}
},
"id": "1"
}{
"jsonrpc": "2.0",
"error": {
"code": -32003,
"message": "网关超时",
"data": {}
},
"id": "1"
}