ウォレットのトークン残高 API:ERC-20 資産と送金履歴
残高がゼロでない ERC-20 トークン、トークン送金履歴、一括取得したメタデータを使ってウォレット資産ページを構築します。チェーンの対応範囲を確認し、結果をページ分割して取得し、decimals に応じて整数の数量を換算します。
ブロックチェーンのウォレットデータ API を使ってウォレット資産ページを構築します。残高がゼロでない ERC-20 保有資産にはトークン残高 API、ウォレットの履歴にはトークン送金 APIを使用します。開発者と AI エージェントは同じ認証付きリクエストを使用します。クエリを実行する前に GET /v1/status を取得し、選択したチェーンの data_features と data_status を確認してください。残高データの対応範囲はチェーンによって異なります。リクエストパラメータとレスポンススキーマは Data API リファレンスに記載されています。
このガイドでできること
- キーを使ってウォレットのトークン残高を取得し、残高がゼロでない ERC-20 保有資産をページ分割して取得します。
- 固定したブロック範囲内のウォレットの送金履歴を取得し、選択したアドレスのカーソルをたどります。
- トークンのメタデータを補完して、未取得のフィールドをそのまま保持しながら、生の整数残高とともに名前やシンボルを表示します。
ウォレット資産ページに必要な 3 種類のデータ
ウォレット資産ページには、アドレスの ERC-20 トークン残高、トークン送金履歴、トークンのメタデータを表示できます。Data API では、それぞれに対応するエンドポイントを提供しています。
- 残高:
GET /{chain}/addresses/{address}/balancesは、そのアドレスの残高がゼロでない ERC-20 トークンを、tokenアドレスの昇順で返します。トークンのsymbolとdecimalsは取得できる場合に含まれます。残高がないアドレスには、data: []を含む200を返します。 - 送金:
GET /{chain}/addresses/{address}/transfersは、必須のブロック範囲内でそのアドレスが関わったトークン送金を、(block_number, log_index)の降順で返します。 - トークンのメタデータ:
GET /{chain}/tokens/{token}は、コントラクトアドレスを使って 1 つのトークンの名前、シンボル、小数桁数、総供給量を取得します。POST /{chain}/tokens:batchは、同じメタデータを 1 回のリクエストで最大 100 アドレス分取得します。
3 つともベース URL に https://api.blockvectra.com/v1/data、リクエストヘッダーに x-api-key を使用し、チェーンの例として robinhood_mainnet を使います。それぞれ balances、transfers、token_metadata の機能に属します。各機能を提供するチェーンについては、対応チェーンページを参照してください。その機能に対応していないチェーンでは、エンドポイントは 422 no_coverage を返します。
リクエスト 1:アドレスの残高
このエンドポイントはパラメータが少ないため、ページで最初に行うリクエストに適しています。
{chain}(パスパラメータ、必須):チェーン識別子です。GET /chainsのエントリーのchain値(例:robinhood_mainnet)を指定します。完全一致で、大文字と小文字を区別します。別名や数値の Chain ID は受け付けません。{address}(パスパラメータ、必須):20 バイトのアドレスです。0xプレフィックスは任意で、大文字と小文字のどちらも受け付けます。limit(クエリパラメータ、任意):ページサイズです。デフォルトは 50 で、500 を超える値は 500 に切り詰められます。0または整数以外の値は400 bad_requestを返します。cursor(クエリパラメータ、任意):前のレスポンスのnext_cursorを変更せずに渡し、次のページを取得します。カーソルは、それを発行したチェーン、エンドポイント、クエリパラメータに対してのみ有効です。別の条件で再利用すると400 bad_requestを返します。
キーセット方式のページネーションを使用します。next_cursor は次のページがある場合にのみ含まれます。最終ページではキー自体が存在せず、null になることはありません。
export BLOCKVECTRA_API_KEY=rgw_your_api_key
curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/0x1111111111111111111111111111111111111111/balances?limit=50" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"レスポンスのエンベロープは AddressBalanceListEnvelope で、data と meta を含みます。data の各項目は AddressBalance です。
| フィールド | 型 | 説明 |
|---|---|---|
token | string(アドレス) | トークンのコントラクトアドレス。正規形は 0x に小文字の 16 進数 40 桁を続けた形式です。 |
balance | string(10 進数) | 生の整数残高。2^53 を超える場合があり、通常の 10 進数文字列として返されます。JSON 数値、指数表記、16 進数では返されません。 |
symbol | string または null | トークンのシンボル。取得できない場合は null です。 |
decimals | integer または null | トークンの小数桁数。0–255 の値で、取得できない場合は null です。 |
リクエスト 2:アドレスの送金
送金エンドポイントでは、ブロック範囲を明示する必要があります。from_block と to_block はどちらも必須で、from_block <= to_block を満たす必要があります。次のパラメータも使用します。
standard(クエリパラメータ、必須):erc20またはerc721。アドレス単位のクエリはerc1155に対応していません。指定すると422 no_coverageを返します。direction(クエリパラメータ、任意):in、out、anyのいずれか。デフォルトはanyで、アドレスから見た送金方向でフィルタリングします。token(クエリパラメータ、任意):結果を 1 つのトークンコントラクトに限定します。clamp(クエリパラメータ、任意):文字列trueをそのまま指定した場合にのみ有効になります。それ以外の値はfalseとして扱われます。
範囲の境界とファイナリティ:明示した to_block が as_of_block を超えると 409 not_indexed_yet を返します。ただし、clamp=true の場合は as_of_block まで切り詰められます。範囲がチェーンの上限(GET /chains の limits.max_window_blocks)を超えると 409 window_too_large を返します。ただし、clamp=true の場合は古い側から切り詰められます(from_block を引き上げ、to_block は固定します)。from_block 自体がすでに as_of_block を超えている場合は、clamp=true でも必ず 409 を返します。範囲が切り詰められた場合や一部のみが対応範囲内にある場合、レスポンスの meta.coverage は "partial" になります。それ以外の場合は "full" です。
送金レコードでは、ERC-20 の項目には amount、ERC-721 の項目には token_id が追加されます。どちらも token、standard、from、to、block_number、block_timestamp、tx_hash、tx_index、log_index を含みます。
export BLOCKVECTRA_API_KEY=rgw_your_api_key
# 1) Read as_of_block from the balances response.
AS_OF_BLOCK=$(curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/0x1111111111111111111111111111111111111111/balances" \
-H "x-api-key: $BLOCKVECTRA_API_KEY" | grep -o '"as_of_block":[0-9]*' | cut -d: -f2)
# 2) clamp=true truncates a too-wide window, or a to_block above as_of_block, instead of returning 409.
curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/0x1111111111111111111111111111111111111111/transfers?standard=erc20&from_block=0&to_block=$AS_OF_BLOCK&direction=any&clamp=true" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"ページネーションで全送金を取得する
アドレス送金エンドポイントの next_cursor は、次のページの存在を確約しません。返された行数がちょうど limit の場合にのみ含まれるため、next_cursor があるページでも、実際には最終ページである可能性があります。ページが空でも処理を止めず、キーがなくなるまで next_cursor をたどってください。
次のコードは、指定範囲内のすべての送金を取得します。
const address = "0x1111111111111111111111111111111111111111";
const head = await fetch(
`https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/${address}/balances`,
{ headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! } },
).then((r) => r.json());
const asOfBlock = head.meta.as_of_block;
const transfers: unknown[] = [];
let cursor: string | undefined;
do {
const url = new URL(
`https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/${address}/transfers`,
);
url.searchParams.set("standard", "erc20");
url.searchParams.set("from_block", "0");
url.searchParams.set("to_block", String(asOfBlock));
url.searchParams.set("limit", "500");
// clamp truncates from the older end
url.searchParams.set("clamp", "true");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, {
headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! },
});
const page = await res.json();
transfers.push(...page.data);
cursor = page.next_cursor; // absent on the last page
} while (cursor);リクエスト 3:トークンのメタデータと tokens:batch
GET /{chain}/tokens/{token} で 1 つのトークンを取得します。パスには {chain} と {token} のみを指定し、ページネーションはありません。レスポンスのエンベロープは TokenEnvelope で、data は Token です。
| フィールド | 型 | 説明 |
|---|---|---|
address | string(アドレス) | トークンのコントラクトアドレス。 |
standard | string | erc20、erc721、unknown のいずれか。 |
name | string または null | トークン名。取得できない場合は null です。 |
symbol | string または null | トークンのシンボル。取得できない場合は null です。 |
decimals | integer または null | トークンの小数桁数。0–255 の値で、取得できない場合は null です。 |
total_supply | string または null | 生の総供給量。API は decimals に応じた換算を行いません。取得できない場合は null です。 |
first_seen_block | integer (int64) | トークンが最初に検出されたブロック高。 |
metadata_updated_at | string(タイムスタンプ) | メタデータが最後に更新された UTC 時刻。 |
metadata_block | integer (int64) | メタデータを読み取ったブロック高。 |
metadata_status | string | ok、partial、unavailable のいずれか。 |
metadata_issues | object | name、symbol、decimals、total_supply をキーとする、フィールドごとの問題の記録。値は reverted、no_data、invalid_encoding、temporarily_unavailable のいずれかです。 |
{token} が有効な 20 バイトのアドレスでない場合は 400 bad_request、未知の {token} は 404 not_found、未知の {chain} は 404 unknown_chain を返します。
残高エンドポイントには、取得できる場合はすでに symbol と decimals が含まれていますが、どちらも null になる可能性があります。ウォレット内のすべてのトークンの名前と小数桁数を補完するには、POST /{chain}/tokens:batch を使用します。
- リクエストボディは
{"addresses": [...]}で、1 回のリクエストにつき最大 100 アドレスを指定できます。100 件を超える場合や、有効な 20 バイトのアドレスでない項目がある場合は、400 bad_requestを返します(順番に検証し、最初に見つかった無効なアドレスで失敗します)。 - 見つからないアドレスはエラーにならず、
data.missingに列挙されます。data.tokensには、メタデータが見つかったトークンのみが含まれます。 - 重複するアドレスは
tokensとmissingの両方で重複排除され、それぞれリクエスト内で最初に出現した順序で並びます。
export BLOCKVECTRA_API_KEY=rgw_your_api_key
# Single token
curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"
# Batch: up to 100 addresses per request
curl -s -X POST "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens:batch" \
-H "x-api-key: $BLOCKVECTRA_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"addresses":["0x1111111111111111111111111111111111111111","0x2222222222222222222222222222222222222222"]}'decimals に応じて数量を換算する
残高フィールド balance と ERC-20 送金フィールド amount は、10 進数文字列(UInt256String)で表された生の整数です。トークンの total_supply も、decimals に応じた換算が適用されていない、生のオンチェーン整数です。人が読みやすい数量を表示するには、そのトークンの decimals に応じて割り算を行います。
decimalsは、残高項目自体のsymbol/decimals、またはGET /{chain}/tokens/{token}とPOST /{chain}/tokens:batchから取得します。nullの場合もあります。- これらの値は
2^53を超える可能性があるため、JSON 数値で計算しないでください。TypeScript ではBigInt、Python ではDecimalを使い、精度の損失を防ぐために 10 進数文字列をそのまま解析します。
function toDisplayAmount(raw: string, decimals: number | null): string {
if (decimals === null) return raw; // no decimals metadata: keep the raw integer
const value = BigInt(raw);
const base = 10n ** BigInt(decimals);
const whole = value / base;
const fraction = (value % base)
.toString()
.padStart(decimals, "0")
.replace(/0+$/, "");
return fraction ? `${whole}.${fraction}` : whole.toString();
}
// balance.balance is a raw decimal string; decimals comes from the same item or tokens:batch.
const display = toDisplayAmount(balance.balance, balance.decimals);データの鮮度
チェーン単位の成功レスポンスには、すべて meta が含まれます。
as_of_block:チェーンの、書き込みが完全に完了した最新ブロックです。ブロック単位のエンドポイントは、この高さまでのデータを返します。safe_block:ノードのコンセンサス上のsafeブロックタグを示す指標です(不明な間はnull)。finalized_blockを下回ることはなく、レスポンスを切り詰めたり、拒否したり、遅延させたりすることもありません。finalized_block:ノードのコンセンサス上のfinalizedブロックタグを示す指標です(不明な間はnull)。レスポンスを切り詰めたり、拒否したり、遅延させたりすることはありません。この指標に基づいてどの程度の安全性が必要か(確認状況など)をクライアント側で判断します。coverage:"full"または"partial"。アドレス送金などのエンドポイントは、clampにより返す範囲が狭められた場合や、範囲の開始がチェーンの最初のインデックス済みブロックより前の場合に"partial"を返します。refreshed_at:レスポンスの元となるデータが最後に更新された時刻(UTC)です。nullの場合もあります。nullはデータの更新時刻が不明であることを意味し、古いデータとして扱う必要があります。ブロック単位のエンドポイントは常に値を返します。chain、chain_slug、chain_external_idも繰り返し含まれます。
よく使われる方法は、最初のレスポンスから meta.as_of_block を読み取って最新のインデックス済みブロックまで取得し、確認済みの状態を表示したい場合は meta.safe_block / meta.finalized_block を確認することです。
ページ読み込み 1 回あたりの CU 見積もり
各メソッドは、プラットフォームのプラン API から取得した CU 重み付けに基づいて課金されます。
呼び出しあたりの CU 重み付け
| メソッド | 呼び出しあたりの CU |
|---|---|
data.address_balances | 25 |
data.address_transfers | 25 |
data.tokens_batch | 10 |
ページを一回読み込む場合(概算)
残高リクエスト 1 回 + 転送履歴 3 ページ + tokens:batch リクエスト 1 回、合計 5 回の呼び出しで約 110 CU です。実際の利用量はページ数とトークン数によって変わります。
課金の判断基準と課金対象外のエラーレスポンスについては、課金ルールを参照してください。必要なものがインデックス済みの送金履歴ではなく最新ブロックのログである場合は、eth_getLogs に切り替えるか判断する前に、最新のノードデータとインデックス済み履歴の比較を読んでください。
次のステップ
- データセット一覧を見ると、BlockVectra がインデックス化しているすべてのデータセットを確認できます。
- 無料プランと料金を見ると、アカウントに含まれる内容を確認できます。
- コンソールにログインして、API keyを作成します。
最終更新: