# API số dư token trong ví: tài sản ERC-20 và lịch sử chuyển tiền

> Source: https://docs.blockvectra.com/vi/guides/wallet-assets/

Xây dựng trang tài sản ví bằng API dữ liệu ví blockchain: dùng [Token Balances API](https://blockvectra.com/vi/data/balances/) cho lượng ERC-20 nắm giữ khác 0 và [Token Transfers API](https://blockvectra.com/vi/data/transfers/) cho lịch sử ví. Nhà phát triển và AI Agent dùng cùng các yêu cầu có xác thực. Trước khi truy vấn, đọc [GET /v1/status](https://api.blockvectra.com/v1/data) và kiểm tra `data_features` cùng `data_status` của chuỗi đã chọn; phạm vi số dư khác nhau theo chuỗi. Tham số yêu cầu và lược đồ phản hồi nằm trong [tham chiếu Data API](https://docs.blockvectra.com/en/api/data/).

## Các tác vụ hướng dẫn này giúp bạn hoàn thành

* [Đọc số dư token trong ví](#request-1-address-balances) bằng key và phân trang lượng ERC-20 nắm giữ khác 0.
* [Đọc lịch sử chuyển tiền của ví](#request-2-address-transfers) trong cửa sổ khối cố định và theo con trỏ cho địa chỉ đã chọn.
* [Bổ sung siêu dữ liệu token](#request-3-token-metadata-and-tokensbatch) để hiển thị tên và ký hiệu bên cạnh số dư nguyên thô, giữ nguyên các trường thiếu.

## Ba loại dữ liệu trang tài sản ví cần

Trang tài sản ví có thể hiển thị số dư token ERC-20, lịch sử chuyển token và siêu dữ liệu token của một địa chỉ. Data API cung cấp endpoint cho từng loại:

* **Số dư**: `GET /{chain}/addresses/{address}/balances` trả số dư ERC-20 khác 0 của địa chỉ, sắp xếp tăng dần theo địa chỉ `token`, kèm `symbol` và `decimals` của token khi có. Địa chỉ không có số dư trả `200` với `data: []`.
* **Chuyển tiền**: `GET /{chain}/addresses/{address}/transfers` trả các chuyển token liên quan tới địa chỉ trong cửa sổ khối bắt buộc, sắp xếp giảm dần theo `(block_number, log_index)`.
* **Siêu dữ liệu token**: `GET /{chain}/tokens/{token}` đọc tên, ký hiệu, số chữ số thập phân và tổng cung của một token theo địa chỉ hợp đồng; `POST /{chain}/tokens:batch` đọc cùng siêu dữ liệu cho tối đa 100 địa chỉ trong một yêu cầu.

Cả ba dùng `https://api.blockvectra.com/v1/data` làm URL cơ sở và header yêu cầu `x-api-key`, với `robinhood_mainnet` là chuỗi ví dụ. Chúng lần lượt thuộc các khả năng `balances`, `transfers` và `token_metadata`; để xem chuỗi cung cấp từng khả năng, đọc trang [Chuỗi được hỗ trợ](https://docs.blockvectra.com/vi/chains/). Trên chuỗi không có khả năng tương ứng, endpoint trả `422 no_coverage`.

## Yêu cầu 1: số dư địa chỉ

Endpoint này cần ít tham số hơn, nên phù hợp làm yêu cầu đầu tiên cho trang:

* `{chain}` (tham số đường dẫn, bắt buộc): định danh chuỗi, là giá trị `chain` của một mục trong `GET /chains` (ví dụ `robinhood_mainnet`). Khớp chính xác và phân biệt chữ hoa, chữ thường; không chấp nhận bí danh hoặc Chain ID dạng số.
* `{address}` (tham số đường dẫn, bắt buộc): địa chỉ 20 byte; tiền tố `0x` là tùy chọn và chấp nhận cả chữ hoa lẫn chữ thường.
* `limit` (tham số truy vấn, tùy chọn): kích thước trang. Mặc định 50; giá trị trên 500 được giới hạn thành 500; `0` hoặc giá trị không nguyên trả `400 bad_request`.
* `cursor` (tham số truy vấn, tùy chọn): `next_cursor` của phản hồi trước, truyền lại nguyên vẹn để lấy trang tiếp theo. Con trỏ chỉ hợp lệ cho chuỗi, endpoint và tham số truy vấn đã tạo ra nó; dùng lại ở nơi khác trả `400 bad_request`.

Endpoint phân trang theo khóa: `next_cursor` chỉ xuất hiện khi có trang tiếp theo. Ở trang cuối, khóa hoàn toàn không có, tuyệt đối không phải `null`.

**cURL**

```bash
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"
```


  **TypeScript**

```ts
const address = "0x1111111111111111111111111111111111111111";
const url = new URL(
  `https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/${address}/balances`,
);
url.searchParams.set("limit", "50");

const res = await fetch(url, {
  headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! },
});
const balanceBody = await res.json();
console.log(balanceBody.data, balanceBody.meta);

// npx tsx example.mts
```


  **Python**

```python
import os
import requests

address = "0x1111111111111111111111111111111111111111"
res = requests.get(
    f"https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/{address}/balances",
    params={"limit": 50},
    headers={"x-api-key": os.environ["BLOCKVECTRA_API_KEY"]},
)
res.raise_for_status()
balance_body = res.json()
print(balance_body["data"], balance_body["meta"])
```


Cấu trúc phản hồi là `AddressBalanceListEnvelope`, chứa `data` và `meta`. Mỗi mục trong `data` là một `AddressBalance`:

| Trường     | Kiểu                  | Mô tả                                                                                                                               |
| ---------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `token`    | `string` (địa chỉ)    | Địa chỉ hợp đồng token; dạng chuẩn là `0x` cộng 40 chữ số hex viết thường.                                                          |
| `balance`  | `string` (thập phân)  | Số dư nguyên thô, có thể vượt `2^53`, trả dưới dạng chuỗi thập phân thuần — tuyệt đối không phải số JSON, ký pháp khoa học hay hex. |
| `symbol`   | `string` hoặc `null`  | Ký hiệu token, hoặc `null` khi không có.                                                                                            |
| `decimals` | `integer` hoặc `null` | Số chữ số thập phân của token, `0`–`255`, hoặc `null` khi không có.                                                                 |

## Yêu cầu 2: chuyển tiền của địa chỉ

Endpoint chuyển tiền yêu cầu cửa sổ khối tường minh: cả `from_block` và `to_block` đều bắt buộc và phải thỏa `from_block <= to_block`. Endpoint cần thêm vài tham số:

* `standard` (tham số truy vấn, bắt buộc): `erc20` hoặc `erc721`. Truy vấn theo địa chỉ không bao phủ `erc1155`; truyền giá trị đó trả `422 no_coverage`.
* `direction` (tham số truy vấn, tùy chọn): `in`, `out` hoặc `any`; mặc định `any` và lọc theo hướng tương đối với địa chỉ.
* `token` (tham số truy vấn, tùy chọn): giới hạn kết quả ở một hợp đồng token.
* `clamp` (tham số truy vấn, tùy chọn): chỉ chuỗi nguyên văn `true` mới bật tùy chọn này; mọi giá trị khác được coi là `false`.

Giới hạn cửa sổ và tính chung cuộc: `to_block` tường minh vượt `as_of_block` trả `409 not_indexed_yet`, trừ khi `clamp=true` cắt xuống `as_of_block`; cửa sổ rộng hơn giới hạn của chuỗi (`limits.max_window_blocks` từ `GET /chains`) trả `409 window_too_large`, trừ khi `clamp=true` cắt từ đầu cũ hơn (tăng `from_block` và giữ nguyên `to_block`). Nếu bản thân `from_block` đã vượt `as_of_block`, vẫn trả lỗi `409` ngay cả với `clamp=true`. Khi cửa sổ bị cắt hoặc chỉ được bao phủ một phần, `meta.coverage` trong phản hồi là `"partial"`; nếu không là `"full"`.

Trong bản ghi chuyển tiền, mục ERC-20 thêm `amount`; mục ERC-721 thêm `token_id`. Cả hai đều có `token`, `standard`, `from`, `to`, `block_number`, `block_timestamp`, `tx_hash`, `tx_index` và `log_index`.

**cURL**

```bash
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"
```


  **TypeScript**

```ts
const address = "0x1111111111111111111111111111111111111111";

// 1) Read as_of_block from any previous response's meta.
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());

// 2) Use as_of_block as the transfer window's upper bound.
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(head.meta.as_of_block));
url.searchParams.set("direction", "any");
url.searchParams.set("clamp", "true");

const res = await fetch(url, {
  headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! },
});
const body = await res.json();
console.log(body.data, body.meta);

// npx tsx example.mts
```


  **Python**

```python
import os
import requests

address = "0x1111111111111111111111111111111111111111"
headers = {"x-api-key": os.environ["BLOCKVECTRA_API_KEY"]}

# 1) Read as_of_block from any previous response's meta.
head = requests.get(
    f"https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/{address}/balances",
    headers=headers,
).json()

# 2) Use as_of_block as the transfer window's upper bound.
res = requests.get(
    f"https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/{address}/transfers",
    params={
        "standard": "erc20",
        "from_block": 0,
        "to_block": head["meta"]["as_of_block"],
        "direction": "any",
        "clamp": "true",
    },
    headers=headers,
)
res.raise_for_status()
body = res.json()
print(body["data"], body["meta"])
```


## Phân trang qua toàn bộ chuyển tiền

`next_cursor` của endpoint chuyển tiền theo địa chỉ mang tính dự đoán: chỉ xuất hiện khi trang trả đúng `limit` hàng, nên trang có thể có `next_cursor` nhưng vẫn là trang cuối. Đừng dừng khi trang rỗng; theo `next_cursor` cho đến khi khóa không còn.

Mã bên dưới lấy mọi chuyển tiền trong cửa sổ:

**TypeScript**

```ts
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);
```


  **Python**

```python
import os
import requests

address = "0x1111111111111111111111111111111111111111"
head = requests.get(
    f"https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/{address}/balances",
    headers={"x-api-key": os.environ["BLOCKVECTRA_API_KEY"]},
).json()
as_of_block = head["meta"]["as_of_block"]
transfers = []
cursor = None

while True:
    params = {
        "standard": "erc20",
        "from_block": 0,
        "to_block": as_of_block,
        "limit": 500,
        # clamp truncates from the older end
        "clamp": "true",
    }
    if cursor:
        params["cursor"] = cursor
    res = requests.get(
        f"https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/{address}/transfers",
        params=params,
        headers={"x-api-key": os.environ["BLOCKVECTRA_API_KEY"]},
    )
    res.raise_for_status()
    page = res.json()
    transfers.extend(page["data"])
    cursor = page.get("next_cursor")  # absent on the last page
    if not cursor:
        break
```


## Yêu cầu 3: siêu dữ liệu token và tokens:batch

Đọc một token bằng `GET /{chain}/tokens/{token}`; đường dẫn chỉ nhận `{chain}` và `{token}`, không phân trang. Cấu trúc phản hồi là `TokenEnvelope`, và `data` là một `Token`:

| Trường                | Kiểu                     | Mô tả                                                                                                                                                                       |
| --------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `address`             | `string` (địa chỉ)       | Địa chỉ hợp đồng token.                                                                                                                                                     |
| `standard`            | `string`                 | `erc20`, `erc721` hoặc `unknown`.                                                                                                                                           |
| `name`                | `string` hoặc `null`     | Tên token, hoặc `null` khi không có.                                                                                                                                        |
| `symbol`              | `string` hoặc `null`     | Ký hiệu token, hoặc `null` khi không có.                                                                                                                                    |
| `decimals`            | `integer` hoặc `null`    | Số chữ số thập phân của token, `0`–`255`, hoặc `null` khi không có.                                                                                                         |
| `total_supply`        | `string` hoặc `null`     | Tổng cung thô; API không quy đổi theo `decimals`. `null` khi không có.                                                                                                      |
| `first_seen_block`    | `integer` (int64)        | Độ cao khối nơi token được thấy lần đầu.                                                                                                                                    |
| `metadata_updated_at` | `string` (dấu thời gian) | Thời gian UTC siêu dữ liệu được cập nhật lần cuối.                                                                                                                          |
| `metadata_block`      | `integer` (int64)        | Độ cao khối tại đó siêu dữ liệu được đọc.                                                                                                                                   |
| `metadata_status`     | `string`                 | `ok`, `partial` hoặc `unavailable`.                                                                                                                                         |
| `metadata_issues`     | `object`                 | Bản ghi vấn đề theo từng trường, với khóa `name`, `symbol`, `decimals`, `total_supply` và giá trị `reverted`, `no_data`, `invalid_encoding` hoặc `temporarily_unavailable`. |

`{token}` không phải địa chỉ 20 byte hợp lệ trả `400 bad_request`; `{token}` không được biết đến trả `404 not_found`; `{chain}` không được biết đến trả `404 unknown_chain`.

Endpoint số dư đã bao gồm `symbol` và `decimals` khi có, nhưng cả hai có thể là `null`. Để bổ sung tên và số chữ số thập phân cho mọi token trong ví, dùng `POST /{chain}/tokens:batch`:

* Body yêu cầu là `{"addresses": [...]}` với tối đa 100 địa chỉ mỗi yêu cầu; hơn 100 mục, hoặc một mục không phải địa chỉ 20 byte hợp lệ, trả `400 bad_request` (thất bại tại địa chỉ không hợp lệ đầu tiên được duyệt tới).
* Địa chỉ không tìm thấy không gây lỗi; chúng nằm trong `data.missing`, còn `data.tokens` chỉ chứa token có siêu dữ liệu tìm thấy.
* Địa chỉ trùng được loại trùng trong cả `tokens` và `missing`, mỗi danh sách theo thứ tự xuất hiện đầu tiên trong yêu cầu.

**cURL**

```bash
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"]}'
```


  **TypeScript**

```ts
// Single token
const single = await fetch(
  "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111",
  { headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! } },
).then((r) => r.json());
console.log(single.data);

// Batch: group by 100 addresses to enrich the tokens from the balances response
const BATCH_SIZE = 100;
const addresses = balanceBody.data.map((item: { token: string }) => item.token);
const tokens = new Map<string, unknown>();
const missing: string[] = [];

for (let i = 0; i < addresses.length; i += BATCH_SIZE) {
  const res = await fetch("https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens:batch", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": process.env.BLOCKVECTRA_API_KEY!,
    },
    body: JSON.stringify({ addresses: addresses.slice(i, i + BATCH_SIZE) }),
  });
  const body = await res.json();
  for (const token of body.data.tokens) tokens.set(token.address, token);
  missing.push(...body.data.missing);
}

// npx tsx example.mts
```


  **Python**

```python
import os
import requests

headers = {"x-api-key": os.environ["BLOCKVECTRA_API_KEY"]}

# Single token
single = requests.get(
    "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111",
    headers=headers,
).json()
print(single["data"])

# Batch: group by 100 addresses to enrich the tokens from the balances response
BATCH_SIZE = 100
addresses = [item["token"] for item in balance_body["data"]]
tokens = {}
missing = []

for i in range(0, len(addresses), BATCH_SIZE):
    res = requests.post(
        "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens:batch",
        json={"addresses": addresses[i : i + BATCH_SIZE]},
        headers={**headers, "Content-Type": "application/json"},
    )
    res.raise_for_status()
    body = res.json()
    for token in body["data"]["tokens"]:
        tokens[token["address"]] = token
    missing.extend(body["data"]["missing"])
```


## Quy đổi số tiền theo decimals

Trường số dư `balance` và trường chuyển tiền ERC-20 `amount` là số nguyên thô biểu diễn bằng chuỗi thập phân (`UInt256String`); `total_supply` của token cũng là số nguyên thô trên chuỗi chưa quy đổi theo `decimals`. Để hiển thị số lượng dễ đọc, quy đổi theo `decimals` của token đó.

* `decimals` lấy từ `symbol`/`decimals` của chính mục số dư, hoặc từ `GET /{chain}/tokens/{token}` và `POST /{chain}/tokens:batch`; có thể là `null`.
* Các giá trị này có thể vượt `2^53`, nên đừng tính toán bằng số JSON: dùng `BigInt` trong TypeScript và `Decimal` trong Python, phân tích nguyên chuỗi thập phân để tránh mất độ chính xác.

**TypeScript**

```ts
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);
```


  **Python**

```python
from decimal import Decimal


def to_display_amount(raw: str, decimals: int | None) -> str:
    if decimals is None:
        return raw  # no decimals metadata: keep the raw integer
    value = Decimal(raw)  # parse the decimal string exactly
    return format(value.scaleb(-decimals).normalize(), "f")


# balance["balance"] is a raw decimal string; decimals comes from the same item or tokens:batch.
display = to_display_amount(balance["balance"], balance["decimals"])
```


## Độ mới của dữ liệu

Mọi phản hồi thành công trong phạm vi chuỗi đều có `meta`:

* `as_of_block`: khối mới nhất của chuỗi đã được ghi đầy đủ. Endpoint theo khối phục vụ dữ liệu đến độ cao này.
* `safe_block`: mốc chỉ ra block tag đồng thuận `safe` của nút (`null` khi chưa biết). Không bao giờ thấp hơn `finalized_block`, và không cắt, từ chối hoặc trì hoãn phản hồi.
* `finalized_block`: mốc chỉ ra block tag đồng thuận `finalized` của nút (`null` khi chưa biết). Không cắt, từ chối hoặc trì hoãn phản hồi; client quyết định mức an toàn cần từ mốc đó (chẳng hạn trạng thái xác nhận).
* `coverage`: `"full"` hoặc `"partial"`. Chuyển tiền theo địa chỉ và endpoint tương tự báo `"partial"` khi `clamp` thu hẹp cửa sổ được phục vụ, hoặc khi cửa sổ bắt đầu trước khối đầu tiên được lập chỉ mục của chuỗi.
* `refreshed_at`: thời điểm dữ liệu của phản hồi được cập nhật lần cuối (UTC). Có thể là `null`: `null` nghĩa là chưa biết thời gian cập nhật dữ liệu và cần coi dữ liệu là cũ; endpoint theo khối luôn trả giá trị.
* Phản hồi cũng lặp lại `chain`, `chain_slug` và `chain_external_id`.

Cách dùng phổ biến: đọc `meta.as_of_block` từ phản hồi đầu tiên bất kỳ để đọc đến khối mới nhất đã lập chỉ mục, và kiểm tra `meta.safe_block` / `meta.finalized_block` nếu muốn hiển thị trạng thái đã xác nhận.

## Ước tính CU cho một lần tải trang

Mỗi phương thức được tính phí theo trọng số CU, đọc từ API gói của nền tảng:

**Trọng số CU mỗi lệnh gọi**

| Phương thức | CU mỗi lệnh gọi |
| --- | --- |
| `data.address_balances` | 25 |
| `data.address_transfers` | 25 |
| `data.tokens_batch` | 10 |

**Một lần tải trang (ước tính)**

1 yêu cầu balances + 3 trang transfer + 1 yêu cầu `tokens:batch`, tổng cộng 5 lệnh gọi, khoảng 110 CU. Mức sử dụng thực tế phụ thuộc vào số lượng trang và token.

Để quyết định tính phí và biết phản hồi lỗi không tính phí, xem [quy tắc thanh toán](https://docs.blockvectra.com/en/guides/billing-rules/). Nếu cần log từ các khối mới nhất thay vì lịch sử chuyển tiền được lập chỉ mục, đọc [Dữ liệu nút gần đây và lịch sử được lập chỉ mục](https://docs.blockvectra.com/vi/guides/logs-vs-transfers/) trước khi quyết định chuyển sang `eth_getLogs`.

## Các bước tiếp theo

* [Duyệt danh mục bộ dữ liệu](https://blockvectra.com/vi/data/) để xem mọi bộ dữ liệu BlockVectra lập chỉ mục.
* [Xem gói miễn phí và giá](https://blockvectra.com/vi/pricing/#free) để kiểm tra những gì tài khoản bao gồm.
* [Đăng nhập bảng điều khiển](https://console.blockvectra.com/login/?next=%2Fkeys%2F) để tạo API key.
