# Create Plant
POST /plants
Creates a new plant in the store
# Delete Plant
DELETE /plants/{id}
Deletes a single plant based on the ID supplied
# Get Plants
GET /plants
Returns all plants from the system that the user has access to
# Introduction
Example section for showcasing API endpoints
If you're not looking to build API reference documentation, you can delete
this section by removing the api-reference folder.
## Welcome
There are two ways to build API documentation: [OpenAPI](https://mintlify.com/docs/api-playground/openapi/setup) and [MDX components](https://mintlify.com/docs/api-playground/mdx/configuration). For the starter kit, we are using the following OpenAPI specification.
View the OpenAPI specification file
## Authentication
All API endpoints are authenticated using Bearer tokens and picked up from the specification file.
```json
"security": [
{
"bearerAuth": []
}
]
```
# Authentication
### Per-Client Security Schemes
This SDK supports the following security scheme globally:
| Name | Type | Scheme |
| -------- | ------ | ------- |
| `apiKey` | apiKey | API key |
The AvaCloud SDK can be used without an API key, but rate limits will be lower. Adding an API key allows for higher rate limits. To get an API key, create one via [AvaCloud](https://app.avacloud.io/) and securely store it. Whether or not you use an API key, you can still interact with the SDK effectively, but the API key provides performance benefits for higher request volumes.
```javascript
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
const avaCloudSDK = new AvaCloudSDK({
apiKey: "",
chainId: "43114",
network: "mainnet",
});
async function run() {
const result = await avaCloudSDK.metrics.healthCheck.metricsHealthCheck();
// Handle the result
console.log(result);
}
run();
```
Never hardcode your API key directly into your code. Instead, securely store it and retrieve it from an environment variable, a secrets manager, or a dedicated configuration storage mechanism. This ensures that sensitive information remains protected and is not exposed in version control or publicly accessible code.
# Cross-Chain EVM Requests
### Setup and Authentication
In order to utilize your accounts rate limits, you will need to make API requests with an API key. You can generate API Keys from the AvaCloud portal. Once you've created and retrieved that, you will be able to make authenticated queries by passing in your API key in the x-glacier-api-key header of your HTTP request.
An example curl request can be found below:
```bash
curl -H "Content-Type: Application/json" -H "x-glacier-api-key: your_api_key" \
"https://glacier-api.avax.network/v1/chains"
```
### Rate Limits
The Glacier API has rate limits in place to maintain it's stability and protect from bursts of incoming traffic. The rate limits associated with various plans can be found within AvaCloud.
When you hit your rate limit, the server will respond with a 429 http response code, and response headers to help you determine when you should start to make additional requests. The response headers follow the standards set in the RateLimit header fields for HTTP draft from the Internet Engineering Task Force.
With every response with a valid api key, the server will include the following headers:
ratelimit-policy - The rate limit policy tied to your api key.
ratelimit-limit - The number of requests you can send according to your policy.
ratelimit-remaining - How many request remaining you can send in the period for your policy
For any request after the rate limit has been reached, the server will also respond with these headers:
ratelimit-reset
retry-after
Both of these headers are set to the number of seconds until your period is over and requests will start succeeding again.
If you start receiving rate limit errors with the 429 response code, we recommend you discontinue sending requests to the server. You should wait to retry requests for the duration specified in the response headers. Alternatively, you can implement an exponential backoff algorithm to prevent continuous errors. Failure to discontinue requests may result in being temporarily blocked from accessing the API.
Error Types
The Glacier API generates standard error responses along with error codes based on provided requests and parameters.
Typically, response codes within the 2XX range signifies successful requests, while those within the 4XX range points to errors originating from the client's side. Meanwhile, response codes within the 5XX range indicates problems on the server's side.
# Custom HTTP Client
The TypeScript SDK makes API calls using an HTTPClient that wraps the native [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). This client is a thin wrapper around `fetch` and provides the ability to attach hooks around the request lifecycle that can be used to modify the request or handle errors and response.
The `HTTPClient` constructor takes an optional `fetcher` argument that can be used to integrate a third-party HTTP client or when writing tests to mock out the HTTP client and feed in fixtures.
The following example shows how to use the `beforeRequest` hook to to add a custom header and a timeout to requests and how to use the `requestError` hook to log errors:
```javascript
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
import { HTTPClient } from "@avalabs/avacloud-sdk/lib/http";
const httpClient = new HTTPClient({
// fetcher takes a function that has the same signature as native `fetch`.
fetcher: (request) => {
return fetch(request);
}
});
httpClient.addHook("beforeRequest", (request) => {
const nextRequest = new Request(request, {
signal: request.signal || AbortSignal.timeout(5000)
});
nextRequest.headers.set("x-custom-header", "custom value");
return nextRequest;
});
httpClient.addHook("requestError", (error, request) => {
console.group("Request Error");
console.log("Reason:", `${error}`);
console.log("Endpoint:", `${request.method} ${request.url}`);
console.groupEnd();
});
const sdk = new AvaCloudSDK({ httpClient });
```
# Error Handling
All SDK methods return a response object or throw an error. If Error objects are specified in your OpenAPI Spec, the SDK will throw the appropriate Error type.
| Error Object | Status Code | Content Type |
| :------------------------- | :---------- | :--------------- |
| errors.BadRequest | 400 | application/json |
| errors.Unauthorized | 401 | application/json |
| errors.Forbidden | 403 | application/json |
| errors.NotFound | 404 | application/json |
| errors.TooManyRequests | 429 | application/json |
| errors.InternalServerError | 500 | application/json |
| errors.BadGateway | 502 | application/json |
| errors.ServiceUnavailable | 503 | application/json |
| errors.SDKError | 4xx-5xx | / |
Validation errors can also occur when either method arguments or data returned from the server do not match the expected format. The SDKValidationError that is thrown as a result will capture the raw value that failed validation in an attribute called `rawValue`. Additionally, a `pretty()` method is available on this error that can be used to log a nicely formatted string since validation errors can list many issues and the plain error string may be difficult read when debugging.
```javascript
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
import {
BadGateway,
BadRequest,
Forbidden,
InternalServerError,
NotFound,
SDKValidationError,
ServiceUnavailable,
TooManyRequests,
Unauthorized,
} from "@avalabs/avacloud-sdk/models/errors";
const avaCloudSDK = new AvaCloudSDK({
apiKey: "",
chainId: "43114",
network: "mainnet",
});
async function run() {
try {
await avaCloudSDK.data.nfts.reindexNft({
address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
tokenId: "145",
});
} catch (err) {
switch (true) {
case err instanceof SDKValidationError: {
// Validation errors can be pretty-printed
console.error(err.pretty());
// Raw value may also be inspected
console.error(err.rawValue);
return;
}
case err instanceof BadRequest: {
// Handle err.data$: BadRequestData
console.error(err);
return;
}
case err instanceof Unauthorized: {
// Handle err.data$: UnauthorizedData
console.error(err);
return;
}
case err instanceof Forbidden: {
// Handle err.data$: ForbiddenData
console.error(err);
return;
}
case err instanceof NotFound: {
// Handle err.data$: NotFoundData
console.error(err);
return;
}
case err instanceof TooManyRequests: {
// Handle err.data$: TooManyRequestsData
console.error(err);
return;
}
case err instanceof InternalServerError: {
// Handle err.data$: InternalServerErrorData
console.error(err);
return;
}
case err instanceof BadGateway: {
// Handle err.data$: BadGatewayData
console.error(err);
return;
}
case err instanceof ServiceUnavailable: {
// Handle err.data$: ServiceUnavailableData
console.error(err);
return;
}
default: {
throw err;
}
}
}
}
run();
```
# Getting Started
### AvaCloud SDK
The AvaCloud SDK provides web3 application developers with multi-chain data related to Avalanche's primary network, Avalanche L1s, and Ethereum. With the Data API, you can easily build products that leverage real-time and historical transaction and transfer history, native and token balances, and various types of token metadata.
The SDK is currently available in TypeScript, with more languages coming soon. If you are interested in a language that is not listed, please reach out to us in the [#dev-tools](https://discord.com/channels/578992315641626624/1280920394236297257) channel in the [Avalanche Discord](https://discord.gg/avax).
[https://www.npmjs.com/package/@avalabs/avacloud-sdk](https://www.npmjs.com/package/@avalabs/avacloud-sdk)
[https://github.com/ava-labs/avacloud-sdk-typescript?tab=readme-ov-file#-avacloud-sdk-typescript-](https://github.com/ava-labs/avacloud-sdk-typescript?tab=readme-ov-file#-avacloud-sdk-typescript-)
### SDK Installation
```npm NPM
npm add @avalabs/avacloud-sdk
```
```pnpm PNPM
pnpm add @avalabs/avacloud-sdk
```
```bun Bun
bun add @avalabs/avacloud-sdk
```
```yarn Yarn
yarn add @avalabs/avacloud-sdk zod
# Note that Yarn does not install peer dependencies automatically. You will need
# to install zod as shown above.
```
### SDK Example Usage
```javascript
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
const avaCloudSDK = new AvaCloudSDK({
apiKey: "",
chainId: "43114",
network: "mainnet",
});
async function run() {
const result = await avaCloudSDK.metrics.healthCheck.metricsHealthCheck();
// Handle the result
console.log(result);
}
run();
```
Refer to the code samples provided for each route to see examples of how to use them in the SDK. Explore routes here [Data API](/data-api/health-check/get-the-health-of-the-service),
[Metrics API](/metrics-api/health-check/get-the-health-of-the-service) & [Webhooks API](/webhooks-api/webhooks/list-webhooks).
# Global Parameters
Certain parameters are configured globally. These parameters may be set on the SDK client instance itself during initialization. When configured as an option during SDK initialization, These global values will be used as defaults on the operations that use them. When such operations are called, there is a place in each to override the global value, if needed.
For example, you can set `chainId` to `43114` at SDK initialization and then you do not have to pass the same value on calls to operations like getBlock. But if you want to do so you may, which will locally override the global setting. See the example code below for a demonstration.
### Available Globals
The following global parameters are available.
| Name | Type | Required | Description |
| :-------- | :---------------------------- | :------- | :------------------------------------------------------- |
| `chainId` | string | No | A supported EVM chain id, chain alias, or blockchain id. |
| `network` | components.GlobalParamNetwork | No | A supported network type, either mainnet or a testnet. |
Example
```javascript
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
const avaCloudSDK = new AvaCloudSDK({
apiKey: "",
chainId: "43114", // Sets chainId globally, will be used if not passed during method call.
network: "mainnet",
});
async function run() {
const result = await avaCloudSDK.data.evm.blocks.getBlock({
blockId: "0x17533aeb5193378b9ff441d61728e7a2ebaf10f61fd5310759451627dfca2e7c",
chainId: "", // Override the globally set chain id.
});
// Handle the result
console.log(result)
}
run();
```
# Pagination
Some of the endpoints in this SDK support pagination. To use pagination, you make your SDK calls as usual, but the returned response object will also be an async iterable that can be consumed using the `for await...of` syntax.
Here's an example of one such pagination call:
```javascript
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
const avaCloudSDK = new AvaCloudSDK({
apiKey: "",
chainId: "43114",
network: "mainnet",
});
async function run() {
const result = await avaCloudSDK.metrics.evm.chains.listChains({
network: "mainnet",
});
for await (const page of result) {
// Handle the page
console.log(page);
}
}
run();
```
# Retries
Some of the endpoints in this SDK support retries. If you use the SDK without any configuration, it will fall back to the default retry strategy provided by the API. However, the default retry strategy can be overridden on a per-operation basis, or across the entire SDK.
To change the default retry strategy for a single API call, simply provide a retryConfig object to the call:
```javascript
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
const avaCloudSDK = new AvaCloudSDK({
apiKey: "",
chainId: "43114",
network: "mainnet",
});
async function run() {
const result = await avaCloudSDK.metrics.healthCheck.metricsHealthCheck({
retries: {
strategy: "backoff",
backoff: {
initialInterval: 1,
maxInterval: 50,
exponent: 1.1,
maxElapsedTime: 100,
},
retryConnectionErrors: false,
},
});
// Handle the result
console.log(result);
}
run();
```
If you'd like to override the default retry strategy for all operations that support retries, you can provide a retryConfig at SDK initialization:
```javascript
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
const avaCloudSDK = new AvaCloudSDK({
retryConfig: {
strategy: "backoff",
backoff: {
initialInterval: 1,
maxInterval: 50,
exponent: 1.1,
maxElapsedTime: 100,
},
retryConnectionErrors: false,
},
apiKey: "",
chainId: "43114",
network: "mainnet",
});
async function run() {
const result = await avaCloudSDK.metrics.healthCheck.metricsHealthCheck();
// Handle the result
console.log(result);
}
run();
```
# Changelog
### Jan 08th, 2025
**🚀 New API Endpoints: Aggregated Transactions & Blocks Across L1 Chains & Avalanche C-Chain**
![Aggregated Transactions & Blocks](https://mintlify.s3.us-west-1.amazonaws.com/avalabs-47ea3976/images/aggregated-blocks-transactions.png)
We're excited to introduce **two new API endpoints** that provide **aggregated transaction and block data** across **all supported L1 chains and the Avalanche C-Chain**! These endpoints allow developers to **query, filter, and sort** blockchain data efficiently, unlocking powerful insights across multiple chains.
**đź“Ś Get Started**
* [List Latest Blocks for All Supported EVM Chains](https://developers.avacloud.io/data-api/evm-chains/list-latest-blocks-for-all-supported-evm-chains)
* [List Latest Transactions for All Supported EVM Chains](https://developers.avacloud.io/data-api/evm-chains/list-latest-transactions-for-all-supported-evm-chains)
These enhancements **simplify multi-chain data retrieval**, making it easier for developers to **build cross-chain analytics, wallets, and monitoring tools**. Try them out today and streamline your blockchain data integration! 🚀
***
### Dec 20th, 2024
**Token Reputation Analysis 🛡️🔍**
![Token Reputation Analysis](https://mintlify.s3.us-west-1.amazonaws.com/avalabs-47ea3976/images/token-reputation.png)
We’re thrilled to introduce Token Reputation Analysis, a new feature for identifying potential security risks with ERC20 tokens on the Avalanche C-Chain!
This update adds a new field, `tokenReputation`, to the [List ERC-20 balances](/data-api/evm-balances/list-erc-20-balances) response. The field categorizes tokens into the following reputations:
* `Benign`: Tokens considered safe based on security analysis.
* `Malicious`: Tokens flagged for suspicious activity, spam, or phishing.
* `null`: Reputation unknown.
**Example Usage**
Here’s an example API call:
```bash
curl -X 'GET' \
'https://glacier-api.avax.network/v1/chains/43114/addresses/0x51a679853D9582d29FF9e23ae336a0947BD0f337/balances:listErc20?pageSize=10&filterSpamTokens=true¤cy=usd' \
-H 'accept: application/json' | jq
```
As you can see in the response, the `$AVA` token is flagged as `Malicious`:
```json
{
"erc20TokenBalances": [
{
"ercType": "ERC-20",
"chainId": "43114",
"address": "0x397e48aF37b7d7660D0Aee74c35b2218D7EFca12",
"name": "$AVA (https://avalaunch.farm)",
"symbol": "$AVA",
"decimals": 2,
"balance": "8200000",
"tokenReputation": "Malicious"
},
{
"ercType": "ERC-20",
"chainId": "43114",
"address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"name": "USD Coin",
"symbol": "USDC",
"decimals": 6,
"balance": "1515448052896",
"tokenReputation": "Benign"
}
]
}
```
Try it out [here](/data-api/evm-balances/list-erc-20-balances)!
***
### Nov 25th, 2024
**Avalanche9000 (Etna Upgrade 🌋)**
![Avalanche9000](https://mintlify.s3.us-west-1.amazonaws.com/avalabs-47ea3976/images/avalanche9000.jpeg)
**New endpoint to list L1 validators:**
* Added new endpoint to [list all or specific L1 validators](/data-api/primary-network/list-l1-validators)
* Filters include `L1ValidationID`, `SubnetID`, `NodeID`, and `IncludeInactiveL1Validators`.
**Updated transactions endpoint:**
* [List latest transactions on the primary network](/data-api/primary-network-transactions/list-latest-transactions) now supports `l1ValidationID` to fetch transactions linked to specific L1 validators (e.g., `ConvertSubnetToL1Tx`).
* L1 transactions are sorted in descending order with additional filters like `timestamp` and `txTypes`.
**Enhanced transaction properties:**
* P-Chain Transaction responses now include:
* L1 validator details (`validationID`, `nodeID`, `weight`, `balances`, etc.).
* Burned AVAX details for increasing L1 validator balance.
* Validator manager details (`BlockchainID` and `ContractAddress`).
**New block properties:**
* P-Chain blocks now include:
* `ActiveL1Validators` (total active L1 validators).
* `L1ValidatorsAccruedFees` (fees from active L1 validators).
**New subnet properties:**
* Subnet details now have:
* `IsL1` to indicate if a subnet has been converted to an L1.
* Validator manager details for L1 subnets.
These changes support seamless management and visibility of L1 validators introduced in the Etna upgrade. For more details, see [here](/data-api/etna)
***
### Oct 25th, 2024
**Data API new endpoint - Listing networks an address has interacted with**
Returns a list of all networks on which an EVM address has had activity, filtering out networks with no activity for the provided address.
Endpoint: `GET https://glacier-api.avax.network/v1/chains/address/{address}`
[Gets the list of chains an address has interacted with](/data-api/evm-chains/get-chains-for-address).
Example response:
```json
{
"indexedChains": [
{
"chainId": "43114",
"status": "OK",
"chainName": "Avalanche (C-Chain)",
"description": "The Contract Chain (C-Chain) runs on an Ethereum Virtual Machine and is used to deploy smart contracts and connect to dApps.",
"platformChainId": "2q9e4r6Mu3U68nU1fYjgbR6JvwrRx36CohpAX5UQxse55x1Q5",
"subnetId": "11111111111111111111111111111111LpoYY",
"vmId": "mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6",
"vmName": "EVM",
"explorerUrl": "https://subnets.avax.network/c-chain",
"rpcUrl": "https://api.avax.network/ext/bc/C/rpc",
"wsUrl": "wss://api.avax.network/ext/bc/C/ws",
"isTestnet": false,
"utilityAddresses": {
"multicall": "0xed386Fe855C1EFf2f843B910923Dd8846E45C5A4"
},
"networkToken": {
"name": "Avalanche",
"symbol": "AVAX",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/3e4b8ff10b69bfa31e70080a4b142cd0/avalanche-avax-logo.svg",
"description": "AVAX is the native utility token of Avalanche. It’s a hard-capped, scarce asset that is used to pay for fees, secure the platform through staking, and provide a basic unit of account between the multiple Subnets created on Avalanche."
},
"chainLogoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/3e4b8ff10b69bfa31e70080a4b142cd0/avalanche-avax-logo.svg",
"private": false,
"enabledFeatures": [
"nftIndexing",
"webhooks",
"teleporter"
]
},
{
"chainId": "43113",
"status": "OK",
"chainName": "Avalanche (C-Chain)",
"description": "The Contract Chain on Avalanche's test subnet.",
"platformChainId": "yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp",
"subnetId": "11111111111111111111111111111111LpoYY",
"vmId": "mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6",
"vmName": "EVM",
"explorerUrl": "https://subnets-test.avax.network/c-chain",
"rpcUrl": "https://api.avax-test.network/ext/bc/C/rpc",
"wsUrl": "wss://api.avax-test.network/ext/bc/C/ws",
"isTestnet": true,
"utilityAddresses": {
"multicall": "0xE898101ffEF388A8DA16205249a7E4977d4F034c"
},
"networkToken": {
"name": "Avalanche",
"symbol": "AVAX",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/3e4b8ff10b69bfa31e70080a4b142cd0/avalanche-avax-logo.svg",
"description": "AVAX is the native utility token of Avalanche. It’s a hard-capped, scarce asset that is used to pay for fees, secure the platform through staking, and provide a basic unit of account between the multiple Subnets created on Avalanche."
},
"chainLogoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/3e4b8ff10b69bfa31e70080a4b142cd0/avalanche-avax-logo.svg",
"private": false,
"enabledFeatures": [
"nftIndexing",
"webhooks",
"teleporter"
]
},
{
"chainId": "779672",
"status": "OK",
"chainName": "Dispatch L1",
"description": "Environment for testing Avalanche Warp Messaging and Teleporter.",
"platformChainId": "2D8RG4UpSXbPbvPCAWppNJyqTG2i2CAXSkTgmTBBvs7GKNZjsY",
"subnetId": "7WtoAMPhrmh5KosDUsFL9yTcvw7YSxiKHPpdfs4JsgW47oZT5",
"vmId": "mDtV8ES8wRL1j2m6Kvc1qRFAvnpq4kufhueAY1bwbzVhk336o",
"vmName": "EVM",
"explorerUrl": "https://subnets-test.avax.network/dispatch",
"rpcUrl": "https://subnets.avax.network/dispatch/testnet/rpc",
"isTestnet": true,
"utilityAddresses": {
"multicall": "0xb35f163b70AbABeE69cDF40bCDA94df2c37d9df8"
},
"networkToken": {
"name": "DIS",
"symbol": "DIS",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/60XrKdf99PqQKrHiuYdwTE/908622f5204311dbb11be9c6008ead44/Dispatch_Subnet_Logo.png",
"description": ""
},
"chainLogoUri": "https://images.ctfassets.net/gcj8jwzm6086/60XrKdf99PqQKrHiuYdwTE/908622f5204311dbb11be9c6008ead44/Dispatch_Subnet_Logo.png",
"private": false,
"enabledFeatures": [
"teleporter"
]
},
{
"chainId": "173750",
"status": "OK",
"chainName": "Echo L1",
"description": "Environment for testing Avalanche Warp Messaging and Teleporter.",
"platformChainId": "98qnjenm7MBd8G2cPZoRvZrgJC33JGSAAKghsQ6eojbLCeRNp",
"subnetId": "i9gFpZQHPLcGfZaQLiwFAStddQD7iTKBpFfurPFJsXm1CkTZK",
"vmId": "meq3bv7qCMZZ69L8xZRLwyKnWp6chRwyscq8VPtHWignRQVVF",
"vmName": "EVM",
"explorerUrl": "https://subnets-test.avax.network/echo",
"rpcUrl": "https://subnets.avax.network/echo/testnet/rpc",
"isTestnet": true,
"utilityAddresses": {
"multicall": "0x0E3a5F409eF471809cc67311674DDF7572415682"
},
"networkToken": {
"name": "ECH",
"symbol": "ECH",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/7kyTY75fdtnO6mh7f0osix/4c92c93dd688082bfbb43d5d910cbfeb/Echo_Subnet_Logo.png",
"description": ""
},
"chainLogoUri": "https://images.ctfassets.net/gcj8jwzm6086/7kyTY75fdtnO6mh7f0osix/4c92c93dd688082bfbb43d5d910cbfeb/Echo_Subnet_Logo.png",
"private": false,
"enabledFeatures": [
"teleporter"
]
}
],
"unindexedChains": []
}
```
***
### Sep 12th, 2024
**Data API new endpoint - List teleporter messages by address**
Endpoint: `GET https://glacier-api.avax.network/v1/teleporter/addresses/{address}/messages`
[Lists teleporter messages by address](/data-api/teleporter/list-teleporter-messages-address). Ordered by timestamp in descending order.
Example response:
```json
{
"messages": [{
"messageId": "25e7bcf7304516a24f5ee597048ada3680dfa3264b27722b46b399da2180dea6",
"teleporterContractAddress": "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf",
"sourceBlockchainId": "2q9e4r6Mu3U68nU1fYjgbR6JvwrRx36CohpAX5UQxse55x1Q5",
"destinationBlockchainId": "75babf9b4db10c46cd1c4cc28e199cc4acf4c64f78327ff6cda26b8785a7bb5d",
"sourceEvmChainId": "43114",
"destinationEvmChainId": "",
"messageNonce": "29",
"from": "0x573e623caCfDe4427C460Fc408aDD5AB21220FD7",
"to": "0xB324bf38e6aFf06670EF649077062A7563b87fC5",
"data": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006626f6272696b0000000000000000000000000000000000000000000000000000",
"messageExecuted": false,
"receipts": [],
"receiptDelivered": false,
"rewardDetails": {
"value": "0",
"address": "0x0000000000000000000000000000000000000000",
"ercType": "ERC-20",
"name": "",
"symbol": "",
"decimals": 0
},
"status": "pending",
"sourceTransaction": {
"txHash": "0x7f5258b78964bc0f7b7abd1b3b99fb8665acb3d67e5ebe8fdf1b9e6ae6402b2a",
"timestamp": 1722571442,
"gasSpent": "3338675000000000"
}
}, {
"messageId": "2c56bfe4c816ca2d8241bf7a76ade09cb1cc9ab52dbc7b774184ad7cc9fba2a8",
"teleporterContractAddress": "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf",
"sourceBlockchainId": "2q9e4r6Mu3U68nU1fYjgbR6JvwrRx36CohpAX5UQxse55x1Q5",
"destinationBlockchainId": "2q9e4r6Mu3U68nU1fYjgbR6JvwrRx36CohpAX5UQxse55x1Q5",
"sourceEvmChainId": "43114",
"destinationEvmChainId": "43114",
"messageNonce": "28",
"from": "0x573e623caCfDe4427C460Fc408aDD5AB21220FD7",
"to": "0xB324bf38e6aFf06670EF649077062A7563b87fC5",
"data": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006626f6272696b0000000000000000000000000000000000000000000000000000",
"messageExecuted": false,
"receipts": [],
"receiptDelivered": false,
"rewardDetails": {
"value": "0",
"address": "0x0000000000000000000000000000000000000000",
"ercType": "ERC-20",
"name": "",
"symbol": "",
"decimals": 0
},
"status": "pending",
"sourceTransaction": {
"txHash": "0xee1b6a56dca07cc35d01a912d0e80b1124f8986b1d375534ef651a822798e509",
"timestamp": 1722570309,
"gasSpent": "3338675000000000"
}
}]
}
```
***
### August 6th, 2024
**Data API new endpoint- Get L1 details by `subnetID`**
Endpoint: `GET https://glacier-api.avax.network/v1/networks/{network}/subnets/{subnetId}`
This endpoint retrieves detailed information about a specific L1/subnet registered on the network. By providing the network type (mainnet or a testnet) and the L1 ID, you can fetch various details including the subnet’s creation timestamp, ownership information, and associated blockchains.
Example response:
```JSON
{
"createBlockTimestamp": 1599696000,
"createBlockIndex": "-1",
"subnetId": "11111111111111111111111111111111LpoYY",
"ownerAddresses": [
""
],
"threshold": 0,
"locktime": 0,
"subnetOwnershipInfo": {
"addresses": [
"0"
],
"locktime": 0,
"threshold": null
},
"blockchains": [
{
"blockchainId": "11111111111111111111111111111111LpoYY"
},
{
"blockchainId": "2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM"
},
{
"blockchainId": "2q9e4r6Mu3U68nU1fYjgbR6JvwrRx36CohpAX5UQxse55x1Q5"
}
]
}
```
***
### May 21st, 2024
**Filter spam tokens and new endpoints for the primary network**
The following improvements have been made to the Glacier API:
* **EVM**
Remove Spam Tokens from Balances Endpoint
Users can now pass in an optional query parameter `filterSpamTokens` when getting balances for a particular address to filter out balances of tokens that we've determined to be spam. By default, the route will now filter spam tokens unless `filterSpamTokens=false`.
Try it out [here](/data-api/evm-balances/list-erc-20-balances)!
* **Primary Network**
In the [List Validators](/data-api/primary-network/list-validators) endpoint, users can now sort validators by Block Index, Delegation Capacity, Time Remaining, Delegation Fee, or Uptime Performance.
Users can also filter by validator uptime performance using `minUptimePerformance` and `maxUptimePerformance` and by fee percentage using `minFeePercentage` and \`maxFeePercentage.
* **Webhooks**
A new [API endpoint](/webhooks-api/webhooks/list-adresses-by-webhook) has been added to enable users to list all addresses associated with a webhook.
***
### Aug 20th, 2024
**Webhook service launched**
With Glacier Webhooks, you can monitor real-time events on the Avalanche C-chain and L1s. For example, you can monitor smart contract events, track NFT transfers, and observe wallet-to-wallet transactions.
![webhooks](https://mintlify.s3.us-west-1.amazonaws.com/avalabs-47ea3976/images/webhooks.png)
**Key Features:**
* **Real-time notifications**: Receive immediate updates on specified on-chain activities without polling.
* **Customizable**: Specify the desired event type to listen for, customizing notifications according to individual requirements.
* **Secure**: Employ shared secrets and signature-based verification to guarantee that notifications originate from a trusted source.
* **Broad Coverage**: Support for C-chain mainnet, testnet, and L1s within the Avalanche ecosystem, ensuring wide-ranging monitoring capabilities.
**Use cases**
* **NFT Marketplace Transactions**: Get alerts for NFT minting, transfers, auctions, bids, sales, and other interactions within NFT marketplaces.
* **Wallet Notifications**: Receive alerts when an address performs actions such as sending, receiving, swapping, or burning assets.
* **DeFi Activities**: Receive notifications for various DeFi activities such as liquidity provisioning, yield farming, borrowing, lending, and liquidations.
For further details, visit our:
* [Overview](/webhooks-api/overview)
* [Getting Started](/webhooks-api/getting-started)
# How to get all transactions of an address
This guide will walk you through how to retrieve all transactions associated with a specific wallet address on the C-chain network using the Data API.
### Step 1: Setup Avacloud
First, ensure that you have set up [AvaCloud](https://app.avacloud.io/) and have access to your API key. If you’re new to Avacloud, create an account and obtain an API key.
### Step 2: Get All Transactions for an Address
To get all transactions for a specific address you can use [list transactions](/data-api/evm-transactions/list-transactions) endpoint. You’ll need to specify the `chainId` and the `address` for which you want to retrieve the transactions.
Here’s how you can do it:
```javascript AvacloudSDK
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
const avaCloudSDK = new AvaCloudSDK({
glacierApiKey: "",
chainId: "43114",
network: "mainnet",
});
async function run() {
const result = await avaCloudSDK.data.evm.transactions.listTransactions({
pageSize: 10,
address: "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
sortOrder: "asc",
});
console.log(JSON.stringify(result, null, 2));
}
run();
```
```bash cURL
curl --request GET \
--url https://glacier-api.avax.network/v1/chains/43114/addresses/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/transactions \
--header 'x-glacier-api-key: '
```
### Step 3: Run the script
Once you’ve copied the code into your preferred developer tool, you can run it using the following commands:
```bash
node index.js
```
After running the script, you should see a JSON response similar to this in your terminal:
```json
{
"transactions": [
{
"nativeTransaction": {
"blockNumber": "43281797",
"blockIndex": 3,
"blockHash": "0x85ad9ece9c384554f100318c7d88834ebacf5c9dd970d1406297eb4c90ee850f",
"txHash": "0x4dde404e7ac7fb9eb10ab780fba9715ef07105312aa3a6367cfc2322cbd352fe",
"txStatus": "1",
"txType": 2,
"gasLimit": "92394",
"gasUsed": "61126",
"gasPrice": "26500000000",
"nonce": "16",
"blockTimestamp": 1711218299,
"from": {
"address": "0x5AEdcCaeCA2cb3f87a90713c83872f7515e19c90"
},
"to": {
"address": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
"name": "TetherToken",
"symbol": "USDT",
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/4ac7cb0c-5260-473b-a4bc-b809801aa5da/49d45340a82166bdb26fce4d3e62ce65/43114-0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7.png"
},
"method": {
"callType": "CONTRACT_CALL",
"methodHash": "0xa9059cbb"
},
"value": "0"
},
"erc20Transfers": [
{
"from": {
"address": "0x5AEdcCaeCA2cb3f87a90713c83872f7515e19c90"
},
"to": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"logIndex": 10,
"value": "400000",
"erc20Token": {
"ercType": "ERC-20",
"address": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
"name": "TetherToken",
"symbol": "USDT",
"decimals": 6,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/4ac7cb0c-5260-473b-a4bc-b809801aa5da/49d45340a82166bdb26fce4d3e62ce65/43114-0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7.png",
"price": {
"value": 0.999436,
"currencyCode": "usd"
}
}
}
]
},
{
"nativeTransaction": {
"blockNumber": "26002777",
"blockIndex": 0,
"blockHash": "0x83c8d5bb1b885d1efaf116da6a9d776088d19c9fdfd06a71d16156c80b339261",
"txHash": "0xc96e00ce365f2fae67f940985fc8b9af97a051f5bf0f29c891205ca1ae3287d4",
"txStatus": "1",
"txType": 0,
"gasLimit": "21000",
"gasUsed": "21000",
"gasPrice": "27500000000",
"nonce": "11",
"blockTimestamp": 1675878866,
"from": {
"address": "0x444782F140e31B5687d166FA077C3049062911Ba"
},
"to": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"method": {
"callType": "NATIVE_TRANSFER",
"methodHash": "",
"methodName": "Native Transfer"
},
"value": "3005020000000000"
}
},
{
"nativeTransaction": {
"blockNumber": "25037477",
"blockIndex": 6,
"blockHash": "0x048a00b6e40ffdb2b3a4b596cfa6ee2e4479c53099f23afed3145569d2cbfb02",
"txHash": "0x4c29443c0d8f08d2be1cc6c20bc7a1a05211b91340550edf26d0bd9196c593a3",
"txStatus": "1",
"txType": 2,
"gasLimit": "31500",
"gasUsed": "21000",
"gasPrice": "26500000000",
"nonce": "2590",
"blockTimestamp": 1673917315,
"from": {
"address": "0x1C42F2fCc9c7F4a30dC15ACf9C047DDeCF39de06"
},
"to": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"method": {
"callType": "NATIVE_TRANSFER",
"methodHash": "",
"methodName": "Native Transfer"
},
"value": "150000000000000000"
}
},
{
"nativeTransaction": {
"blockNumber": "23826282",
"blockIndex": 0,
"blockHash": "0x96caae2487206cba32000e49e2f593406981fcf26cffd646da92661ce907fc62",
"txHash": "0x7aaa1beff9466f8e099b8a25e37b1f748b2623856f65cd479bd721e983ee84ac",
"txStatus": "1",
"txType": 0,
"gasLimit": "21000",
"gasUsed": "21000",
"gasPrice": "31250000000",
"nonce": "0",
"blockTimestamp": 1671433238,
"from": {
"address": "0x6B69f15BCEeB1a2326De003ca97b1F61AE57b774"
},
"to": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"method": {
"callType": "NATIVE_TRANSFER",
"methodHash": "",
"methodName": "Native Transfer"
},
"value": "57557140067572377"
}
},
{
"nativeTransaction": {
"blockNumber": "23249650",
"blockIndex": 4,
"blockHash": "0xf0161747357482ce078d398f55e9413ce4a4e21611b20f5488b74e4d181536bd",
"txHash": "0x760ac8c147a9ec795a7695f3c3383408890f53fc23e39f43f938f513910a9612",
"txStatus": "1",
"txType": 2,
"gasLimit": "21000",
"gasUsed": "21000",
"gasPrice": "26843560317",
"nonce": "9",
"blockTimestamp": 1670254442,
"from": {
"address": "0xcd661208b0138A9468D5B6E3E119215e5aA14c15"
},
"to": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"method": {
"callType": "NATIVE_TRANSFER",
"methodHash": "",
"methodName": "Native Transfer"
},
"value": "9942019140711807"
}
},
{
"nativeTransaction": {
"blockNumber": "21989662",
"blockIndex": 8,
"blockHash": "0x9eefeb39b435c625cf659674df2a0d56b85b2efffb677edd8a4365d7fc39dd68",
"txHash": "0x0e1f96c84ddc07d74a19ee748381048e1876bf8a706e5e8f917897f6761eef51",
"txStatus": "1",
"txType": 2,
"gasLimit": "2146729",
"gasUsed": "1431153",
"gasPrice": "26000000000",
"nonce": "4",
"blockTimestamp": 1667660826,
"from": {
"address": "0x55906a1d87f7426497fDBa498B8F5edB1C741cef"
},
"to": {
"address": "0xF9d922c055A3f1759299467dAfaFdf43BE844f7a"
},
"method": {
"callType": "CONTRACT_CALL",
"methodHash": "0x74a72e41"
},
"value": "0"
},
"erc20Transfers": [
{
"from": {
"address": "0xF9d922c055A3f1759299467dAfaFdf43BE844f7a"
},
"to": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"logIndex": 33,
"value": "30000000000000",
"erc20Token": {
"ercType": "ERC-20",
"address": "0xF9d922c055A3f1759299467dAfaFdf43BE844f7a",
"name": "Minereum AVAX",
"symbol": "MNEAV",
"decimals": 8
}
}
]
},
{
"nativeTransaction": {
"blockNumber": "17768295",
"blockIndex": 3,
"blockHash": "0x55f934b32644b9c6d53283ac9275747929779e38ad0687d9c8e895a8699986b2",
"txHash": "0x8138fea77335fd208ec7618a67fdd3a104577e8e89a6d0665407202163ff9d07",
"txStatus": "1",
"txType": 2,
"gasLimit": "77829",
"gasUsed": "51886",
"gasPrice": "26500000000",
"nonce": "6",
"blockTimestamp": 1658716784,
"from": {
"address": "0x7e31af176DA39a9986c8f5c7632178B4AcF0c868"
},
"to": {
"address": "0x4cb70De91e6Bb85fB132880D5Af3418477a90083"
},
"method": {
"callType": "CONTRACT_CALL",
"methodHash": "0xa9059cbb"
},
"value": "0"
},
"erc20Transfers": [
{
"from": {
"address": "0x7e31af176DA39a9986c8f5c7632178B4AcF0c868"
},
"to": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"logIndex": 6,
"value": "50000000000000000",
"erc20Token": {
"ercType": "ERC-20",
"address": "0x4cb70De91e6Bb85fB132880D5Af3418477a90083",
"name": "Woodcut Token",
"symbol": "WOOD",
"decimals": 18
}
}
]
},
{
"nativeTransaction": {
"blockNumber": "17328312",
"blockIndex": 0,
"blockHash": "0x96c1554115937f2ea11efac5d08c9a844925dace3ff4b62679fd3183fc8c8aa9",
"txHash": "0xdcc0cf0982aed703d72da1fefd686e97fa549bd3717972a547440a3abc384bfb",
"txStatus": "1",
"txType": 0,
"gasLimit": "21000",
"gasUsed": "21000",
"gasPrice": "30000000000",
"nonce": "3",
"blockTimestamp": 1657827243,
"from": {
"address": "0x6ae30413ddA067f8BB2D904d630081784f4c2a3E"
},
"to": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"method": {
"callType": "NATIVE_TRANSFER",
"methodHash": "",
"methodName": "Native Transfer"
},
"value": "3694737000000000"
}
},
{
"nativeTransaction": {
"blockNumber": "13923067",
"blockIndex": 17,
"blockHash": "0x22c7369d613a8ee600fa8dd0a178c575139150d18d04220633cc682465774536",
"txHash": "0x9a8822afb1f082f1250ac1876223384fac8370f9dd1f785ce54c3acbcaf7341a",
"txStatus": "1",
"txType": 2,
"gasLimit": "21000",
"gasUsed": "21000",
"gasPrice": "72048389636",
"nonce": "1",
"blockTimestamp": 1650975303,
"from": {
"address": "0xffc83E3777DB33ff4af4A3fB72056fF3bDF02e47"
},
"to": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"method": {
"callType": "NATIVE_TRANSFER",
"methodHash": "",
"methodName": "Native Transfer"
},
"value": "31240000000000000"
}
},
{
"nativeTransaction": {
"blockNumber": "13922287",
"blockIndex": 19,
"blockHash": "0x42c6a341b8d5794b83026cd1aa63606cbeb917b2e58a017b48c8d24b69fe788c",
"txHash": "0x12681543ad3f41cb2146f959528fbedea3250afbed14f152cf0ec87bce4c69c2",
"txStatus": "1",
"txType": 2,
"gasLimit": "21000",
"gasUsed": "21000",
"gasPrice": "69081184341",
"nonce": "9",
"blockTimestamp": 1650973723,
"from": {
"address": "0xB30228A0FfB21f68a144Dd4f3af703ce975Cf490"
},
"to": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"method": {
"callType": "NATIVE_TRANSFER",
"methodHash": "",
"methodName": "Native Transfer"
},
"value": "33305869282988272"
}
}
],
"nextPageToken": "5b7c89d0-1f1b-45d1-a539-57b8e70d034f"
}
```
Congratulations 🎉 You’ve successfully retrieved all transactions for a wallet address on the C-chain using the Data API! With just a few lines of code, you can now access this data easily and integrate it into your projects.
# Advanced SDK Setup
Glacier's rate limiting is handled using a weighted scoring system. This allows users to make more basic requests and will better allow Glacier to handle more computationally complex requests.
Rate Limit Tiers
The maximum rate-limiting score for a user depends on their subscription level and is delineated in the following table:
Subscription Level Per Minute Limit Per Day Limit
| Subscription Level | Per Minute Limit | Per Day Limit |
| --------------------------------------------------------- | ---------------- | ------------- |
| Free | 8,000 | 2,00,000 |
| Base | 10,000 | 3,750,000 |
| Growth | 14,000 | 11,200,000 |
| Pro | 20,000 | 25,000,000 |
| To update your subscription level use the AvaCloud Portal | | |
## Rate Limit Categories
The keys and values for each weight are as follows:
| Weight | Value |
| ------ | ----- |
| Free | 1 |
| Small | 10 |
| Medium | 20 |
| Large | 50 |
| XL | 100 |
| XXL | 200 |
## Rate Limits for Glacier Endpoints
The weights for each route are defined in the table below:
| Endpoint | Method | Weight |
| ------------------------------------------------------------------------------- | ------ | ------ |
| /v1/chains | GET | Free |
| /v1/chains/{chainId} | GET | Free |
| /v1/chains/{chainId}/blocks | GET | Small |
| /v1/chains/{chainId}/blocks/{blockId} | GET | Small |
| /v1/chains/{chainId}/contracts/{address}/transactions:getDeployment | GET | Medium |
| /v1/chains/{chainId}/contracts/{address}/deployments | GET | Medium |
| /v1/chains/{chainId}/tokens/{address}/transfers | GET | Medium |
| /v1/chains/{chainId}/addresses/{address}/transactions | GET | Medium |
| /v1/chains/{chainId}/addresses/{address}/transactions:listNative | GET | Medium |
| /v1/chains/{chainId}/addresses/{address}/transactions:listErc20 | GET | Medium |
| /v1/chains/{chainId}/addresses/{address}/transactions:listErc721 | GET | Medium |
| /v1/chains/{chainId}/addresses/{address}/transactions:listErc1155 | GET | Medium |
| /v1/chains/{chainId}/addresses/{address}/transactions:listInternals | GET | Medium |
| /v1/chains/{chainId}/transactions/{txHash} | GET | Medium |
| /v1/chains/{chainId}/blocks/{blockId}/transactions | GET | Medium |
| /v1/chains/{chainId}/transactions | GET | Medium |
| /v1/chains/{chainId}/addresses/{address}/balances:getNative | GET | Medium |
| /v1/chains/{chainId}/addresses/{address}/balances:listErc20 | GET | Medium |
| /v1/chains/{chainId}/addresses/{address}/balances:listErc721 | GET | Medium |
| /v1/chains/{chainId}/addresses/{address}/balances:listErc1155 | GET | Medium |
| /v1/chains/{chainId}/addresses/{address}/balances:listCollectibles | GET | Medium |
| /v1/chains/{chainId}/addresses/{address} | GET | Medium |
| /v1/networks/{network}/blockchains/{blockchainId}/assets/{assetId} | GET | XL |
| /v1/networks/{network}/addresses:listChainIds | GET | XL |
| /v1/networks/{network} | GET | XL |
| /v1/networks/{network}/blockchains | GET | Medium |
| /v1/networks/{network}/subnets | GET | Medium |
| /v1/networks/{network}/validators | GET | Medium |
| /v1/networks/{network}/validators/{nodeId} | GET | Medium |
| /v1/networks/{network}/delegators | GET | Medium |
| /v1/networks/{network}/blockchains/{blockchainId}/blocks/{blockId} | GET | Medium |
| /v1/networks/{network}/blockchains/{blockchainId}/nodes/{nodeId}/blocks | GET | Medium |
| /v1/networks/{network}/blockchains/{blockchainId}/blocks | GET | Medium |
| /v1/networks/{network}/blockchains/{blockchainId}/vertices | GET | Medium |
| /v1/networks/{network}/blockchains/{blockchainId}/vertices/{vertexHash} | GET | Medium |
| /v1/networks/{network}/blockchains/{blockchainId}/vertices:listByHeight | GET | Medium |
| /v1/networks/{network}/blockchains/{blockchainId}/transactions/{txHash} | GET | Medium |
| /v1/networks/{network}/blockchains/{blockchainId}/transactions | GET | XL |
| /v1/networks/{network}/blockchains/{blockchainId}/transactions:listStaking | GET | XL |
| /v1/networks/{network}/blockchains/{blockchainId}/assets/{assetId}/transactions | GET | XL |
| /v1/networks/{network}/blockchains/{blockchainId}/balances | GET | XL |
| /v1/networks/{network}/blockchains/{blockchainId}/utxos | GET | XL |
| /v1/networks/{network}/rewards:listPending | GET | XL |
| /v1/networks/{network}/rewards | GET | XL |
| /v1/chains/{chainId}/nfts/collections/{address}/tokens/{tokenId}:reindex | POST | Small |
| /v1/chains/{chainId}/nfts/collections/{address}/tokens | GET | Medium |
| /v1/chains/{chainId}/nfts/collections/{address}/tokens/{tokenId} | GET | Medium |
| /v1/operations/{operationId} | GET | Small |
| /v1/operations/transactions:export | POST | Medium |
| /v1/health-check | GET | Small |
| /v1/teleporter/messages/{messageId} | GET | Medium |
| /v1/teleporter/messages | GET | Medium |
| /v1/webhooks | POST | Medium |
| /v1/webhooks | GET | Small |
| /v1/webhooks/{id} | GET | Small |
| /v1/webhooks/{id} | DELETE | Medium |
| /v1/webhooks/{id} | PATCH | Medium |
| /v1/webhooks:generateOrRotateSharedSecret | POST | Medium |
| /v1/webhooks:getSharedSecret | GET | Small |
## Rate Limits for RPC endpoints
The weights for RPC calls will be calculated based on the RPC method(s) within the request. The methods are weighted as follows:
| Method | Weight |
| ---------------------------------------- | ------ |
| eth\_accounts | Medium |
| eth\_blockNumber | Small |
| eth\_call | Small |
| eth\_coinbase | Small |
| eth\_chainId | Free |
| eth\_gasPrice | Small |
| eth\_getBalance | Small |
| eth\_getBlockByHash | Small |
| eth\_getBlockByNumber | Small |
| eth\_getBlockTransactionCountByNumber | Medium |
| eth\_getCode | Medium |
| eth\_getLogs | XXL |
| eth\_getStorageAt | Medium |
| eth\_getTransactionByBlockNumberAndIndex | Medium |
| eth\_getTransactionByHash | Small |
| eth\_getTransactionCount | Small |
| eth\_getTransactionReceipt | Small |
| eth\_sendRawTransaction | Small |
| eth\_syncing | Free |
| net\_listening | Free |
| net\_version | Free |
| web3\_clientVersion | Small |
| web3\_sha3 | Small |
| eth\_maxPriorityFeePerGas | Small |
| eth\_baseFee | Small |
| rpc\_modules | Free |
| eth\_getChainConfig | Small |
| eth\_feeConfig | Small |
| eth\_getActivePrecompilesAt | Small |
All rate limits, weights, and values are subject to change
# Get logs for requests made by client
get /v1/apiLogs
Gets logs for requests made by client over a specified time interval for a specific organization.
# Get usage metrics for the Data API
get /v1/apiUsageMetrics
Gets metrics for Data API usage over a specified time interval aggregated at the specified time-duration granularity.
# Get usage metrics for the Subnet RPC
get /v1/rpcUsageMetrics
Gets metrics for public Subnet RPC usage over a specified time interval aggregated at the specified time-duration granularity.
# Data API vs RPC
In the rapidly evolving world of Web3 development, efficiently retrieving token balances for a user's address is a fundamental requirement. Whether you're building DeFi platforms, wallets, analytics tools, or exchanges, displaying accurate token balances is crucial for user engagement and trust. A typical use case involves showing a user's token portfolio in a wallet application, in this case, we have sAvax and USDC.
![title](https://mintlify.s3.us-west-1.amazonaws.com/avalabs-47ea3976/images/wallet.png)
Developers generally have two options to fetch this data:
1. **Using RPC methods to index blockchain data on their own**
2. **Leveraging an indexer provider like the AvaCloud Data API**
While both methods aim to achieve the same goal, the Data API offers a more efficient, scalable, and developer-friendly solution. This article delves into why using the Data API is better than relying on traditional RPC (Remote Procedure Call) methods.
### What Are RPC methods and their challenges?
Remote Procedure Call (RPC) methods allow developers to interact directly with blockchain nodes. One of their key advantages is that they are standardized and universally understood by blockchain developers across different platforms. With RPC, you can perform tasks such as querying data, submitting transactions, and interacting with smart contracts. These methods are typically low-level and synchronous, meaning they require a deep understanding of the blockchain’s architecture and specific command structures.
You can refer to the [official documentation](https://ethereum.org/en/developers/docs/apis/json-rpc/) to gain a more comprehensive understanding of the JSON-RPC API.
Here’s an example using the `eth_getBalance` method to retrieve the native balance of a wallet:
```bash
curl --location 'https://api.avax.network/ext/bc/C/rpc' \
--header 'Content-Type: application/json' \
--data '{"method":"eth_getBalance","params":["0x8ae323046633A07FB162043f28Cea39FFc23B50A", "latest"],"id":1,"jsonrpc":"2.0"}'
```
This call returns the following response:
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x284476254bc5d594"
}
```
The balance in this wallet is 2.9016 AVAX. However, despite the wallet holding multiple tokens such as USDC, the `eth_getBalance` method only returns the AVAX amount and it does so in Wei and in hexadecimal format. This is not particularly human-readable, adding to the challenge for developers who need to manually convert the balance to a more understandable format.
#### No direct RPC methods to retrieve token balances
Despite their utility, RPC methods come with significant limitations when it comes to retrieving detailed token and transaction data. Currently, RPC methods do not provide direct solutions for the following:
* **Listing all tokens held by a wallet**: There is no RPC method that provides a complete list of ERC-20 tokens owned by a wallet.
* **Retrieving all transactions for a wallet**: : There is no direct method for fetching all transactions associated with a wallet.
* **Getting ERC-20/721/1155 token balances**: The `eth_getBalance` method only returns the balance of the wallet’s native token (such as AVAX on Avalanche) and cannot be used to retrieve ERC-20/721/1155 token balances.
To achieve these tasks using RPC methods alone, you would need to:
* **Query every block for transaction logs**: Scan the entire blockchain, which is resource-intensive and impractical.
* **Parse transaction logs**: Identify and extract ERC-20 token transfer events from each transaction.
* **Aggregate data**: Collect and process this data to compute balances and transaction histories.
#### Manual blockchain indexing is difficult and costly
Using RPC methods to fetch token balances involves an arduous process:
1. You must connect to a node and subscribe to new block events.
2. For each block, parse every transaction to identify ERC-20 token transfers involving the user's address.
3. Extract contract addresses and other relevant data from the parsed transactions.
4. Compute balances by processing transfer events.
5. Store the processed data in a database for quick retrieval and aggregation.
#### Why this is difficult:
* **Resource-Intensive**: Requires significant computational power and storage to process and store blockchain data.
* **Time-consuming**: Processing millions of blocks and transactions can take an enormous amount of time.
* **Complexity**: Handling edge cases like contract upgrades, proxy contracts, and non-standard implementations adds layers of complexity.
* **Maintenance**: Keeping the indexed data up-to-date necessitates continuous synchronization with new blocks being added to the blockchain.
* **High Costs**: Associated with servers, databases, and network bandwidth.
### The Data API Advantage
The Data API provides a streamlined, efficient, and scalable solution for fetching token balances. Here's why it's the best choice:
With a single API call, you can retrieve all ERC-20 token balances for a user's address:
```javascript
avaCloudSDK.data.evm.balances.listErc20Balances({
address: "0xYourAddress"
});
```
Sample Response:
```json
{
"erc20TokenBalances": [
{
"ercType": "ERC-20",
"chainId": "43114",
"address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"name": "USD Coin",
"symbol": "USDC",
"decimals": 6,
"price": {
"value": 1.00,
"currencyCode": "usd"
},
"balance": "15000000",
"balanceValue": {
"currencyCode": "usd",
"value": 9.6
},
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/e50058c1-2296-4e7e-91ea-83eb03db95ee/8db2a492ce64564c96de87c05a3756fd/43114-0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E.png"
},
// Additional tokens...
]
}
```
As you can see with a single call the API returns an array of token balances for all the wallet tokens, including:
* **Token metadata**: Contract address, name, symbol, decimals.
* **Balance information**: Token balance in both hexadecimal and decimal formats, Also retrieves balances of native assets like ETH or AVAX.
* **Price data**: Current value in USD or other supported currencies, saving you the effort of integrating another API.
* **Visual assets**: Token logo URI for better user interface integration.
If you’re building a wallet, DeFi app, or any application that requires displaying balances, transaction history, or smart contract interactions, relying solely on RPC methods can be challenging. Just as there’s no direct RPC method to retrieve token balances, there’s also no simple way to fetch all transactions associated with a wallet, especially for ERC-20, ERC-721, or ERC-1155 token transfers.
However, by using the Data API, you can retrieve all token transfers for a given wallet **with a single API call**, making the process much more efficient. This approach simplifies tracking and displaying wallet activity without the need to manually scan the entire blockchain.
Below are two examples that demonstrate the power of the Data API: in the first, it returns all ERC transfers, including ERC-20, ERC-721, and ERC-1155 tokens, and in the second, it shows all internal transactions, such as when one contract interacts with another.
[Lists ERC transfers](/data-api/evm-transactions/list-erc-transfers) for an ERC-20, ERC-721, or ERC-1155 contract address.
```javascript
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
const avaCloudSDK = new AvaCloudSDK({
apiKey: "",
chainId: "43114",
network: "mainnet",
});
async function run() {
const result = await avaCloudSDK.data.evm.transactions.listTransfers({
startBlock: 6479329,
endBlock: 6479330,
pageSize: 10,
address: "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
});
for await (const page of result) {
// Handle the page
console.log(page);
}
}
run();
```
Example response
```json
{
"nextPageToken": "",
"transfers": [
{
"blockNumber": "339",
"blockTimestamp": 1648672486,
"blockHash": "0x17533aeb5193378b9ff441d61728e7a2ebaf10f61fd5310759451627dfca2e7c",
"txHash": "0x3e9303f81be00b4af28515dab7b914bf3dbff209ea10e7071fa24d4af0a112d4",
"from": {
"name": "Wrapped AVAX",
"symbol": "WAVAX",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg",
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"to": {
"name": "Wrapped AVAX",
"symbol": "WAVAX",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg",
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"logIndex": 123,
"value": "10000000000000000000",
"erc20Token": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"name": "Wrapped AVAX",
"symbol": "WAVAX",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg",
"ercType": "ERC-20",
"price": {
"currencyCode": "usd",
"value": "42.42"
}
}
}
]
}
```
[Returns a list of internal transactions](/data-api/evm-transactions/list-internal-transactions) for an address and chain. Filterable by block range.
```javascript
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
const avaCloudSDK = new AvaCloudSDK({
apiKey: "",
chainId: "43114",
network: "mainnet",
});
async function run() {
const result = await avaCloudSDK.data.evm.transactions.listInternalTransactions({
startBlock: 6479329,
endBlock: 6479330,
pageSize: 10,
address: "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
});
for await (const page of result) {
// Handle the page
console.log(page);
}
}
run();
```
Example response
```json
{
"nextPageToken": "",
"transactions": [
{
"blockNumber": "339",
"blockTimestamp": 1648672486,
"blockHash": "0x17533aeb5193378b9ff441d61728e7a2ebaf10f61fd5310759451627dfca2e7c",
"txHash": "0x3e9303f81be00b4af28515dab7b914bf3dbff209ea10e7071fa24d4af0a112d4",
"from": {
"name": "Wrapped AVAX",
"symbol": "WAVAX",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg",
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"to": {
"name": "Wrapped AVAX",
"symbol": "WAVAX",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg",
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"internalTxType": "UNKNOWN",
"value": "10000000000000000000",
"isReverted": true,
"gasUsed": "",
"gasLimit": ""
}
]
}
```
### Conclusion
Using the Data API over traditional RPC methods for fetching token balances offers significant advantages:
* **Efficiency**: Retrieve all necessary information in a single API call.
* **Simplicity**: Eliminates complex data processing and reduces development time.
* **Scalability**: Handles large volumes of data efficiently, suitable for real-time applications.
* **Comprehensive Data**: Provides enriched information, including token prices and logos.
* **Reliability**: Ensures data accuracy and consistency without the need for extensive error handling.
For developers building Web3 applications, leveraging the Data API is the smarter choice. It not only simplifies your codebase but also enhances the user experience by providing accurate and timely data.
If you’re building cutting-edge Web3 applications, this API is the key to improving your workflow and performance. Whether you’re developing DeFi solutions, wallets, or analytics platforms, take your project to the next level. [Start today with the Data API](/data-api/getting-started) and experience the difference!
# How to get all ERC20 transfers by wallet
This guide will walk you through the process of listing all ERC-20 transfers associated with a specific wallet address on the DFK L1 network using the Data API.
### Step 1: Set Up AvaCloud
First, ensure that you have set up [AvaCloud](https://app.avacloud.io/) and have access to your API key. If you’re new to Avacloud, create an account and obtain an API key.
### Step 2: Retrieve the Native Balance of an Address
To obtain a list of ERC-20 transfers you can use the [List ERC-20 transfers](/data-api/evm-transactions/list-erc-20-transfers) endpoint. You’ll need to specify the `chainId` and the `address` for which you want to retrieve the balance.
Here’s how you can do it:
```javascript AvacloudSDK
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
const avaCloudSDK = new AvaCloudSDK({
apiKey: "",
chainId: "53935",
network: "mainnet",
});
async function run() {
const result = await avaCloudSDK.data.evm.transactions.listErc20Transactions({
pageSize: 10,
address: "0x1137643FE14b032966a59Acd68EBf3c1271Df316",
});
// Handle the result
console.log(JSON.stringify(result, null, 2));
}
run();
```
```bash cURL
curl --request GET \
--url https://glacier-api.avax.network/v1/chains/43114/addresses/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/transactions:listErc20 \
--header 'x-glacier-api-key: '
```
Response
```json
{
"result": {
"nextPageToken": "f8dba2d2-b128-41ae-be6b-5a6f76ca5141",
"transactions": [
{
"blockNumber": "36952303",
"blockTimestamp": 1725460283,
"blockHash": "0xb18c21736207b15efa0bdc1377c9ffde8c95bd20bd8b7422cc5eaefad41375a6",
"txHash": "0x5d88c29e5d10d60a56f98d44883b6f8a82461dfe4c7b15e4c5726d626c41a484",
"from": {
"address": "0x1137643FE14b032966a59Acd68EBf3c1271Df316"
},
"to": {
"address": "0x6ef29103747EdFA66bcb3237D5AE4f773a5B9beE"
},
"logIndex": 21,
"value": "16800000000000000",
"erc20Token": {
"address": "0x04b9dA42306B023f3572e106B11D82aAd9D32EBb",
"name": "Crystals",
"symbol": "CRYSTAL",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/0212db53-26f6-4e56-99a2-05526ce32816/4a62bf9e90f401ea69fcf89496cbe96a/53935-0x04b9dA42306B023f3572e106B11D82aAd9D32EBb.png",
"ercType": "ERC-20",
"price": {
"currencyCode": "usd",
"value": 0.00719546
}
}
},
{
"blockNumber": "36952300",
"blockTimestamp": 1725460277,
"blockHash": "0xf0c3017e0428ae2442fb0162d5ed0bd3c735ab25b5e85e94b0fe3cb97a914ede",
"txHash": "0xea4aa8e2fa0b78aaf624252b762a32bcf5361ce441a7bc464edfd0bae3302a0e",
"from": {
"address": "0x3f04bAD8c90984e16e51656270f82D6C3B73a571"
},
"to": {
"address": "0x1137643FE14b032966a59Acd68EBf3c1271Df316"
},
"logIndex": 83,
"value": "1350000000000000000",
"erc20Token": {
"address": "0x04b9dA42306B023f3572e106B11D82aAd9D32EBb",
"name": "Crystals",
"symbol": "CRYSTAL",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/0212db53-26f6-4e56-99a2-05526ce32816/4a62bf9e90f401ea69fcf89496cbe96a/53935-0x04b9dA42306B023f3572e106B11D82aAd9D32EBb.png",
"ercType": "ERC-20",
"price": {
"currencyCode": "usd",
"value": 0.00719546
}
}
},
{
"blockNumber": "36952297",
"blockTimestamp": 1725460271,
"blockHash": "0x1e9a118d60b931b7968c9bb5fbf615cecf14a1e0c15ab0ee4bdbca69150ef3f9",
"txHash": "0x21fcc2430fe3d437bbb3147a877552a94a33bd02d29eb23731f26b80674fd78c",
"from": {
"address": "0x3f04bAD8c90984e16e51656270f82D6C3B73a571"
},
"to": {
"address": "0x1137643FE14b032966a59Acd68EBf3c1271Df316"
},
"logIndex": 197,
"value": "1350000000000000000",
"erc20Token": {
"address": "0x04b9dA42306B023f3572e106B11D82aAd9D32EBb",
"name": "Crystals",
"symbol": "CRYSTAL",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/0212db53-26f6-4e56-99a2-05526ce32816/4a62bf9e90f401ea69fcf89496cbe96a/53935-0x04b9dA42306B023f3572e106B11D82aAd9D32EBb.png",
"ercType": "ERC-20",
"price": {
"currencyCode": "usd",
"value": 0.00719546
}
}
},
{
"blockNumber": "36952297",
"blockTimestamp": 1725460271,
"blockHash": "0x1e9a118d60b931b7968c9bb5fbf615cecf14a1e0c15ab0ee4bdbca69150ef3f9",
"txHash": "0x5ec8a1e8ad42e2190633da46c69529ecf4d8c6c85d9df74db641e6f2a8a5e383",
"from": {
"address": "0x3f04bAD8c90984e16e51656270f82D6C3B73a571"
},
"to": {
"address": "0x1137643FE14b032966a59Acd68EBf3c1271Df316"
},
"logIndex": 183,
"value": "1350000000000000000",
"erc20Token": {
"address": "0x04b9dA42306B023f3572e106B11D82aAd9D32EBb",
"name": "Crystals",
"symbol": "CRYSTAL",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/0212db53-26f6-4e56-99a2-05526ce32816/4a62bf9e90f401ea69fcf89496cbe96a/53935-0x04b9dA42306B023f3572e106B11D82aAd9D32EBb.png",
"ercType": "ERC-20",
"price": {
"currencyCode": "usd",
"value": 0.00719546
}
}
},
{
"blockNumber": "36952297",
"blockTimestamp": 1725460271,
"blockHash": "0x1e9a118d60b931b7968c9bb5fbf615cecf14a1e0c15ab0ee4bdbca69150ef3f9",
"txHash": "0x643b4c239820667452fd5c38d2f1f980cbbb8ccd120682e76b21fcd742d43cb3",
"from": {
"address": "0x3f04bAD8c90984e16e51656270f82D6C3B73a571"
},
"to": {
"address": "0x1137643FE14b032966a59Acd68EBf3c1271Df316"
},
"logIndex": 167,
"value": "1350000000000000000",
"erc20Token": {
"address": "0x04b9dA42306B023f3572e106B11D82aAd9D32EBb",
"name": "Crystals",
"symbol": "CRYSTAL",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/0212db53-26f6-4e56-99a2-05526ce32816/4a62bf9e90f401ea69fcf89496cbe96a/53935-0x04b9dA42306B023f3572e106B11D82aAd9D32EBb.png",
"ercType": "ERC-20",
"price": {
"currencyCode": "usd",
"value": 0.00719546
}
}
},
{
"blockNumber": "36952294",
"blockTimestamp": 1725460263,
"blockHash": "0xa15acd24ce980ce5ffcf12daf6ce538fd4f9896634b4a3f48c599c77192577cf",
"txHash": "0x370627ddb71c638305a8cce92ecae69423115668039902f83aa4fd3a25983ee0",
"from": {
"address": "0x1137643FE14b032966a59Acd68EBf3c1271Df316"
},
"to": {
"address": "0x6ef29103747EdFA66bcb3237D5AE4f773a5B9beE"
},
"logIndex": 246,
"value": "18000000000000000",
"erc20Token": {
"address": "0x04b9dA42306B023f3572e106B11D82aAd9D32EBb",
"name": "Crystals",
"symbol": "CRYSTAL",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/0212db53-26f6-4e56-99a2-05526ce32816/4a62bf9e90f401ea69fcf89496cbe96a/53935-0x04b9dA42306B023f3572e106B11D82aAd9D32EBb.png",
"ercType": "ERC-20",
"price": {
"currencyCode": "usd",
"value": 0.00719546
}
}
},
{
"blockNumber": "36952294",
"blockTimestamp": 1725460263,
"blockHash": "0xa15acd24ce980ce5ffcf12daf6ce538fd4f9896634b4a3f48c599c77192577cf",
"txHash": "0xe8b58b9a413de06d4dbfebc226e3607f34a5071e7a0a315478a37d5ad76a7026",
"from": {
"address": "0xC475ecce788ECBF4b6E78BC501f4B1Ce73c46232"
},
"to": {
"address": "0x1137643FE14b032966a59Acd68EBf3c1271Df316"
},
"logIndex": 38,
"value": "300000000000000000",
"erc20Token": {
"address": "0x04b9dA42306B023f3572e106B11D82aAd9D32EBb",
"name": "Crystals",
"symbol": "CRYSTAL",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/0212db53-26f6-4e56-99a2-05526ce32816/4a62bf9e90f401ea69fcf89496cbe96a/53935-0x04b9dA42306B023f3572e106B11D82aAd9D32EBb.png",
"ercType": "ERC-20",
"price": {
"currencyCode": "usd",
"value": 0.00719546
}
}
},
{
"blockNumber": "36952294",
"blockTimestamp": 1725460263,
"blockHash": "0xa15acd24ce980ce5ffcf12daf6ce538fd4f9896634b4a3f48c599c77192577cf",
"txHash": "0xe8b58b9a413de06d4dbfebc226e3607f34a5071e7a0a315478a37d5ad76a7026",
"from": {
"address": "0xC475ecce788ECBF4b6E78BC501f4B1Ce73c46232"
},
"to": {
"address": "0x1137643FE14b032966a59Acd68EBf3c1271Df316"
},
"logIndex": 22,
"value": "300000000000000000",
"erc20Token": {
"address": "0x04b9dA42306B023f3572e106B11D82aAd9D32EBb",
"name": "Crystals",
"symbol": "CRYSTAL",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/0212db53-26f6-4e56-99a2-05526ce32816/4a62bf9e90f401ea69fcf89496cbe96a/53935-0x04b9dA42306B023f3572e106B11D82aAd9D32EBb.png",
"ercType": "ERC-20",
"price": {
"currencyCode": "usd",
"value": 0.00719546
}
}
},
{
"blockNumber": "36952294",
"blockTimestamp": 1725460263,
"blockHash": "0xa15acd24ce980ce5ffcf12daf6ce538fd4f9896634b4a3f48c599c77192577cf",
"txHash": "0xe8b58b9a413de06d4dbfebc226e3607f34a5071e7a0a315478a37d5ad76a7026",
"from": {
"address": "0xC475ecce788ECBF4b6E78BC501f4B1Ce73c46232"
},
"to": {
"address": "0x1137643FE14b032966a59Acd68EBf3c1271Df316"
},
"logIndex": 6,
"value": "300000000000000000",
"erc20Token": {
"address": "0x04b9dA42306B023f3572e106B11D82aAd9D32EBb",
"name": "Crystals",
"symbol": "CRYSTAL",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/0212db53-26f6-4e56-99a2-05526ce32816/4a62bf9e90f401ea69fcf89496cbe96a/53935-0x04b9dA42306B023f3572e106B11D82aAd9D32EBb.png",
"ercType": "ERC-20",
"price": {
"currencyCode": "usd",
"value": 0.00719546
}
}
},
{
"blockNumber": "36952292",
"blockTimestamp": 1725460259,
"blockHash": "0x2b59f6314f27da325237afd457757cbd48302b7e292cc711f9b8d20cb5befea6",
"txHash": "0xc6a98299de6cb14b8dccaeb9204fca3c93966f5a6da77d6dd799aa94e72f51ff",
"from": {
"address": "0x1137643FE14b032966a59Acd68EBf3c1271Df316"
},
"to": {
"address": "0x063DEB90452247AEcE9Be5F6c076446b3ca01910"
},
"logIndex": 19,
"value": "5000000000000000",
"erc20Token": {
"address": "0x04b9dA42306B023f3572e106B11D82aAd9D32EBb",
"name": "Crystals",
"symbol": "CRYSTAL",
"decimals": 18,
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/0212db53-26f6-4e56-99a2-05526ce32816/4a62bf9e90f401ea69fcf89496cbe96a/53935-0x04b9dA42306B023f3572e106B11D82aAd9D32EBb.png",
"ercType": "ERC-20",
"price": {
"currencyCode": "usd",
"value": 0.00719546
}
}
}
]
}
}
```
Congratulations 🎉 You’ve successfully retrieved the list of ERC-20 transfers for a wallet address with just a few lines of code using the AvaCloud Data API!​
# Etna Upgrade
The **Avalanche9000 (Etna Upgrade)** is focused on reinventing Subnets and providing other UX enhancements. One of the major changes in this upgrade is how users manage subnets and their validators. See [ACP 77](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/77-reinventing-subnets).
## Pre-Etna vs. Post-Etna
### Pre-Etna
* A node had to be a **Primary Network Validator** before it could validate a Subnet.
* All validators, delegators, and rewards for permissionless subnets were managed on the **P-Chain**.
### Post-Etna:
The Avalanche network now supports both **Subnets** and **Layer 1 blockchains (L1s)**:
* **Subnets (legacy flow):**
* Do not pay a continuous fee.
* Validators must be Primary Network Validators.
* **L1s (new flow):**
* Pay a continuous fee.
* Validators do not need to be Primary Network Validators.
* Can be either permissioned or permissionless, depending on the chain manager.
This shift moves the responsibility of validator management from the P-Chain to the Subnets themselves. Permissionless Subnets have been rebranded as **L1s**, reflecting their true potential as independent Layer 1 blockchains. The P-Chain now handles only the registration of these L1 validators and imposes a subscription fee based on the number of active validators for each L1.
Each L1 manages its validators through a [Validator Manager Smart Contract](https://github.com/ava-labs/teleporter/blob/main/contracts/validator-manager/README.md) deployed on a specific blockchain within that L1, streamlining operations and decentralizing management.
***
## Impact on existing subnets
Existing subnets will continue to operate under the legacy flow unless they choose to convert to an L1 using the `ConvertSubnetToL1Tx` transaction. Validators of these Subnets must remain Primary Network Validators. There is no mandatory action required for existing Subnets; however, they can opt to leverage the new L1 functionalities if desired.
***
## L1 validator registration
To convert a permissioned Subnet into an L1, you need to issue a `ConvertSubnetToL1Tx` transaction, providing the validator manager smart contract address and the blockchain ID where this contract is deployed. This transaction requires an array of initial L1 validators with the following details:
* `nodeID`: NodeID of the validator being added.
* `weight`: Weight of the validator being added.
* `balance`: Initial balance for this validator.
* `signer`: The BLS public key and proof-of-possession for the validator.
* `disableOwner`: The P-Chain owner (a set of addresses and threshold) authorized to disable the validator using DisableL1ValidatorTx..
* `remainingBalanceOwner`: The P-Chain owner where any leftover AVAX from the validator’s balance will be sent when the validator is removed from the validator set.
Additional L1 validators can be added later by issuing a `RegisterL1ValidatorTx` and providing the necessary validator details. These L1 validators are assigned a unique `validationID` to identify them across the network. The `validationID` remains valid from the registration of a `NodeID` on a Subnet until it is disabled by setting its weight to `0`.
***
## Weight and balance
Weight and Balance of an L1 validator can be updated by issuing following transactions corresponding to their L1 `validationID`:
* `SetL1ValidatorWeightTx` - Updates the weight of an L1 validator. If the weight is set to `0`, then the validator will be removed and the remaining balance will be returned to `RemainingBalanceOwner`.
* `IncreaseL1ValidatorBalanceTx` - Increases the balance of an L1 validator which will be used as a maintenance fee to the Primary Network.
* `DisableL1ValidatorTx` - Marks the validator as inactive and returning the remaining balance to the `RemainingBalanceOwner`.
***
## Continuous fee mechanism
L1 validators are required to maintain a balance on the P-Chain to cover continuous fees for their participation in the network. The fee is deducted over time from the validator’s balance and is used to compensate for the resources consumed on the Primary Network. Validators should monitor their balance regularly and use the `IncreaseL1ValidatorBalanceTx` to top up their balance as needed to prevent becoming inactive.
### Fee calculation:
* The continuous fee is calculated based on network parameters and the number of active L1 validators.
* The minimum fee rate is **512 nAVAX per second**, which equates to approximately **1.33 AVAX per month** per validator when the number of validators is at or below the target.
### Recommendations for validators:
* Set up alerts or monitoring tools to track balance levels.
* Plan regular intervals for balance top-ups to ensure uninterrupted validation services.
* Remember that any unspent balance can be reclaimed using the `DisableL1ValidatorTx`.
***
## Data API changes
To incorporate Etna-related data, we have made the following changes to the Data API’s Primary Network endpoints:
### 1. List L1 validators
**New endpoint:** [List L1 Validators](/data-api/primary-network/list-l1-validators)
* **Purpose:** Allows listing all or specific L1 validators.
* **Parameters or query changes:**
* `L1ValidationID`: Get details of a specific L1 validator.
* `SubnetID`: Filter validators of a particular subnet.
* `NodeID`: Filter validators associated with a specific `NodeID`.
* `IncludeInactiveL1Validators`: Include inactive L1 validators in the response.
* **Response Changes:**
* Returns a list of L1 validators. Refer to the [endpoint documentation](/data-api/primary-network/list-l1-validators) for full details.
***
### 2. List L1 validator transactions
**Updated endpoint:** [List latest transactions on the primary network](/data-api/primary-network-transactions/list-latest-transactions)
* **Changes:**
* Added a new query parameter `L1ValidationID`.
* **Purpose:**
* By providing `L1ValidationID`, you receive all transactions linked to the validator’s `validationID`, such as `ConvertSubnetToL1Tx`, `IncreaseL1ValidatorBalanceTx`, or `DisableL1ValidatorTx`.
* **Sorting and filtering:**
* Transactions can only be sorted in descending order by their `timestamp`.
* Additional filters include start and end timestamp bounds and transaction types (`txTypes`).
* **Response changes:**
* Refer to [New Transaction Properties](/data-api/etna#3-new-transaction-properties) for details.
***
### 3. New transaction properties
**Updated endpoints:**
* [Get Transaction](https://developers.avacloud.io/data-api/primary-network-transactions/get-transaction)
* [List Latest Transactions](https://developers.avacloud.io/data-api/primary-network-transactions/list-latest-transactions)
* [List Staking Transactions](https://developers.avacloud.io/data-api/primary-network-transactions/list-staking-transactions)
**Parameters or query changes:**
* None
**Response changes:**
The `PChainTransaction` response type now includes the following new properties:
* `L1ValidatorManagerDetails`:
* `BlockchainID`: The blockchain ID where the validator manager is deployed.
* `ContractAddress`: Address of the validator manager smart contract.
* `L1ValidatorDetails`
* `validationID`: Unique identifier for this L1 validation.
* `nodeID`: NodeID of the validator.
* `subnetID`: SubnetID of which this validationID belongs to.
* `weight`: Weight to be used when participating in validation process.
* `remainingBalance`: Remaining L1 validator balance in nAVAX until inactive.
* `balanceChange`: Change in Balance of validator in the current transaction.
* `AmountL1ValidatorBalanceBurned`
* Asset details and amount of AVAX burned to increase the L1 validator balance
***
### 4. New block properties
**Updated endpoints:**
* [Get Block](https://developers.avacloud.io/data-api/primary-network-blocks/get-block)
* [List Latest Blocks](https://developers.avacloud.io/data-api/primary-network-blocks/list-latest-blocks)
* [List Blocks Proposed By Node](https://developers.avacloud.io/data-api/primary-network-blocks/list-blocks-proposed-by-node)
**Parameters or query changes:**
* None
**Response changes:**
Each P-Chain block will now include properties representing L1 validators state at that block height:
* `ActiveL1Validators`: Total active L1 validators
* `L1ValidatorsAccruedFees`: Total fees accrued by network (in nAVAX) from active L1 validators
***
### 5. New Subnet Properties
**Updated endpoint:**
* [Get Subnet Details By ID](https://developers.avacloud.io/data-api/primary-network/get-subnet-details-by-id)
* [List Subnets](https://developers.avacloud.io/data-api/primary-network/list-subnets)
**Parameters or query changes:**
* None
**Response changes:**
Each Subnet will now have a new property IsL1 to identify whether it has been converted to an L1 or not. For all L1s, there will be a new property:
* `isL1`: Whether the subnet is converted to L1 or not
* `L1ValidatorManagerDetails`: Includes `blockchainID` and contract address of the validator manager.
## Additional Resources
For more detailed information and technical specifications, please refer to the following resources:
* [**ACP-77: Reinventing Subnets**:](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/77-reinventing-subnets)
ACP 77 provides an in-depth explanation of the changes introduced in the Etna Upgrade.
* [**What to Expect After the Etna Upgrade**:](https://academy.avax.network/guide/etna-changes)
This guide outlines the operational impact on existing network participants expected from the activation of the AvalancheGo "Etna" upgrade.
* [**Subnet & L1 Validators, What's the Difference?**:](https://academy.avax.network/guide/subnet-vs-l1-validators)
This guide defines the difference between Subnet and L1 validators, differentiating the roles and responsibilities of each.
* [**Validator Manager Smart Contract Documentation**:](https://github.com/ava-labs/teleporter/blob/main/contracts/validator-manager/README.md)
Validator Manager Smart Contract contains technical details on deploying and interacting with the validator manager.
* [**AvalancheGo Implementation Details**:](https://github.com/ava-labs/avalanchego/releases/tag/v1.12.0-fuji)
For developers interested in the implementation, refer to the AvalancheGo repository.
* [**Etna DevNet Resources**:](https://github.com/ava-labs/etna-devnet-resources)
The Etna DevNet is a temporary Avalanche network instance that was created for the purpose of testing and integrating with the changes introduced in the Etna upgrade prior to their activation on the Fuji testnet.
The Etna Upgrade marks a significant milestone in the evolution of the Avalanche network, introducing more flexibility and autonomy for Subnets through the concept of Layer 1 blockchains. By understanding and leveraging these new features, network participants can optimize their operations and contribute to the growth and decentralization of the Avalanche ecosystem.
# Get native token balance
get /v1/chains/{chainId}/addresses/{address}/balances:getNative
Gets native token balance of a wallet address.
Balance at a given block can be retrieved with the `blockNumber` parameter.
# List collectible (ERC-721/ERC-1155) balances
get /v1/chains/{chainId}/addresses/{address}/balances:listCollectibles
Lists ERC-721 and ERC-1155 token balances of a wallet address.
Balance for a specific contract can be retrieved with the `contractAddress` parameter.
# List ERC-1155 balances
get /v1/chains/{chainId}/addresses/{address}/balances:listErc1155
Lists ERC-1155 token balances of a wallet address.
Balance at a given block can be retrieved with the `blockNumber` parameter.
Balance for a specific contract can be retrieved with the `contractAddress` parameter.
# List ERC-20 balances
get /v1/chains/{chainId}/addresses/{address}/balances:listErc20
Lists ERC-20 token balances of a wallet address.
Balance at a given block can be retrieved with the `blockNumber` parameter.
Balance for specific contracts can be retrieved with the `contractAddresses` parameter.
# List ERC-721 balances
get /v1/chains/{chainId}/addresses/{address}/balances:listErc721
Lists ERC-721 token balances of a wallet address.
Balance for a specific contract can be retrieved with the `contractAddress` parameter.
# Get block
get /v1/chains/{chainId}/blocks/{blockId}
Gets the details of an individual block on the EVM-compatible chain.
# List latest blocks
get /v1/chains/{chainId}/blocks
Lists the latest indexed blocks on the EVM-compatible chain sorted in descending order by block timestamp.
# List latest blocks across all supported EVM chains
get /v1/blocks
Lists the most recent blocks from all supported EVM-compatible chains. The results can be filtered by network.
# Get chain information
get /v1/chains/{chainId}
Gets chain information for the EVM-compatible chain if supported by the api.
# null
get /v1/chains/address/{address}
**[Deprecated]** Gets a list of all chains where the address was either a sender or receiver in a transaction or ERC transfer. The list is currently updated every 15 minutes.
⚠️ **This operation will be removed in a future release. Please use /v1/address/:address/chains endpoint instead** .
# List all chains associated with a given address
get /v1/address/{address}/chains
Lists the chains where the specified address has participated in transactions or ERC token transfers, either as a sender or receiver. The data is refreshed every 15 minutes.
# List chains
get /v1/chains
Lists the supported EVM-compatible chains. Filterable by network.
# null
get /v1/chains/allBlocks
**[Deprecated]** Lists the latest blocks for all supported EVM chains. Filterable by network.
⚠️ **This operation will be removed in a future release. Please use /v1/blocks endpoint instead** .
# null
get /v1/chains/allTransactions
**[Deprecated]** Lists the latest transactions for all supported EVM chains. Filterable by status.
⚠️ **This operation will be removed in a future release. Please use /v1/transactions endpoint instead** .
# Get contract metadata
get /v1/chains/{chainId}/addresses/{address}
Gets metadata about the contract at the given address.
# Get deployment transaction
get /v1/chains/{chainId}/contracts/{address}/transactions:getDeployment
If the address is a smart contract, returns the transaction in which it was deployed.
# Get transaction
get /v1/chains/{chainId}/transactions/{txHash}
Gets the details of a single transaction.
# List deployed contracts
get /v1/chains/{chainId}/contracts/{address}/deployments
Lists all contracts deployed by the given address.
# List ERC-1155 transfers
get /v1/chains/{chainId}/addresses/{address}/transactions:listErc1155
Lists ERC-1155 transfers for an address. Filterable by block range.
# List ERC-20 transfers
get /v1/chains/{chainId}/addresses/{address}/transactions:listErc20
Lists ERC-20 transfers for an address. Filterable by block range.
# List ERC-721 transfers
get /v1/chains/{chainId}/addresses/{address}/transactions:listErc721
Lists ERC-721 transfers for an address. Filterable by block range.
# List ERC transfers
get /v1/chains/{chainId}/tokens/{address}/transfers
Lists ERC transfers for an ERC-20, ERC-721, or ERC-1155 contract address.
# List internal transactions
get /v1/chains/{chainId}/addresses/{address}/transactions:listInternals
Returns a list of internal transactions for an address and chain. Filterable by block range.
Note that the internal transactions list only contains `CALL` or `CALLCODE` transactions with a non-zero value and `CREATE`/`CREATE2` transactions. To get a complete list of internal transactions use the `debug_` prefixed RPC methods on an archive node.
# List latest transactions
get /v1/chains/{chainId}/transactions
Lists the latest transactions. Filterable by status.
# List native transactions
get /v1/chains/{chainId}/addresses/{address}/transactions:listNative
Lists native transactions for an address. Filterable by block range.
# List the latest transactions across all supported EVM chains
get /v1/transactions
Lists the most recent transactions from all supported EVM-compatible chains. The results can be filtered based on transaction status.
# List transactions
get /v1/chains/{chainId}/addresses/{address}/transactions
Returns a list of transactions where the given wallet address had an on-chain interaction for the given chain. The ERC-20 transfers, ERC-721 transfers, ERC-1155, and internal transactions returned are only those where the input address had an interaction. Specifically, those lists only inlcude entries where the input address was the sender (`from` field) or the receiver (`to` field) for the sub-transaction. Therefore the transactions returned from this list may not be complete representations of the on-chain data. For a complete view of a transaction use the `/chains/:chainId/transactions/:txHash` endpoint.
Filterable by block ranges.
# List transactions for a block
get /v1/chains/{chainId}/blocks/{blockId}/transactions
Lists the transactions that occured in a given block.
# Getting Started
To begin, create your free AvaCloud account by visiting [AvaCloud](https://app.avacloud.io/).
Once the account is created:
1. Navigating to **Web3 Data API**
2. Click on **Add API Key**
3. Set an alias and click on **create**
4. Copy the the value
Always keep your API keys in a secure environment. Never expose them in public repositories, such as GitHub, or share them with unauthorized individuals. Compromised API keys can lead to unauthorized access and potential misuse of your account.
With your API Key you can start making queries, for example to get the latest block on the C-chain(43114):
```bash
curl --location 'https://glacier-api.avax.network/v1/chains/43114/blocks' \
--header 'accept: application/json' \
--header 'x-glacier-api-key: ' \
```
And you should see something like this:
```json
{
"blocks": [
{
"blockNumber": "49889407",
"blockTimestamp": 1724990250,
"blockHash": "0xd34becc82943e3e49048cdd3f75b80a87e44eb3aed6b87cc06867a7c3b9ee213",
"txCount": 1,
"baseFee": "25000000000",
"gasUsed": "53608",
"gasLimit": "15000000",
"gasCost": "0",
"parentHash": "0xf4917efb4628a1d8f4d101b3d15bce9826e62ef2c93c3e16ee898d27cf02f3d4",
"feesSpent": "1435117553916960",
"cumulativeTransactions": "500325352"
},
{
"blockNumber": "49889406",
"blockTimestamp": 1724990248,
"blockHash": "0xf4917efb4628a1d8f4d101b3d15bce9826e62ef2c93c3e16ee898d27cf02f3d4",
"txCount": 2,
"baseFee": "25000000000",
"gasUsed": "169050",
"gasLimit": "15000000",
"gasCost": "0",
"parentHash": "0x2a54f142fa3acee92a839b071bb6c7cca7abc2a797cf4aac68b07f79406ac0cb",
"feesSpent": "4226250000000000",
"cumulativeTransactions": "500325351"
},
{
"blockNumber": "49889405",
"blockTimestamp": 1724990246,
"blockHash": "0x2a54f142fa3acee92a839b071bb6c7cca7abc2a797cf4aac68b07f79406ac0cb",
"txCount": 4,
"baseFee": "25000000000",
"gasUsed": "618638",
"gasLimit": "15000000",
"gasCost": "0",
"parentHash": "0x0cda1bb5c86e790976c9330c9fc26e241a705afbad11a4caa44df1c81058451d",
"feesSpent": "16763932426044724",
"cumulativeTransactions": "500325349"
},
{
"blockNumber": "49889404",
"blockTimestamp": 1724990244,
"blockHash": "0x0cda1bb5c86e790976c9330c9fc26e241a705afbad11a4caa44df1c81058451d",
"txCount": 3,
"baseFee": "25000000000",
"gasUsed": "254544",
"gasLimit": "15000000",
"gasCost": "0",
"parentHash": "0x60e55dd9eacc095c07f50a73e02d81341c406584f7abbf5d10d938776a4c893c",
"feesSpent": "6984642298020000",
"cumulativeTransactions": "500325345"
},
{
"blockNumber": "49889403",
"blockTimestamp": 1724990242,
"blockHash": "0x60e55dd9eacc095c07f50a73e02d81341c406584f7abbf5d10d938776a4c893c",
"txCount": 2,
"baseFee": "25000000000",
"gasUsed": "65050",
"gasLimit": "15000000",
"gasCost": "0",
"parentHash": "0xa3e9f91f45a85ed00b8ebe8e5e976ed1a1f52612143eddd3de9d2588d05398b8",
"feesSpent": "1846500000000000",
"cumulativeTransactions": "500325342"
},
{
"blockNumber": "49889402",
"blockTimestamp": 1724990240,
"blockHash": "0xa3e9f91f45a85ed00b8ebe8e5e976ed1a1f52612143eddd3de9d2588d05398b8",
"txCount": 2,
"baseFee": "25000000000",
"gasUsed": "74608",
"gasLimit": "15000000",
"gasCost": "0",
"parentHash": "0x670db772edfc2fdae322d55473ba0670690aed6358a067a718492c819d63356a",
"feesSpent": "1997299851936960",
"cumulativeTransactions": "500325340"
},
{
"blockNumber": "49889401",
"blockTimestamp": 1724990238,
"blockHash": "0x670db772edfc2fdae322d55473ba0670690aed6358a067a718492c819d63356a",
"txCount": 1,
"baseFee": "25000000000",
"gasUsed": "273992",
"gasLimit": "15000000",
"gasCost": "0",
"parentHash": "0x75742cf45383ce54823690b9dd2e85a743be819281468163d276f145d077902a",
"feesSpent": "7334926295195040",
"cumulativeTransactions": "500325338"
},
{
"blockNumber": "49889400",
"blockTimestamp": 1724990236,
"blockHash": "0x75742cf45383ce54823690b9dd2e85a743be819281468163d276f145d077902a",
"txCount": 1,
"baseFee": "25000000000",
"gasUsed": "291509",
"gasLimit": "15000000",
"gasCost": "0",
"parentHash": "0xe5055eae3e1fd2df24b61e9c691f756c97e5619cfc66b69cbcb6025117d1bde7",
"feesSpent": "7724988500000000",
"cumulativeTransactions": "500325337"
},
{
"blockNumber": "49889399",
"blockTimestamp": 1724990234,
"blockHash": "0xe5055eae3e1fd2df24b61e9c691f756c97e5619cfc66b69cbcb6025117d1bde7",
"txCount": 8,
"baseFee": "25000000000",
"gasUsed": "824335",
"gasLimit": "15000000",
"gasCost": "0",
"parentHash": "0xbcacff928f7dd20cc1522155e7c9b9716997914b53ab94034b813c3f207174ef",
"feesSpent": "21983004380692400",
"cumulativeTransactions": "500325336"
},
{
"blockNumber": "49889398",
"blockTimestamp": 1724990229,
"blockHash": "0xbcacff928f7dd20cc1522155e7c9b9716997914b53ab94034b813c3f207174ef",
"txCount": 1,
"baseFee": "25000000000",
"gasUsed": "21000",
"gasLimit": "15000000",
"gasCost": "0",
"parentHash": "0x0b686812078429d33e4224d2b48bd26b920db8dbb464e7f135d980759ca7e947",
"feesSpent": "562182298020000",
"cumulativeTransactions": "500325328"
}
],
"nextPageToken": "9f9e1d25-14a9-49f4-8742-fd4bf12f7cd8"
}
```
Congratulations! You’ve successfully set up your AvaCloud account and made your first query to the Data API 🚀🚀🚀
# Get logs for requests made by client
get /v1/apiLogs
Gets logs for requests made by client over a specified time interval for a specific organization.
# Get usage metrics for the Data API
get /v1/apiUsageMetrics
Gets metrics for Data API usage over a specified time interval aggregated at the specified time-duration granularity.
# Get the health of the service
get /v1/health-check
# null
get /v1/address/{address}
# null
get /v1/allBlocks
# null
get /v1/allTransactions
# How to get the native balance of an address
Checking the balance of a wallet is vital for users who want to manage their digital assets effectively. By reviewing their wallet balance, users can:
* **Track their asset portfolio**: Regularly monitoring the wallet balance keeps users informed about the value of their holdings, enabling them to make informed decisions on buying, selling, or retaining their digital assets.
* **Confirm received transactions**: When expecting digital assets, verifying the wallet balance helps ensure that transactions have been successfully completed and the correct amounts have been received.
* **Plan future transactions**: Knowing the wallet’s balance allows users to prepare for upcoming transactions and verify that they have enough funds to cover fees or other related costs.
This guide will walk you through how to get the native balance associated with a specific wallet address on the C-chain network using the Data API.
### Step 1: Set Up AvaCloud
First, ensure that you have set up [AvaCloud](https://app.avacloud.io/) and have access to your API key. If you’re new to Avacloud, create an account and obtain an API key.
### Step 2: Retrieve the Native Balance of an Address
To obtain the native balance of a wallet address you can use [Get native token balance](/data-api/evm-balances/get-native-token-balance) endpoint. You’ll need to specify the `chainId` and the `address` for which you want to retrieve the balance.
Here’s how you can do it:
```javascript AvacloudSDK
import { AvaCloudSDK } from "@avalabs/avacloud-sdk";
const avaCloudSDK = new AvaCloudSDK({
apiKey: "",
chainId: "43114",
network: "mainnet",
});
async function run() {
const result = await avaCloudSDK.data.evm.balances.getNativeBalance({
blockNumber: "6479329",
address: "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
currency: "usd",
});
// Handle the result
console.log(JSON.stringify(result, null, 2));
}
run();
```
```bash cURL
curl --request GET \
--url https://glacier-api.avax.network/v1/chains/43114/addresses/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/balances:getNative \
--header 'x-glacier-api-key: '
```
Response
```json
{
"nativeTokenBalance": {
"chainId": "43114",
"name": "Avalanche",
"symbol": "AVAX",
"decimals": 18,
"price": {
"currencyCode": "usd",
"value": 26.32
},
"balance": "3316667947990566036",
"balanceValue": {
"currencyCode": "usd",
"value": 87.29
},
"logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/3e4b8ff10b69bfa31e70080a4b142cd0/avalanche-avax-logo.svg"
}
}
```
Congratulations 🎉 You’ve successfully retrieved the native balance of a wallet address using just a few lines of code with the AvaCloud Data API!
# Get token details
get /v1/chains/{chainId}/nfts/collections/{address}/tokens/{tokenId}
Gets token details for a specific token of an NFT contract.
# List tokens
get /v1/chains/{chainId}/nfts/collections/{address}/tokens
Lists tokens for an NFT contract.
# Reindex NFT metadata
post /v1/chains/{chainId}/nfts/collections/{address}/tokens/{tokenId}:reindex
Triggers reindexing of token metadata for an NFT token. Reindexing can only be called once per hour for each NFT token.
# Create transaction export operation
post /v1/operations/transactions:export
Trigger a transaction export operation with given parameters.
The transaction export operation runs asynchronously in the background. The status of the job can be retrieved from the `/v1/operations/:operationId` endpoint using the `operationId` returned from this endpoint.
# Get operation
get /v1/operations/{operationId}
Gets operation details for the given operation id.
# Overview
### What is the Data API?
The Data API provides web3 application developers with multi-chain data related to Avalanche's primary network, Avalanche L1s, and Ethereum. With the Data API, you can easily build products that leverage real-time and historical transaction and transfer history, native and token balances, and various types of token metadata.
![Data API](https://mintlify.s3.us-west-1.amazonaws.com/avalabs-47ea3976/images/data-api.png)
The [Data API](/data-api), along with the [Metrics API](/metrics-api), are the engines behind the [Avalanche Explorer](https://subnets.avax.network/stats/) and the [Core wallet](https://core.app/en/). They are used to display transactions, logs, balances, NFTs, and more. The data and visualizations presented are all powered by these APIs, offering real-time and historical insights that are essential for building sophisticated, data-driven blockchain products.
The Data API and Glacier API refer to the same API. If you encounter the term “Glacier API” in other documentation, it is referring to the Data API, which was previously known as the Glacier API.​
### Features
* **Extensive L1 Support**: Gain access to data from over 100+ L1s across both mainnet and testnet. If an L1 is listed on the [Avalanche Explorer](https://subnets.avax.network/), you can query its data using the Data API.
* **Transactions and UTXOs**: easily retrieve details related to transactions, UTXOs, and token transfers from Avalanche EVMs, Ethereum, and Avalanche's Primary Network - the P-Chain, X-Chain and C-Chain.
* **Blocks**: retrieve latest blocks and block details
* **Balances**: fetch balances of native, ERC-20, ERC-721, and ERC-1155 tokens along with relevant metadata.
* **Tokens**: augment your user experience with asset details.
* **Staking**: get staking related data for active and historical validations.
### Supported Chains
Avalanche’s architecture supports a diverse ecosystem of interconnected L1 blockchains, each operating independently while retaining the ability to seamlessly communicate with other L1s within the network. Central to this architecture is the Primary Network—Avalanche’s foundational network layer, which all validators are required to validate prior to [ACP-77](https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/77-reinventing-subnets/README.md). The Primary Network runs three essential blockchains:
* The Contract Chain (C-Chain)
* The Platform Chain (P-Chain)
* The Exchange Chain (X-Chain)
However, with the implementation of [ACP-77](https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/77-reinventing-subnets/README.md), this requirement will change. Subnet Validators will be able to operate independently of the Primary Network, allowing for more flexible and affordable Subnet creation and management.
The **Data API** supports a wide range of L1 blockchains (**over 100**) across both **mainnet** and **testnet**, including popular ones like Beam, DFK, Lamina1, Dexalot, Shrapnel, and Pulsar. In fact, every L1 you see on the [Avalanche Explorer](https://subnets.avax.network/) can be queried through the Data API. This list is continually expanding as we keep adding more L1s. For a full list of supported chains, visit [List chains](/data-api/evm-chains/list-chains).
#### The Contract Chain (C-Chain)
The C-Chain is an implementation of the Ethereum Virtual Machine (EVM). The primary network endpoints only provide information related to C-Chain atomic memory balances and import/export transactions. For additional data, please reference the EVM APIs.
#### The Platform Chain (P-Chain)
The P-Chain is responsible for all validator and L1-level operations. The P-Chain supports the creation of new blockchains and L1s, the addition of validators to L1s, staking operations, and other platform-level operations.
#### The Exchange Chain (X-Chain)
The X-Chain is responsible for operations on digital smart assets known as Avalanche Native Tokens. A smart asset is a representation of a real-world resource (for example, equity, or a bond) with sets of rules that govern its behavior, like "can’t be traded until tomorrow." The X-Chain supports the creation and trade of Avalanche Native Tokens.
| Feature | Description |
| :--------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Chains** | Utilize this endpoint to retrieve the Primary Network chains that an address has transaction history associated with. |
| **Blocks** | Blocks are the container for transactions executed on the Primary Network. Retrieve the latest blocks, a specific block by height or hash, or a list of blocks proposed by a specified NodeID on Primary Network chains. |
| **Vertices** | Prior to Avalanche Cortina (v1.10.0), the X-Chain functioned as a DAG with vertices rather than blocks. These endpoints allow developers to retrieve historical data related to that period of chain history. Retrieve the latest vertices, a specific vertex, or a list of vertices at a specific height from the X-Chain. |
| **Transactions** | Transactions are a user's primary form of interaction with a chain and provide details around their on-chain activity, including staking-related behavior. Retrieve a list of the latest transactions, a specific transaction, a list of active staking transactions for a specified address, or a list of transactions associated with a provided asset id from Primary Network chains. |
| **UTXOs** | UTXOs are fundamental elements that denote the funds a user has available. Get a list of UTXOs for provided addresses from the Primary Network chains. |
| **Balances** | User balances are an essential function of the blockchain. Retrieve balances related to the X and P-Chains, as well as atomic memory balances for the C-Chain. |
| **Rewards** | Staking is the process where users lock up their tokens to support a blockchain network and, in return, receive rewards. It is an essential part of proof-of-stake (PoS) consensus mechanisms used by many blockchain networks, including Avalanche. Using the Data API, you can easily access pending and historical rewards associated with a set of addresses. |
| **Assets** | Get asset details corresponding to the given asset id on the X-Chain. |
#### EVM
The C-Chain is an instance of the Coreth Virtual Machine, and many Avalanche L1s are instances of the *Subnet-EVM*, which is a Virtual Machine (VM) that defines the L1 Contract Chains. *Subnet-EVM* is a simplified version of *Coreth VM* (C-Chain).
| Feature | Description |
| :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Chains** | There are a number of chains supported by the Data API. These endpoints can be used to understand which chains are included/indexed as part of the API and retrieve information related to a specific chain. |
| **Blocks** | Blocks are the container for transactions executed within the EVM. Retrieve the latest blocks or a specific block by height or hash. |
| **Transactions** | Transactions are a user's primary form of interaction with a chain and provide details around their on-chain activity. These endpoints can be used to retrieve information related to specific transaction details, internal transactions, contract deployments, specific token standard transfers, and more! |
| **Balances** | User balances are an essential function of the blockchain. Easily retrieve native token, collectible, and fungible token balances related to an EVM chain with these endpoints. |
#### Operations
The Operations API allows users to easily access their on-chain history by creating transaction exports returned in a CSV format. This API supports EVMs as well as non-EVM Primary Network chains.
# Get balances
get /v1/networks/{network}/blockchains/{blockchainId}/balances
Gets primary network balances for one of the Primary Network chains for the supplied addresses.
C-Chain balances returned are only the shared atomic memory balance. For EVM balance, use the `/v1/chains/:chainId/addresses/:addressId/balances:getNative` endpoint.
# Get block
get /v1/networks/{network}/blockchains/{blockchainId}/blocks/{blockId}
Gets a block by block height or block hash on one of the Primary Network chains.
# List blocks proposed by node
get /v1/networks/{network}/blockchains/{blockchainId}/nodes/{nodeId}/blocks
Lists the latest blocks proposed by a given NodeID on one of the Primary Network chains.
# List latest blocks
get /v1/networks/{network}/blockchains/{blockchainId}/blocks
Lists latest blocks on one of the Primary Network chains.
# List historical rewards
get /v1/networks/{network}/rewards
Lists historical rewards on the Primary Network for the supplied addresses.
# List pending rewards
get /v1/networks/{network}/rewards:listPending
Lists pending rewards on the Primary Network for the supplied addresses.
# Get transaction
get /v1/networks/{network}/blockchains/{blockchainId}/transactions/{txHash}
Gets the details of a single transaction on one of the Primary Network chains.
# List asset transactions
get /v1/networks/{network}/blockchains/{blockchainId}/assets/{assetId}/transactions
Lists asset transactions corresponding to the given asset id on the X-Chain.
# List latest transactions
get /v1/networks/{network}/blockchains/{blockchainId}/transactions
Lists the latest transactions on one of the Primary Network chains.
Transactions are filterable by addresses, txTypes, and timestamps. When querying for latest transactions without an address parameter, filtering by txTypes and timestamps is not supported. An address filter must be provided to utilize txTypes and timestamp filters.
For P-Chain, you can fetch all L1 validators related transactions like ConvertSubnetToL1Tx, IncreaseL1ValidatorBalanceTx etc. using the unique L1 validation ID. These transactions are further filterable by txTypes and timestamps as well.
Given that each transaction may return a large number of UTXO objects, bounded only by the maximum transaction size, the query may return less transactions than the provided page size. The result will contain less results than the page size if the number of utxos contained in the resulting transactions reach a performance threshold.
# List staking transactions
get /v1/networks/{network}/blockchains/{blockchainId}/transactions:listStaking
Lists active staking transactions on the P-Chain for the supplied addresses.
# List UTXOs
get /v1/networks/{network}/blockchains/{blockchainId}/utxos
Lists UTXOs on one of the Primary Network chains for the supplied addresses.
# Get vertex
get /v1/networks/{network}/blockchains/{blockchainId}/vertices/{vertexHash}
Gets a single vertex on the X-Chain.
# List vertices
get /v1/networks/{network}/blockchains/{blockchainId}/vertices
Lists latest vertices on the X-Chain.
# List vertices by height
get /v1/networks/{network}/blockchains/{blockchainId}/vertices:listByHeight
Lists vertices at the given vertex height on the X-Chain.
# Get asset details
get /v1/networks/{network}/blockchains/{blockchainId}/assets/{assetId}
Gets asset details corresponding to the given asset id on the X-Chain.
# Get chain interactions for addresses
get /v1/networks/{network}/addresses:listChainIds
Returns Primary Network chains that each address has touched in the form of an address mapped array. If an address has had any on-chain interaction for a chain, that chain's chain id will be returned.
# Get network details
get /v1/networks/{network}
Gets network details such as validator and delegator stats.
# Get single validator details
get /v1/networks/{network}/validators/{nodeId}
List validator details for a single validator. Filterable by validation status.
# Get Subnet details by ID
get /v1/networks/{network}/subnets/{subnetId}
Get details of the Subnet registered on the network.
# List blockchains
get /v1/networks/{network}/blockchains
Lists all blockchains registered on the network.
# List delegators
get /v1/networks/{network}/delegators
Lists details for delegators.
# List L1 validators
get /v1/networks/{network}/l1Validators
Lists details for L1 validators. By default, returns details for all active L1 validators. Filterable by validator node ids, subnet id, and validation id.
# List subnets
get /v1/networks/{network}/subnets
Lists all subnets registered on the network.
# List validators
get /v1/networks/{network}/validators
Lists details for validators. By default, returns details for all validators. Filterable by validator node ids and minimum delegation capacity.
# Rate Limits
Rate limiting is managed through a weighted scoring system, known as Compute Units (CUs). Each API request consumes a specified number of CUs, determined by the complexity of the request. This system is designed to accommodate basic requests while efficiently handling more computationally intensive operations.
## Rate Limit Tiers
The maximum CUs (rate-limiting score) for a user depends on their subscription level and is delineated in the following table:
| Subscription Level | Per Minute Limit (CUs) | Per Day Limit (CUs) |
| :----------------- | :--------------------- | :------------------ |
| Unauthenticated | 6,000 | 1,200,000 |
| Free | 8,000 | 2,000,000 |
| Base | 10,000 | 3,750,000 |
| Growth | 14,000 | 11,200,000 |
| Pro | 20,000 | 25,000,000 |
To update your subscription level use the [AvaCloud Portal](https://app.avacloud.io/)
Note: Rate limits apply collectively across both Webhooks and Data APIs, with usage from each counting toward your total CU limit.
## Rate Limit Categories
The CUs for each category are defined in the following table:
{/* data weights value table start */}
| Weight | CU Value |
| :----- | :------- |
| Free | 1 |
| Small | 10 |
| Medium | 20 |
| Large | 50 |
| XL | 100 |
| XXL | 200 |
{/* data weights value table end */}
## Rate Limits for Data API Endpoints
The CUs for each route are defined in the table below:
{/* data execution weights table start */}
| Endpoint | Method | Weight | CU Value |
| :-------------------------------------------------------------------------------- | :----- | :----- | :------- |
| `/v1/health-check` | GET | Medium | 20 |
| `/v1/address/{address}` | GET | Medium | 20 |
| `/v1/allTransactions` | GET | Medium | 20 |
| `/v1/allBlocks` | GET | Medium | 20 |
| `/v1/chains/{chainId}/nfts/collections/{address}/tokens/{tokenId}:reindex` | POST | Small | 10 |
| `/v1/chains/{chainId}/nfts/collections/{address}/tokens` | GET | Medium | 20 |
| `/v1/chains/{chainId}/nfts/collections/{address}/tokens/{tokenId}` | GET | Medium | 20 |
| `/v1/operations/{operationId}` | GET | Small | 10 |
| `/v1/operations/transactions:export` | POST | Medium | 20 |
| `/v1/networks/{network}/blockchains/{blockchainId}/transactions/{txHash}` | GET | Medium | 20 |
| `/v1/networks/{network}/blockchains/{blockchainId}/transactions` | GET | XL | 100 |
| `/v1/networks/{network}/blockchains/{blockchainId}/transactions:listStaking` | GET | XL | 100 |
| `/v1/networks/{network}/rewards:listPending` | GET | XL | 100 |
| `/v1/networks/{network}/rewards` | GET | XL | 100 |
| `/v1/networks/{network}/blockchains/{blockchainId}/utxos` | GET | XL | 100 |
| `/v1/networks/{network}/blockchains/{blockchainId}/balances` | GET | XL | 100 |
| `/v1/networks/{network}/blockchains/{blockchainId}/blocks/{blockId}` | GET | XL | 100 |
| `/v1/networks/{network}/blockchains/{blockchainId}/nodes/{nodeId}/blocks` | GET | Medium | 20 |
| `/v1/networks/{network}/blockchains/{blockchainId}/blocks` | GET | Medium | 20 |
| `/v1/networks/{network}/blockchains/{blockchainId}/vertices` | GET | Medium | 20 |
| `/v1/networks/{network}/blockchains/{blockchainId}/vertices/{vertexHash}` | GET | Medium | 20 |
| `/v1/networks/{network}/blockchains/{blockchainId}/vertices:listByHeight` | GET | Medium | 20 |
| `/v1/networks/{network}/blockchains/{blockchainId}/assets/{assetId}` | GET | XL | 100 |
| `/v1/networks/{network}/blockchains/{blockchainId}/assets/{assetId}/transactions` | GET | XL | 100 |
| `/v1/networks/{network}/addresses:listChainIds` | GET | XL | 100 |
| `/v1/networks/{network}` | GET | XL | 100 |
| `/v1/networks/{network}/blockchains` | GET | Medium | 20 |
| `/v1/networks/{network}/subnets` | GET | Medium | 20 |
| `/v1/networks/{network}/subnets/{subnetId}` | GET | Medium | 20 |
| `/v1/networks/{network}/validators` | GET | Medium | 20 |
| `/v1/networks/{network}/validators/{nodeId}` | GET | Medium | 20 |
| `/v1/networks/{network}/delegators` | GET | Medium | 20 |
| `/v1/networks/{network}/l1Validators` | GET | Medium | 20 |
| `/v1/teleporter/messages/{messageId}` | GET | Medium | 20 |
| `/v1/teleporter/messages` | GET | Medium | 20 |
| `/v1/teleporter/addresses/{address}/messages` | GET | Medium | 20 |
| `/v1/apiUsageMetrics` | GET | XXL | 200 |
| `/v1/apiLogs` | GET | XXL | 200 |
| `/v1/rpcUsageMetrics` | GET | XXL | 200 |
| `/v1/primaryNetworkRpcUsageMetrics` | GET | XXL | 200 |
| `/v1/signatureAggregator/aggregateSignatures` | POST | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}/balances:getNative` | GET | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}/balances:listErc20` | GET | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}/balances:listErc721` | GET | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}/balances:listErc1155` | GET | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}/balances:listCollectibles` | GET | Medium | 20 |
| `/v1/chains/{chainId}/blocks` | GET | Small | 10 |
| `/v1/chains/{chainId}/blocks/{blockId}` | GET | Small | 10 |
| `/v1/chains/{chainId}/contracts/{address}/transactions:getDeployment` | GET | Medium | 20 |
| `/v1/chains/{chainId}/contracts/{address}/deployments` | GET | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}` | GET | Medium | 20 |
| `/v1/chains` | GET | Free | 1 |
| `/v1/chains/{chainId}` | GET | Free | 1 |
| `/v1/chains/address/{address}` | GET | Free | 1 |
| `/v1/chains/allTransactions` | GET | Free | 1 |
| `/v1/chains/allBlocks` | GET | Free | 1 |
| `/v1/chains/{chainId}/tokens/{address}/transfers` | GET | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}/transactions` | GET | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}/transactions:listNative` | GET | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}/transactions:listErc20` | GET | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}/transactions:listErc721` | GET | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}/transactions:listErc1155` | GET | Medium | 20 |
| `/v1/chains/{chainId}/addresses/{address}/transactions:listInternals` | GET | Medium | 20 |
| `/v1/chains/{chainId}/transactions/{txHash}` | GET | Medium | 20 |
| `/v1/chains/{chainId}/blocks/{blockId}/transactions` | GET | Medium | 20 |
| `/v1/chains/{chainId}/transactions` | GET | Medium | 20 |
{/* data execution weights table end */}
## Rate Limits for RPC endpoints
The CUs for RPC calls are calculated based on the RPC method(s) within the request. The CUs assigned to each method are defined in the table below:
{/* rpc execution weights table start */}
| Method | Weight | CU Value |
| :---------------------------------------- | :----- | :------- |
| `eth_accounts` | Free | 1 |
| `eth_blockNumber` | Small | 10 |
| `eth_call` | Small | 10 |
| `eth_coinbase` | Small | 10 |
| `eth_chainId` | Free | 1 |
| `eth_gasPrice` | Small | 10 |
| `eth_getBalance` | Small | 10 |
| `eth_getBlockByHash` | Small | 10 |
| `eth_getBlockByNumber` | Small | 10 |
| `eth_getBlockTransactionCountByNumber` | Medium | 20 |
| `eth_getCode` | Medium | 20 |
| `eth_getLogs` | XXL | 200 |
| `eth_getStorageAt` | Medium | 20 |
| `eth_getTransactionByBlockNumberAndIndex` | Medium | 20 |
| `eth_getTransactionByHash` | Small | 10 |
| `eth_getTransactionCount` | Small | 10 |
| `eth_getTransactionReceipt` | Small | 10 |
| `eth_signTransaction` | Medium | 20 |
| `eth_sendTransaction` | Medium | 20 |
| `eth_sign` | Medium | 20 |
| `eth_sendRawTransaction` | Small | 10 |
| `eth_syncing` | Free | 1 |
| `net_listening` | Free | 1 |
| `net_peerCount` | Medium | 20 |
| `net_version` | Free | 1 |
| `web3_clientVersion` | Small | 10 |
| `web3_sha3` | Small | 10 |
| `eth_newPendingTransactionFilter` | Medium | 20 |
| `eth_maxPriorityFeePerGas` | Small | 10 |
| `eth_baseFee` | Small | 10 |
| `rpc_modules` | Free | 1 |
| `eth_getChainConfig` | Small | 10 |
| `eth_feeConfig` | Small | 10 |
| `eth_getActivePrecompilesAt` | Small | 10 |
{/* rpc execution weights table end */}
All rate limits, weights, and CU values are subject to change.
# Aggregate Signatures
post /v1/signatureAggregator/aggregateSignatures
Aggregates Signatures for a Warp message from Subnet validators.
# Get a teleporter message
get /v1/teleporter/messages/{messageId}
Gets a teleporter message by message ID.
# List teleporter messages
get /v1/teleporter/messages
Lists teleporter messages. Ordered by timestamp in descending order.
# List teleporter messages by address
get /v1/teleporter/addresses/{address}/messages
Lists teleporter messages by address. Ordered by timestamp in descending order.
# List teleporter messages by address
get /v1/teleporter/addresses/{address}/messages
Lists teleporter messages by address. Ordered by timestamp in descending order.
# Usage Guide
### Setup and Authentication
In order to utilize your accounts rate limits, you will need to make API requests with an API key. You can generate API Keys from the AvaCloud portal. Once you've created and retrieved that, you will be able to make authenticated queries by passing in your API key in the `x-glacier-api-key` header of your HTTP request.
An example curl request can be found below:
```bash
curl -H "Content-Type: Application/json" -H "x-glacier-api-key: your_api_key" \
"https://glacier-api.avax.network/v1/chains"
```
### Rate Limits
The Data API has rate limits in place to maintain it's stability and protect from bursts of incoming traffic. The rate limits associated with various plans can be found within AvaCloud.
When you hit your rate limit, the server will respond with a 429 http response code, and response headers to help you determine when you should start to make additional requests. The response headers follow the standards set in the RateLimit header fields for HTTP draft from the Internet Engineering Task Force.
With every response with a valid api key, the server will include the following headers:
* `ratelimit-policy` - The rate limit policy tied to your api key.
* `ratelimit-limit` - The number of requests you can send according to your policy.
* `ratelimit-remaining` - How many request remaining you can send in the period for your policy
For any request after the rate limit has been reached, the server will also respond with these headers:
* `ratelimit-reset`
* `retry-after`
Both of these headers are set to the number of seconds until your period is over and requests will start succeeding again.
If you start receiving rate limit errors with the 429 response code, we recommend you discontinue sending requests to the server. You should wait to retry requests for the duration specified in the response headers. Alternatively, you can implement an exponential backoff algorithm to prevent continuous errors. Failure to discontinue requests may result in being temporarily blocked from accessing the API.
Error Types
The Data API generates standard error responses along with error codes based on provided requests and parameters.
Typically, response codes within the `2XX` range signifies successful requests, while those within the `4XX` range points to errors originating from the client's side. Meanwhile, response codes within the `5XX` range indicates problems on the server's side.
### Error Types
The Glacier API generates standard error responses along with error codes based on provided requests and parameters.
Typically, response codes within the `2XX` range signifies successful requests, while those within the `4XX` range points to errors originating from the client's side. Meanwhile, response codes within the `5XX` range indicates problems on the server's side.
The error response body is formatted like this:
```json
{
"message": ["Invalid address format"], // route specific error message
"error": "Bad Request", // error type
"statusCode": 400 // http response code
}
```
Let's go through every error code that we can respond with:
| Error Code | Error Type | Description |
| :--------- | :-------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **400** | Bad Request | Bad requests generally mean the client has passed invalid or malformed parameters. Error messages in the response could help in evaluating the error. |
| **401** | Unauthorized | When a client attempts to access resources that require authorization credentials but the client lacks proper authentication in the request, the server responds with 401. |
| **403** | Forbidden | When a client attempts to access resources with valid credentials but doesn't have the privilege to perform that action, the server responds with 403. |
| **404** | Not Found | The 404 error is mostly returned when the client requests with either mistyped URL, or the passed resource is moved or deleted, or the resource doesn't exist. |
| **500** | Internal Server Error | The 500 error is a generic server-side error that is returned for any uncaught and unexpected issues on the server side. This should be very rare, and you may reach out to us if the problem persists for a longer duration. |
| **502** | Bad Gateway | This is an internal error indicating invalid response received by the client-facing proxy or gateway from the upstream server. |
| **503** | Service Unavailable | The 503 error is returned for certain routes on a particular Subnet. This indicates an internal problem with our Subnet node, and may not necessarily mean the Subnet is down or affected. |
The above list is not exhaustive of all the errors that you'll receive, but is categorized on the basis of error codes. You may see route-specific errors along with detailed error messages for better evaluating the response.
Reach out to our team when you see an error in the `5XX` range for a longer duration. These errors should be very rare, but we try to fix them as soon as possible once detected.
### Pagination
When utilizing pagination for endpoints that return lists of data such as transactions, UTXOs, or blocks, our API uses a straightforward mechanism to manage the navigation through large datasets. We divide data into pages and each page is limited with a `pageSize` number of elements as passed in the request. Users can navigate to subsequent pages using the page token received in the `nextPageToken` field. This method ensures efficient retrieval.
Routes with pagination have a following common response format:
```json
{
"blocks": [""], // This field name will vary by route
"nextPageToken": "3d22deea-ea64-4d30-8a1e-c2a353b67e90"
}
```
### Page Token Structure
* If there's more data in the dataset for the request, the API will include a UUID-based page token in the response. This token acts as a pointer to the next page of data.
* The UUID page token is generated randomly and uniquely for each pagination scenario, enhancing security and minimizing predictability.
* It's important to note that the page token is only returned when a next page is present. If there's no further data to retrieve, a page token will not be included in the response.
* The generated page token has an expiration window of 24 hours. Beyond this timeframe, the token will no longer be valid for accessing subsequent pages.
### Integration and Usage:
To make use of the pagination system, simply examine the API response. If a UUID page token is present, it indicates the availability of additional data on the next page. You can extract this token and include it in the subsequent request to access the subsequent page of results.
Please note that you must ensure that the subsequent request is made within the 24-hour timeframe after the original token's generation. Beyond this duration, the token will expire, and you will need to initiate a fresh request from the initial page.
By incorporating UUID page tokens, our API offers a secure, efficient, and user-friendly approach to navigating large datasets, streamlining your data retrieval proces
### Swagger API Reference
You can explore the full API definitions and interact with the endpoints in the Swagger documentation at:
[https://glacier-api.avax.network/api](https://glacier-api.avax.network/api)
# Add addresses to webhook
patch /v1/webhooks/{id}/addresses
Add addresses to webhook.
# Create a webhook
post /v1/webhooks
Create a new webhook.
# Deactivate a webhook
delete /v1/webhooks/{id}
Deactivates a webhook by ID.
# Generate a shared secret
post /v1/webhooks:generateOrRotateSharedSecret
Generates a new shared secret.
# Get a shared secret
get /v1/webhooks:getSharedSecret
Get a previously generated shared secret.
# Get a webhook by ID
get /v1/webhooks/{id}
Retrieves a webhook by ID.
# List adresses by webhook
get /v1/webhooks/{id}/addresses
List adresses by webhook.
# List webhooks
get /v1/webhooks
Lists webhooks for the user.
# Remove addresses from webhook
delete /v1/webhooks/{id}/addresses
Remove addresses from webhook.
# Update a webhook
patch /v1/webhooks/{id}
Updates an existing webhook.
# Code Blocks
Display inline code and code blocks
## Basic
### Inline Code
To denote a `word` or `phrase` as code, enclose it in backticks (\`).
```
To denote a `word` or `phrase` as code, enclose it in backticks (`).
```
### Code Block
Use [fenced code blocks](https://www.markdownguide.org/extended-syntax/#fenced-code-blocks) by enclosing code in three backticks and follow the leading ticks with the programming language of your snippet to get syntax highlighting. Optionally, you can also write the name of your code after the programming language.
```java HelloWorld.java
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
```
````md
```java HelloWorld.java
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
```
````
# Images and Embeds
Add image, video, and other HTML elements
## Image
### Using Markdown
The [markdown syntax](https://www.markdownguide.org/basic-syntax/#images) lets you add images using the following code
```md
![title](/path/image.jpg)
```
Note that the image file size must be less than 5MB. Otherwise, we recommend hosting on a service like [Cloudinary](https://cloudinary.com/) or [S3](https://aws.amazon.com/s3/). You can then use that URL and embed.
### Using Embeds
To get more customizability with images, you can also use [embeds](/writing-content/embed) to add images
```html
```
## Embeds and HTML elements
Mintlify supports [HTML tags in Markdown](https://www.markdownguide.org/basic-syntax/#html). This is helpful if you prefer HTML tags to Markdown syntax, and lets you create documentation with infinite flexibility.
### iFrames
Loads another HTML page within the document. Most commonly used for embedding videos.
```html
```
# Markdown Syntax
Text, title, and styling in standard markdown
## Titles
Best used for section headers.
```md
## Titles
```
### Subtitles
Best use to subsection headers.
```md
### Subtitles
```
Each **title** and **subtitle** creates an anchor and also shows up on the table of contents on the right.
## Text Formatting
We support most markdown formatting. Simply add `**`, `_`, or `~` around text to format it.
| Style | How to write it | Result |
| ------------- | ----------------- | ----------------- |
| Bold | `**bold**` | **bold** |
| Italic | `_italic_` | *italic* |
| Strikethrough | `~strikethrough~` | ~~strikethrough~~ |
You can combine these. For example, write `**_bold and italic_**` to get ***bold and italic*** text.
You need to use HTML to write superscript and subscript text. That is, add `` or `` around your text.
| Text Size | How to write it | Result |
| ----------- | ------------------------ | ---------------------- |
| Superscript | `superscript` | superscript |
| Subscript | `subscript` | subscript |
## Linking to Pages
You can add a link by wrapping text in `[]()`. You would write `[link to google](https://google.com)` to [link to google](https://google.com).
Links to pages in your docs need to be root-relative. Basically, you should include the entire folder path. For example, `[link to text](/writing-content/text)` links to the page "Text" in our components section.
Relative links like `[link to text](../text)` will open slower because we cannot optimize them as easily.
## Blockquotes
### Singleline
To create a blockquote, add a `>` in front of a paragraph.
> Dorothy followed her through many of the beautiful rooms in her castle.
```md
> Dorothy followed her through many of the beautiful rooms in her castle.
```
### Multiline
> Dorothy followed her through many of the beautiful rooms in her castle.
>
> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood.
```md
> Dorothy followed her through many of the beautiful rooms in her castle.
>
> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood.
```
### LaTeX
Mintlify supports [LaTeX](https://www.latex-project.org) through the Latex component.
8 x (vk x H1 - H2) = (0,1)
```md
8 x (vk x H1 - H2) = (0,1)
```
# Navigation
The navigation field in mint.json defines the pages that go in the navigation menu
The navigation menu is the list of links on every website.
You will likely update `mint.json` every time you add a new page. Pages do not show up automatically.
## Navigation syntax
Our navigation syntax is recursive which means you can make nested navigation groups. You don't need to include `.mdx` in page names.
```json Regular Navigation
"navigation": [
{
"group": "Getting Started",
"pages": ["quickstart"]
}
]
```
```json Nested Navigation
"navigation": [
{
"group": "Getting Started",
"pages": [
"quickstart",
{
"group": "Nested Reference Pages",
"pages": ["nested-reference-page"]
}
]
}
]
```
## Folders
Simply put your MDX files in folders and update the paths in `mint.json`.
For example, to have a page at `https://yoursite.com/your-folder/your-page` you would make a folder called `your-folder` containing an MDX file called `your-page.mdx`.
You cannot use `api` for the name of a folder unless you nest it inside another folder. Mintlify uses Next.js which reserves the top-level `api` folder for internal server calls. A folder name such as `api-reference` would be accepted.
```json Navigation With Folder
"navigation": [
{
"group": "Group Name",
"pages": ["your-folder/your-page"]
}
]
```
## Hidden Pages
MDX files not included in `mint.json` will not show up in the sidebar but are accessible through the search bar and by linking directly to them.
# Reusable Snippets
Reusable, custom snippets to keep content in sync
One of the core principles of software development is DRY (Don't Repeat
Yourself). This is a principle that apply to documentation as
well. If you find yourself repeating the same content in multiple places, you
should consider creating a custom snippet to keep your content in sync.
## Creating a custom snippet
**Pre-condition**: You must create your snippet file in the `snippets` directory.
Any page in the `snippets` directory will be treated as a snippet and will not
be rendered into a standalone page. If you want to create a standalone page
from the snippet, import the snippet into another file and call it as a
component.
### Default export
1. Add content to your snippet file that you want to re-use across multiple
locations. Optionally, you can add variables that can be filled in via props
when you import the snippet.
```mdx snippets/my-snippet.mdx
Hello world! This is my content I want to reuse across pages. My keyword of the
day is {word}.
```
The content that you want to reuse must be inside the `snippets` directory in
order for the import to work.
2. Import the snippet into your destination file.
```mdx destination-file.mdx
---
title: My title
description: My Description
---
import MySnippet from '/snippets/path/to/my-snippet.mdx';
## Header
Lorem impsum dolor sit amet.
```
### Reusable variables
1. Export a variable from your snippet file:
```mdx snippets/path/to/custom-variables.mdx
export const myName = 'my name';
export const myObject = { fruit: 'strawberries' };
```
2. Import the snippet from your destination file and use the variable:
```mdx destination-file.mdx
---
title: My title
description: My Description
---
import { myName, myObject } from '/snippets/path/to/custom-variables.mdx';
Hello, my name is {myName} and I like {myObject.fruit}.
```
### Reusable components
1. Inside your snippet file, create a component that takes in props by exporting
your component in the form of an arrow function.
```mdx snippets/custom-component.mdx
export const MyComponent = ({ title }) => (
{title}
... snippet content ...
);
```
MDX does not compile inside the body of an arrow function. Stick to HTML
syntax when you can or use a default export if you need to use MDX.
2. Import the snippet into your destination file and pass in the props
```mdx destination-file.mdx
---
title: My title
description: My Description
---
import { MyComponent } from '/snippets/custom-component.mdx';
Lorem ipsum dolor sit amet.
```
# Global Settings
Mintlify gives you complete control over the look and feel of your documentation using the mint.json file
Every Mintlify site needs a `mint.json` file with the core configuration settings. Learn more about the [properties](#properties) below.
## Properties
Name of your project. Used for the global title.
Example: `mintlify`
An array of groups with all the pages within that group
The name of the group.
Example: `Settings`
The relative paths to the markdown files that will serve as pages.
Example: `["customization", "page"]`
Path to logo image or object with path to "light" and "dark" mode logo images
Path to the logo in light mode
Path to the logo in dark mode
Where clicking on the logo links you to
Path to the favicon image
Hex color codes for your global theme
The primary color. Used for most often for highlighted content, section
headers, accents, in light mode
The primary color for dark mode. Used for most often for highlighted
content, section headers, accents, in dark mode
The primary color for important buttons
The color of the background in both light and dark mode
The hex color code of the background in light mode
The hex color code of the background in dark mode
Array of `name`s and `url`s of links you want to include in the topbar
The name of the button.
Example: `Contact us`
The url once you click on the button. Example: `https://mintlify.com/contact`
Link shows a button. GitHub shows the repo information at the url provided including the number of GitHub stars.
If `link`: What the button links to.
If `github`: Link to the repository to load GitHub information from.
Text inside the button. Only required if `type` is a `link`.
Array of version names. Only use this if you want to show different versions
of docs with a dropdown in the navigation bar.
An array of the anchors, includes the `icon`, `color`, and `url`.
The [Font Awesome](https://fontawesome.com/search?s=brands%2Cduotone) icon used to feature the anchor.
Example: `comments`
The name of the anchor label.
Example: `Community`
The start of the URL that marks what pages go in the anchor. Generally, this is the name of the folder you put your pages in.
The hex color of the anchor icon background. Can also be a gradient if you pass an object with the properties `from` and `to` that are each a hex color.
Used if you want to hide an anchor until the correct docs version is selected.
Pass `true` if you want to hide the anchor until you directly link someone to docs inside it.
One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin"
Override the default configurations for the top-most anchor.
The name of the top-most anchor
Font Awesome icon.
One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin"
An array of navigational tabs.
The name of the tab label.
The start of the URL that marks what pages go in the tab. Generally, this
is the name of the folder you put your pages in.
Configuration for API settings. Learn more about API pages at [API Components](/api-playground/demo).
The base url for all API endpoints. If `baseUrl` is an array, it will enable for multiple base url
options that the user can toggle.
The authentication strategy used for all API endpoints.
The name of the authentication parameter used in the API playground.
If method is `basic`, the format should be `[usernameName]:[passwordName]`
The default value that's designed to be a prefix for the authentication input field.
E.g. If an `inputPrefix` of `AuthKey` would inherit the default input result of the authentication field as `AuthKey`.
Configurations for the API playground
Whether the playground is showing, hidden, or only displaying the endpoint with no added user interactivity `simple`
Learn more at the [playground guides](/api-playground/demo)
Enabling this flag ensures that key ordering in OpenAPI pages matches the key ordering defined in the OpenAPI file.
This behavior will soon be enabled by default, at which point this field will be deprecated.
A string or an array of strings of URL(s) or relative path(s) pointing to your
OpenAPI file.
Examples:
```json Absolute
"openapi": "https://example.com/openapi.json"
```
```json Relative
"openapi": "/openapi.json"
```
```json Multiple
"openapi": ["https://example.com/openapi1.json", "/openapi2.json", "/openapi3.json"]
```
An object of social media accounts where the key:property pair represents the social media platform and the account url.
Example:
```json
{
"x": "https://x.com/mintlify",
"website": "https://mintlify.com"
}
```
One of the following values `website`, `facebook`, `x`, `discord`, `slack`, `github`, `linkedin`, `instagram`, `hacker-news`
Example: `x`
The URL to the social platform.
Example: `https://x.com/mintlify`
Configurations to enable feedback buttons
Enables a button to allow users to suggest edits via pull requests
Enables a button to allow users to raise an issue about the documentation
Customize the dark mode toggle.
Set if you always want to show light or dark mode for new users. When not
set, we default to the same mode as the user's operating system.
Set to true to hide the dark/light mode toggle. You can combine `isHidden` with `default` to force your docs to only use light or dark mode. For example:
```json Only Dark Mode
"modeToggle": {
"default": "dark",
"isHidden": true
}
```
```json Only Light Mode
"modeToggle": {
"default": "light",
"isHidden": true
}
```
A background image to be displayed behind every page. See example with
[Infisical](https://infisical.com/docs) and [FRPC](https://frpc.io).
# Overview
### What is the Glacier Webhooks?
With Glacier Webhooks, you can monitor real-time events on the Avalanche C-chain and L1s. For example, you can monitor smart contract events, track NFT transfers, and observe wallet-to-wallet transactions.
![webhooks](https://mintlify.s3.us-west-1.amazonaws.com/avalabs-47ea3976/images/webhooks.png)
### Key Features:
* **Real-time notifications**: Receive immediate updates on specified on-chain activities without polling.
* **Customizable**: Specify the desired event type to listen for, customizing notifications according to individual requirements.
* **Secure**: Employ shared secrets and signature-based verification to guarantee that notifications originate from a trusted source.
* **Broad Coverage**: Support for C-chain mainnet, testnet, and L1s within the Avalanche ecosystem, ensuring wide-ranging monitoring capabilities.
### Use cases
* **NFT Marketplace Transactions**: Get alerts for NFT minting, transfers, auctions, bids, sales, and other interactions within NFT marketplaces.
* **Wallet Notifications**: Receive alerts when an address performs actions such as sending, receiving, swapping, or burning assets.
* **DeFi Activities**: Receive notifications for various DeFi activities such as liquidity provisioning, yield farming, borrowing, lending, and liquidations.
### Supported L1s
Webhooks are currently enabled for the following L1s. Additional L1s will be added in the future:
**Mainnet**
| L1 | Chain ID |
| -------- | -------- |
| Beam | 4337 |
| DFK | 53935 |
| Dexalot | 432204 |
| Shrapnel | 2044 |
| Testnet | Fuji |
**Testnet**
| L1 | Chain ID |
| -------- | -------- |
| Pulsar | 431234 |
| Beam | 13337 |
| Dexalot | 432201 |
| DFK | 335 |
| WAGMI | 11111 |
| Shrapnel | 2038 |
### What are Webhooks?
A webhook is a communication mechanism to provide applications with real-time information. It delivers data to other applications as it happens, meaning you get data immediately, unlike typical APIs where you would need to poll for data to get it in "real-time". This makes webhooks much more efficient for both providers and consumers. Webhooks work by registering a URL to send notifications once certain events occur.
You can create receiver endpoints in your server in any programming language and those will have an associated URL (e.g. [https://myserver.com/callback](https://myserver.com/callback)). This object contains all the relevant information about what just happened, including the type of event and the data associated with that event.
**Webhooks vs. WebSockets:**\
The difference between webhooks and WebSockets is that webhooks can only facilitate one-way communication between two services. In contrast, WebSockets can facilitate two-way communication between a user and a service, recognizing events and displaying them to the user as they occur.
### Event structure
The Event structure always begins with the following parameters:
```json
{
"webhookId": "6d1bd383-aa8d-47b5-b793-da6d8a115fde",
"eventType": "address_activity",
"messageId": "8e4e7284-852a-478b-b425-27631c8d22d2",
"event": {
}
}
```
**Parameters:**
* `webhookId`: Unique identifier for the webhook in your account.
* `eventType`: The event that caused the webhook to be triggered. In the future, there will be multiple types of events, for the time being only the address\_activity event is supported. The address\_activity event gets triggered whenever the specified addresses participate in a token or AVAX transaction.
* `messageId`: Unique identifier per event sent.
* `event`: Event payload. It contains details about the transaction, logs, and traces. By default logs and internal transactions are not included, if you want to include them use `"includeLogs": true`, and `"includeInternalTxs": true`.
### Address Activity webhook
The address activity webhook allows you to track any interaction with an address (any address). Here is an example of this type of event:
```json
{
"webhookId": "263942d1-74a4-4416-aeb4-948b9b9bb7cc",
"eventType": "address_activity",
"messageId": "94df1881-5d93-49d1-a1bd-607830608de2",
"event": {
"transaction": {
"blockHash": "0xbd093536009f7dd785e9a5151d80069a93cc322f8b2df63d373865af4f6ee5be",
"blockNumber": "44568834",
"from": "0xf73166f0c75a3DF444fAbdFDC7e5EE4a73fA51C7",
"gas": "651108",
"gasPrice": "31466275484",
"maxFeePerGas": "31466275484",
"maxPriorityFeePerGas": "31466275484",
"txHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4",
"txStatus": "1",
"input": "0xb80c2f090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e000000000000000000000000000000000000000000000000006ca0c737b131f2000000000000000000000000000000000000000000000000000000000011554e000000000000000000000000000000000000000000000000000000006627dadc0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000004600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000006ca0c737b131f2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c70000000000000000000000000000000000000000000000000000000000000001000000000000000000000000be882fb094143b59dc5335d32cecb711570ebdd40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000be882fb094143b59dc5335d32cecb711570ebdd400000000000000000000000000000000000000000000000000000000000000010000000000000000000027100e663593657b064e1bae76d28625df5d0ebd44210000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c7000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e0000000000000000000000000000000000000000000000000000000000000bb80000000000000000000000000000000000000000000000000000000000000000",
"nonce": "4",
"to": "0x1dac23e41fc8ce857e86fd8c1ae5b6121c67d96d",
"transactionIndex": 0,
"value": "30576074978046450",
"type": 0,
"chainId": "43114",
"receiptCumulativeGasUsed": "212125",
"receiptGasUsed": "212125",
"receiptEffectiveGasPrice": "31466275484",
"receiptRoot": "0xf355b81f3e76392e1b4926429d6abf8ec24601cc3d36d0916de3113aa80dd674",
"erc20Transfers": [
{
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4",
"type": "ERC20",
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"value": "30576074978046450",
"blockTimestamp": 1713884373,
"logIndex": 2,
"erc20Token": {
"address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"name": "Wrapped AVAX",
"symbol": "WAVAX",
"decimals": 18,
"valueWithDecimals": "0.030576074978046448"
}
},
{
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4",
"type": "ERC20",
"from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"to": "0xf73166f0c75a3DF444fAbdFDC7e5EE4a73fA51C7",
"value": "1195737",
"blockTimestamp": 1713884373,
"logIndex": 3,
"erc20Token": {
"address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"name": "USD Coin",
"symbol": "USDC",
"decimals": 6,
"valueWithDecimals": "1.195737"
}
},
{
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4",
"type": "ERC20",
"from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"to": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"value": "30576074978046450",
"blockTimestamp": 1713884373,
"logIndex": 4,
"erc20Token": {
"address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"name": "Wrapped AVAX",
"symbol": "WAVAX",
"decimals": 18,
"valueWithDecimals": "0.030576074978046448"
}
}
],
"erc721Transfers": [],
"erc1155Transfers": [],
"internalTransactions": [
{
"from": "0xf73166f0c75a3DF444fAbdFDC7e5EE4a73fA51C7",
"to": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"internalTxType": "CALL",
"value": "30576074978046450",
"gasUsed": "212125",
"gasLimit": "651108",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xF2781Bb34B6f6Bb9a6B5349b24de91487E653119",
"internalTxType": "DELEGATECALL",
"value": "30576074978046450",
"gasUsed": "176417",
"gasLimit": "605825",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "9750",
"gasLimit": "585767",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6",
"internalTxType": "DELEGATECALL",
"value": "0",
"gasUsed": "2553",
"gasLimit": "569571",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "CALL",
"value": "30576074978046450",
"gasUsed": "23878",
"gasLimit": "566542",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "25116",
"gasLimit": "540114",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "81496",
"gasLimit": "511279",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "491",
"gasLimit": "501085",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"to": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "74900",
"gasLimit": "497032",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "32063",
"gasLimit": "463431",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6",
"internalTxType": "DELEGATECALL",
"value": "0",
"gasUsed": "31363",
"gasLimit": "455542",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "2491",
"gasLimit": "430998",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"to": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "7591",
"gasLimit": "427775",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "6016",
"gasLimit": "419746",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "491",
"gasLimit": "419670",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "3250",
"gasLimit": "430493",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6",
"internalTxType": "DELEGATECALL",
"value": "0",
"gasUsed": "2553",
"gasLimit": "423121",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "1250",
"gasLimit": "426766",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6",
"internalTxType": "DELEGATECALL",
"value": "0",
"gasUsed": "553",
"gasLimit": "419453",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
}
],
"blockTimestamp": 1713884373
}
}
}
```
In the free plan webhooks are automatically disabled after delivering 10 consecutive unsuccessful events.
# AvaCloud Developer Documentation
export function openSearch() {
document.getElementById("search-bar-entry").click();
}
AvaCloud Developers Hub
Accelerate your development across 100+ Layer 1 blockchains with AvaCloud’s SDKs, APIs, guides, and tutorials.
Retrieve data from multiple L1s with a few lines of code.
Data lake-powered service providing aggregated metrics for dApps.
Monitor real-time events on the Avalanche C-Chain and L1s.
The Avalanche SDK offers all APIs, allowing developers to build and
scale dApps with just a few lines of code.
# Create actor token
post /actor_tokens
Create an actor token that can be used to impersonate the given user.
The `actor` parameter needs to include at least a "sub" key whose value is the ID of the actor (impersonating) user.
# Revoke actor token
post /actor_tokens/{actor_token_id}/revoke
Revokes a pending actor token.
# Add identifier to the allow-list
post /allowlist_identifiers
Create an identifier allowed to sign up to an instance
# Add identifier to the block-list
post /blocklist_identifiers
Create an identifier that is blocked from accessing an instance
# Delete identifier from allow-list
delete /allowlist_identifiers/{identifier_id}
Delete an identifier from the instance allow-list
# Delete identifier from block-list
delete /blocklist_identifiers/{identifier_id}
Delete an identifier from the instance block-list
# List all identifiers on the allow-list
get /allowlist_identifiers
Get a list of all identifiers allowed to sign up to an instance
# List all identifiers on the block-list
get /blocklist_identifiers
Get a list of all identifiers which are not allowed to access an instance
# Update instance settings
patch /beta_features/instance_settings
Updates the settings of an instance
# Update production instance domain
put /beta_features/domain
Change the domain of a production instance.
Changing the domain requires updating the [DNS records](https://clerk.com/docs/deployments/overview#dns-records) accordingly, deploying new [SSL certificates](https://clerk.com/docs/deployments/overview#deploy), updating your Social Connection's redirect URLs and setting the new keys in your code.
WARNING: Changing your domain will invalidate all current user sessions (i.e. users will be logged out). Also, while your application is being deployed, a small downtime is expected to occur.
# Update production instance domain
post /instance/change_domain
Change the domain of a production instance.
Changing the domain requires updating the [DNS records](https://clerk.com/docs/deployments/overview#dns-records) accordingly, deploying new [SSL certificates](https://clerk.com/docs/deployments/overview#deploy), updating your Social Connection's redirect URLs and setting the new keys in your code.
WARNING: Changing your domain will invalidate all current user sessions (i.e. users will be logged out). Also, while your application is being deployed, a small downtime is expected to occur.
# Get metrics for EVM chains
get /v2/chains/{chainId}/metrics/{metric}
Gets metrics for an EVM chain over a specified time interval aggregated at the specified time-interval granularity.
# Get rolling window metrics for EVM chains
get /v2/chains/{chainId}/rollingWindowMetrics/{metric}
Gets the rolling window metrics for an EVM chain for the last hour, day, month, year, and all time.
# Get staking metrics for a given subnet
get /v2/networks/{network}/metrics/{metric}
Gets staking metrics for a given subnet.
# Get teleporter metrics for EVM chains
get /v2/chains/{chainId}/teleporterMetrics/{metric}
Gets teleporter metrics for an EVM chain.
# Get a client
get /clients/{client_id}
Returns the details of a client.
# List all clients
get /clients
Returns a list of all clients. The clients are returned sorted by creation date,
with the newest clients appearing first.
Warning: the endpoint is being deprecated and will be removed in future versions.
# Verify a client
post /clients/verify
Verifies the client in the provided token
# Add a domain
post /domains
Add a new domain for your instance.
Useful in the case of multi-domain instances, allows adding satellite domains to an instance.
The new domain must have a `name`. The domain name can contain the port for development instances, like `localhost:3000`.
At the moment, instances can have only one primary domain, so the `is_satellite` parameter must be set to `true`.
If you're planning to configure the new satellite domain to run behind a proxy, pass the `proxy_url` parameter accordingly.
# Delete a satellite domain
delete /domains/{domain_id}
Deletes a satellite domain for the instance.
It is currently not possible to delete the instance's primary domain.
# List all instance domains
get /domains
Use this endpoint to get a list of all domains for an instance.
The response will contain the primary domain for the instance and any satellite domains. Each domain in the response contains information about the URLs where Clerk operates and the required CNAME targets.
# Update a domain
patch /domains/{domain_id}
The `proxy_url` can be updated only for production instances.
Update one of the instance's domains. Both primary and satellite domains can be updated.
If you choose to use Clerk via proxy, use this endpoint to specify the `proxy_url`.
Whenever you decide you'd rather switch to DNS setup for Clerk, simply set `proxy_url`
to `null` for the domain. When you update a production instance's primary domain name,
you have to make sure that you've completed all the necessary setup steps for DNS and
emails to work. Expect downtime otherwise. Updating a primary domain's name will also
update the instance's home origin, affecting the default application paths.
# List all templates
get /templates/{template_type}
Returns a list of all templates.
The templates are returned sorted by position.
# Preview changes to a template
post /templates/{template_type}/{slug}/preview
Returns a preview of a template for a given template_type, slug and body
# Retrieve a template
get /templates/{template_type}/{slug}
Returns the details of a template
# Revert a template
post /templates/{template_type}/{slug}/revert
Reverts an updated template to its default state
# Toggle the delivery by Clerk for a template of a given type and slug
post /templates/{template_type}/{slug}/toggle_delivery
Toggles the delivery by Clerk for a template of a given type and slug.
If disabled, Clerk will not deliver the resulting email or SMS.
The app developer will need to listen to the `email.created` or `sms.created` webhooks in order to handle delivery themselves.
# Update a template for a given type and slug
put /templates/{template_type}/{slug}
Updates the existing template of the given type and slug
# Create an email address
post /email_addresses
Create a new email address
# Delete an email address
delete /email_addresses/{email_address_id}
Delete the email address with the given ID
# Retrieve an email address
get /email_addresses/{email_address_id}
Returns the details of an email address.
# Update an email address
patch /email_addresses/{email_address_id}
Updates an email address.
# Get a list of supported blockchains
get /v2/chains
Get a list of Metrics API supported blockchains.
# Get chain information for supported blockchain
get /v2/chains/{chainId}
Get chain information for Metrics API supported blockchain.
# Getting Started
The Metrics API is designed to be simple and accessible, requiring no authentication to get started. Just choose your endpoint, make your query, and instantly access on-chain data and analytics to power your applications.
The following query retrieves the daily count of active addresses on the Avalanche C-Chain(43114) over the course of one month (from August 1, 2024 12:00:00 AM to August 31, 2024 12:00:00 AM), providing insights into user activity on the chain for each day during that period. With this data you can use JavaScript visualization tools like Chart.js, D3.js, Highcharts, Plotly.js, or Recharts to create interactive and insightful visual representations.
```bash
curl --request GET \
--url 'https://popsicle-api.avax.network/v2/chains/43114/metrics/activeAddresses?startTimestamp=1722470400&endTimestamp=1725062400&timeInterval=day&pageSize=31'
```
Response:
```json
{
"results": [
{
"value": 37738,
"timestamp": 1724976000
},
{
"value": 53934,
"timestamp": 1724889600
},
{
"value": 58992,
"timestamp": 1724803200
},
{
"value": 73792,
"timestamp": 1724716800
},
{
"value": 70057,
"timestamp": 1724630400
},
{
"value": 46452,
"timestamp": 1724544000
},
{
"value": 46323,
"timestamp": 1724457600
},
{
"value": 73399,
"timestamp": 1724371200
},
{
"value": 52661,
"timestamp": 1724284800
},
{
"value": 52497,
"timestamp": 1724198400
},
{
"value": 50574,
"timestamp": 1724112000
},
{
"value": 46999,
"timestamp": 1724025600
},
{
"value": 45320,
"timestamp": 1723939200
},
{
"value": 54964,
"timestamp": 1723852800
},
{
"value": 60251,
"timestamp": 1723766400
},
{
"value": 48493,
"timestamp": 1723680000
},
{
"value": 71091,
"timestamp": 1723593600
},
{
"value": 50456,
"timestamp": 1723507200
},
{
"value": 46989,
"timestamp": 1723420800
},
{
"value": 50984,
"timestamp": 1723334400
},
{
"value": 46988,
"timestamp": 1723248000
},
{
"value": 66943,
"timestamp": 1723161600
},
{
"value": 64209,
"timestamp": 1723075200
},
{
"value": 57478,
"timestamp": 1722988800
},
{
"value": 80553,
"timestamp": 1722902400
},
{
"value": 70472,
"timestamp": 1722816000
},
{
"value": 53678,
"timestamp": 1722729600
},
{
"value": 70818,
"timestamp": 1722643200
},
{
"value": 99842,
"timestamp": 1722556800
},
{
"value": 76515,
"timestamp": 1722470400
}
]
}
```
Congratulations! You’ve successfully made your first query to the Metrics API. 🚀🚀🚀
# Get the health of the service
get /v2/health-check
# Update instance organization settings
patch /instance/organization_settings
Updates the organization settings of the instance
# Update instance restrictions
patch /instance/restrictions
Updates the restriction settings of an instance
# Update instance settings
patch /instance
Updates the settings of an instance
# Create an invitation
post /invitations
Creates a new invitation for the given email address and sends the invitation email.
Keep in mind that you cannot create an invitation if there is already one for the given email address.
Also, trying to create an invitation for an email address that already exists in your application will result to an error.
# List all invitations
get /invitations
Returns all non-revoked invitations for your application, sorted by creation date
# Revokes an invitation
post /invitations/{invitation_id}/revoke
Revokes the given invitation.
Revoking an invitation will prevent the user from using the invitation link that was sent to them.
However, it doesn't prevent the user from signing up if they follow the sign up flow.
Only active (i.e. non-revoked) invitations can be revoked.
# Retrieve the JSON Web Key Set of the instance
get /jwks
Retrieve the JSON Web Key Set of the instance
# Create a JWT template
post /jwt_templates
Create a new JWT template
# Delete a Template
delete /jwt_templates/{template_id}
# List all templates
get /jwt_templates
# Retrieve a template
get /jwt_templates/{template_id}
Retrieve the details of a given JWT template
# Update a JWT template
patch /jwt_templates/{template_id}
Updates an existing JWT template
# Get addresses by balance over time
get /v2/chains/{chainId}/contracts/{address}/balances
Get list of addresses and their latest balances that have held more than a certain threshold of a given token during the specified time frame.
# Get addresses by BTCb bridged balance
get /v2/chains/43114/btcb/bridged:getAddresses
Get list of addresses and their net bridged amounts that have bridged more than a certain threshold.
# Get addresses running validators during a given time frame
get /v2/subnets/{subnetId}/validators:getAddresses
Get list of addresses and AddValidatorTx timestamps set to receive awards for validation periods during the specified time frame.
# Returns the markup for the interstitial page
get /public/interstitial
The Clerk interstitial endpoint serves an html page that loads clerk.js in order to check the user's authentication state.
It is used by Clerk SDKs when the user's authentication state cannot be immediately determined.
# Create an OAuth application
post /oauth_applications
Creates a new OAuth application with the given name and callback URL for an instance.
The callback URL must be a valid url.
All URL schemes are allowed such as `http://`, `https://`, `myapp://`, etc...
# Delete an OAuth application
delete /oauth_applications/{oauth_application_id}
Deletes the given OAuth application.
This is not reversible.
# Get a list of OAuth applications for an instance
get /oauth_applications
This request returns the list of OAuth applications for an instance.
Results can be paginated using the optional `limit` and `offset` query parameters.
The OAuth applications are ordered by descending creation date.
Most recent OAuth applications will be returned first.
# Retrieve an OAuth application by ID
get /oauth_applications/{oauth_application_id}
Fetches the OAuth application whose ID matches the provided `id` in the path.
# Rotate the client secret of the given OAuth application
post /oauth_applications/{oauth_application_id}/rotate_secret
Rotates the OAuth application's client secret.
When the client secret is rotated, make sure to update it in authorized OAuth clients.
# Update an OAuth application
patch /oauth_applications/{oauth_application_id}
Updates an existing OAuth application
# Bulk create and send organization invitations
post /organizations/{organization_id}/invitations/bulk
Creates new organization invitations in bulk and sends out emails to the provided email addresses with a link to accept the invitation and join the organization.
You can specify a different `role` for each invited organization member.
New organization invitations get a "pending" status until they are revoked by an organization administrator or accepted by the invitee.
The request body supports passing an optional `redirect_url` parameter for each invitation.
When the invited user clicks the link to accept the invitation, they will be redirected to the provided URL.
Use this parameter to implement a custom invitation acceptance flow.
You must specify the ID of the user that will send the invitation with the `inviter_user_id` parameter. Each invitation
can have a different inviter user.
Inviter users must be members with administrator privileges in the organization.
Only "admin" members can create organization invitations.
You can optionally provide public and private metadata for each organization invitation. The public metadata are visible
by both the Frontend and the Backend, whereas the private metadata are only visible by the Backend.
When the organization invitation is accepted, the metadata will be transferred to the newly created organization membership.
# Create and send an organization invitation
post /organizations/{organization_id}/invitations
Creates a new organization invitation and sends an email to the provided `email_address` with a link to accept the invitation and join the organization.
You can specify the `role` for the invited organization member.
New organization invitations get a "pending" status until they are revoked by an organization administrator or accepted by the invitee.
The request body supports passing an optional `redirect_url` parameter.
When the invited user clicks the link to accept the invitation, they will be redirected to the URL provided.
Use this parameter to implement a custom invitation acceptance flow.
You must specify the ID of the user that will send the invitation with the `inviter_user_id` parameter.
That user must be a member with administrator privileges in the organization.
Only "admin" members can create organization invitations.
You can optionally provide public and private metadata for the organization invitation.
The public metadata are visible by both the Frontend and the Backend whereas the private ones only by the Backend.
When the organization invitation is accepted, the metadata will be transferred to the newly created organization membership.
# Get a list of organization invitations
get /organizations/{organization_id}/invitations
This request returns the list of organization invitations.
Results can be paginated using the optional `limit` and `offset` query parameters.
You can filter them by providing the 'status' query parameter, that accepts multiple values.
The organization invitations are ordered by descending creation date.
Most recent invitations will be returned first.
Any invitations created as a result of an Organization Domain are not included in the results.
# Get a list of pending organization invitations
get /organizations/{organization_id}/invitations/pending
This request returns the list of organization invitations with "pending" status.
These are the organization invitations that can still be used to join the organization, but have not been accepted by the invited user yet.
Results can be paginated using the optional `limit` and `offset` query parameters.
The organization invitations are ordered by descending creation date.
Most recent invitations will be returned first.
Any invitations created as a result of an Organization Domain are not included in the results.
# Retrieve an organization invitation by ID
get /organizations/{organization_id}/invitations/{invitation_id}
Use this request to get an existing organization invitation by ID.
# Revoke a pending organization invitation
post /organizations/{organization_id}/invitations/{invitation_id}/revoke
Use this request to revoke a previously issued organization invitation.
Revoking an organization invitation makes it invalid; the invited user will no longer be able to join the organization with the revoked invitation.
Only organization invitations with "pending" status can be revoked.
The request needs the `requesting_user_id` parameter to specify the user which revokes the invitation.
Only users with "admin" role can revoke invitations.
# Create a new organization membership
post /organizations/{organization_id}/memberships
Adds a user as a member to the given organization.
Only users in the same instance as the organization can be added as members.
This organization will be the user's [active organization] (https://clerk.com/docs/organizations/overview#active-organization)
the next time they create a session, presuming they don't explicitly set a
different organization as active before then.
# Get a list of all members of an organization
get /organizations/{organization_id}/memberships
Retrieves all user memberships for the given organization
# Merge and update organization membership metadata
patch /organizations/{organization_id}/memberships/{user_id}/metadata
Update an organization membership's metadata attributes by merging existing values with the provided parameters.
Metadata values will be updated via a deep merge. Deep means that any nested JSON objects will be merged as well.
You can remove metadata keys at any level by setting their value to `null`.
# Remove a member from an organization
delete /organizations/{organization_id}/memberships/{user_id}
Removes the given membership from the organization
# Update an organization membership
patch /organizations/{organization_id}/memberships/{user_id}
Updates the properties of an existing organization membership
# Create an organization
post /organizations
Creates a new organization with the given name for an instance.
In order to successfully create an organization you need to provide the ID of the User who will become the organization administrator.
You can specify an optional slug for the new organization.
If provided, the organization slug can contain only lowercase alphanumeric characters (letters and digits) and the dash "-".
Organization slugs must be unique for the instance.
You can provide additional metadata for the organization and set any custom attribute you want.
Organizations support private and public metadata.
Private metadata can only be accessed from the Backend API.
Public metadata can be accessed from the Backend API, and are read-only from the Frontend API.
The `created_by` user will see this as their [active organization] (https://clerk.com/docs/organizations/overview#active-organization)
the next time they create a session, presuming they don't explicitly set a different organization as active before then.
# Delete an organization
delete /organizations/{organization_id}
Deletes the given organization.
Please note that deleting an organization will also delete all memberships and invitations.
This is not reversible.
# Delete organizations logo
delete /organizations/{organization_id}/logo
Delete the organization's logo.
# Get a list of organizations for an instance
get /organizations
This request returns the list of organizations for an instance.
Results can be paginated using the optional `limit` and `offset` query parameters.
The organizations are ordered by descending creation date.
Most recent organizations will be returned first.
# Merge and update metadata for an organization
patch /organizations/{organization_id}/metadata
Update organization metadata attributes by merging existing values with the provided parameters.
Metadata values will be updated via a deep merge.
Deep meaning that any nested JSON objects will be merged as well.
You can remove metadata keys at any level by setting their value to `null`.
# Retrieve an organization by ID or slug
get /organizations/{organization_id}
Fetches the organization whose ID or slug matches the provided `id_or_slug` URL query parameter.
# Update an organization
patch /organizations/{organization_id}
Updates an existing organization
# Upload a logo for the organization
put /organizations/{organization_id}/logo
Set or replace an organization's logo, by uploading an image file.
This endpoint uses the `multipart/form-data` request content type and accepts a file of image type.
The file size cannot exceed 10MB.
Only the following file content types are supported: `image/jpeg`, `image/png`, `image/gif`, `image/webp`, `image/x-icon`, `image/vnd.microsoft.icon`.
# Overview
### What is the Metrics API?
The Metrics API equips web3 developers with a robust suite of tools to access and analyze on-chain activity across Avalanche’s primary network, Avalanche L1s, and other supported EVM chains. This API delivers comprehensive metrics and analytics, enabling you to seamlessly integrate historical data on transactions, gas consumption, throughput, staking, and more into your applications.
The Metrics API, along with the [Data API](/data-api) are the driving force behind every graph you see on the [Avalanche Explorer](https://subnets.avax.network/stats/). From transaction trends to staking insights, the visualizations and data presented are all powered by these APIs, offering real-time and historical insights that are essential for building sophisticated, data-driven blockchain products..
### Features
* **Chain Throughput:** Retrieve detailed metrics on gas consumption, Transactions Per Second (TPS), and gas prices, including rolling windows of data for granular analysis.
* **Cumulative Metrics:** Access cumulative data on addresses, contracts, deployers, and transaction counts, providing insights into network growth over time.
* **Staking Information:** Obtain staking-related data, including the number of validators and delegators, along with their respective weights, across different subnets.
* **Blockchains and Subnets:** Get information about supported blockchains, including EVM Chain IDs, blockchain IDs, and subnet associations, facilitating multi-chain analytics.
* **Composite Queries:** Perform advanced queries by combining different metric types and conditions, enabling detailed and customizable data retrieval.
The Metrics API is designed to provide developers with powerful tools to analyze and monitor on-chain activity across Avalanche’s primary network, Avalanche L1s, and other supported EVM chains. Below is an overview of the key features available:
### Chain Throughput Metrics
* **Gas Consumption**
Track the average and maximum gas consumption per second, helping to understand network performance and efficiency.
* **Transactions Per Second (TPS)**
Monitor the average and peak TPS to assess the network’s capacity and utilization.
* **Gas Prices**
Analyze average and maximum gas prices over time to optimize transaction costs and predict fee trends.
Monitor the average and peak TPS to assess the network’s capacity and utilization.
### Cumulative Metrics
* **Address Growth**
Access the cumulative number of active addresses on a chain, providing insights into network adoption and user activity.
* **Contract Deployment**
Monitor the cumulative number of smart contracts deployed, helping to gauge developer engagement and platform usage.
* **Transaction Count**
Track the cumulative number of transactions, offering a clear view of network activity and transaction volume.
### Staking Information
* **Validator and Delegator Counts**
Retrieve the number of active validators and delegators for a given L1, crucial for understanding network security and decentralization.
* **Staking Weights**
Access the total stake weight of validators and delegators, helping to assess the distribution of staked assets across the network.
### Rolling Window Analytics
* **Short-Term and Long-Term Metrics:** Perform rolling window analysis on various metrics like gas used, TPS, and gas prices, allowing for both short-term and long-term trend analysis.
* **Customizable Time Frames:** Choose from different time intervals (hourly, daily, monthly) to suit your specific analytical needs.
### Blockchain and L1 Information
* **Chain and L1 Mapping:** Get detailed information about EVM chains and their associated L1s, including chain IDs, blockchain IDs, and subnet IDs, facilitating cross-chain analytics.
### Advanced Composite Queries
* **Custom Metrics Combinations**: Combine multiple metrics and apply logical operators to perform sophisticated queries, enabling deep insights and tailored analytics.
* **Paginated Results:** Handle large datasets efficiently with paginated responses, ensuring seamless data retrieval in your applications.
The Metrics API equips developers with the tools needed to build robust analytics, monitoring, and reporting solutions, leveraging the full power of multi-chain data across the Avalanche ecosystem and beyond.
# Create a phone number
post /phone_numbers
Create a new phone number
# Delete a phone number
delete /phone_numbers/{phone_number_id}
Delete the phone number with the given ID
# Retrieve a phone number
get /phone_numbers/{phone_number_id}
Returns the details of a phone number
# Update a phone number
patch /phone_numbers/{phone_number_id}
Updates a phone number
# Verify the proxy configuration for your domain
post /proxy_checks
This endpoint can be used to validate that a proxy-enabled domain is operational.
It tries to verify that the proxy URL provided in the parameters maps to a functional proxy that can reach the Clerk Frontend API.
You can use this endpoint before you set a proxy URL for a domain. This way you can ensure that switching to proxy-based
configuration will not lead to downtime for your instance.
The `proxy_url` parameter allows for testing proxy configurations for domains that don't have a proxy URL yet, or operate on
a different proxy URL than the one provided. It can also be used to re-validate a domain that is already configured to work with a proxy.
# Rate Limits
Rate limiting is managed through a weighted scoring system, known as Compute Units (CUs). Each API request consumes a specified number of CUs, determined by the complexity of the request. This system is designed to accommodate basic requests while efficiently handling more computationally intensive operations.
## Rate Limit Tiers
The maximum CUs (rate-limiting score) for a user depends on their subscription level and is delineated in the following table:
| Subscription Level | Per Minute Limit (CUs) | Per Day Limit (CUs) |
| :----------------- | :--------------------- | :------------------ |
| Free | 8,000 | 1,200,000 |
> We are working on new subscription tiers with higher rate limits to support even greater request volumes.
## Rate Limit Categories
The CUs for each category are defined in the following table:
{/* metrics weights value table start */}
| Weight | CU Value |
| :----- | :------- |
| Free | 1 |
| Small | 20 |
| Medium | 100 |
| Large | 500 |
| XL | 1000 |
| XXL | 3000 |
{/* metrics weights value table end */}
## Rate Limits for Metrics Endpoints
The CUs for each route are defined in the table below:
{/* metrics execution weights table start */}
| Endpoint | Method | Weight | CU Value |
| :---------------------------------------------------------- | :----- | :----- | :------- |
| `/v2/health-check` | GET | Free | 1 |
| `/v2/chains` | GET | Free | 1 |
| `/v2/chains/{chainId}` | GET | Free | 1 |
| `/v2/chains/{chainId}/metrics/{metric}` | GET | Medium | 100 |
| `/v2/chains/{chainId}/teleporterMetrics/{metric}` | GET | Medium | 100 |
| `/v2/chains/{chainId}/rollingWindowMetrics/{metric}` | GET | Medium | 100 |
| `/v2/networks/{network}/metrics/{metric}` | GET | Medium | 100 |
| `/v2/chains/{chainId}/contracts/{address}/nfts:listHolders` | GET | Large | 500 |
| `/v2/chains/{chainId}/contracts/{address}/balances` | GET | XL | 1000 |
| `/v2/chains/43114/btcb/bridged:getAddresses` | GET | Large | 500 |
| `/v2/subnets/{subnetId}/validators:getAddresses` | GET | Large | 500 |
| `/v2/lookingGlass/compositeQuery` | POST | XXL | 3000 |
{/* metrics execution weights table end */}
All rate limits, weights, and CU values are subject to change.
# Create a redirect URL
post /redirect_urls
Create a redirect URL
# Delete a redirect URL
delete /redirect_urls/{id}
Remove the selected redirect URL from the whitelist of the instance
# List all redirect URLs
get /redirect_urls
Lists all whitelisted redirect_urls for the instance
# Retrieve a redirect URL
get /redirect_urls/{id}
Retrieve the details of the redirect URL with the given ID
# Create a SAML Connection
post /saml_connections
Create a new SAML Connection.
# Delete a SAML Connection
delete /saml_connections/{saml_connection_id}
Deletes the SAML Connection whose ID matches the provided `id` in the path.
# Get a list of SAML Connections for an instance
get /saml_connections
Returns the list of SAML Connections for an instance.
Results can be paginated using the optional `limit` and `offset` query parameters.
The SAML Connections are ordered by descending creation date and the most recent will be returned first.
# Retrieve a SAML Connection by ID
get /saml_connections/{saml_connection_id}
Fetches the SAML Connection whose ID matches the provided `saml_connection_id` in the path.
# Update a SAML Connection
patch /saml_connections/{saml_connection_id}
Updates the SAML Connection whose ID matches the provided `id` in the path.
# Create a session token from a jwt template
post /sessions/{session_id}/tokens/{template_name}
Creates a JSON Web Token(JWT) based on a session and a JWT Template name defined for your instance
# List all sessions
get /sessions
Returns a list of all sessions.
The sessions are returned sorted by creation date, with the newest sessions appearing first.
**Deprecation Notice (2024-01-01):** All parameters were initially considered optional, however
moving forward at least one of `client_id` or `user_id` parameters should be provided.
# Retrieve a session
get /sessions/{session_id}
Retrieve the details of a session
# Revoke a session
post /sessions/{session_id}/revoke
Sets the status of a session as "revoked", which is an unauthenticated state.
In multi-session mode, a revoked session will still be returned along with its client object, however the user will need to sign in again.
# Verify a session
post /sessions/{session_id}/verify
Returns the session if it is authenticated, otherwise returns an error.
WARNING: This endpoint is deprecated and will be removed in future versions. We strongly recommend switching to networkless verification using short-lived session tokens,
which is implemented transparently in all recent SDK versions (e.g. [NodeJS SDK](https://clerk.com/docs/backend-requests/handling/nodejs#clerk-express-require-auth)).
For more details on how networkless verification works, refer to our [Session Tokens documentation](https://clerk.com/docs/backend-requests/resources/session-tokens).
# Create sign-in token
post /sign_in_tokens
Creates a new sign-in token and associates it with the given user.
By default, sign-in tokens expire in 30 days.
You can optionally supply a different duration in seconds using the `expires_in_seconds` property.
# Revoke the given sign-in token
post /sign_in_tokens/{sign_in_token_id}/revoke
Revokes a pending sign-in token
# Update a sign-up
patch /sign_ups/{id}
Update the sign-up with the given ID
# Retrieve a new testing token
post /testing_tokens
Retrieve a new testing token. Only available for development instances.
# Usage Guide
The Metrics API does not require authentication, making it straightforward to integrate into your applications. You can start making API requests without the need for an API key or any authentication headers.
#### Making Requests
You can interact with the Metrics API by sending HTTP GET requests to the provided endpoints. Below is an example of a simple `curl` request.
```bash
curl -H "Content-Type: Application/json" "https://popsicle-api.avax.network/v1/avg_tps/{chainId}"
```
In the above request Replace `chainId` with the specific chain ID you want to query. For example, to retrieve the average transactions per second (TPS) for a specific chain (in this case, chain ID 43114), you can use the following endpoint:
```bash
curl "https://popsicle-api.avax.network/v1/avg_tps/43114"
```
The API will return a JSON response containing the average TPS for the specified chain over a series of timestamps and `lastRun` is a timestamp indicating when the last data point was updated:
```json
{
"results": [
{"timestamp": 1724716800, "value": 1.98},
{"timestamp": 1724630400, "value": 2.17},
{"timestamp": 1724544000, "value": 1.57},
{"timestamp": 1724457600, "value": 1.82},
// Additional data points...
],
"status": 200,
"lastRun": 1724780812
}
```
### Rate Limits
Even though the Metrics API does not require authentication, it still enforces rate limits to ensure stability and performance. If you exceed these limits, the server will respond with a 429 Too Many Requests HTTP response code.
### Error Types
The API generates standard error responses along with error codes based on provided requests and parameters.
Typically, response codes within the `2XX` range signifies successful requests, while those within the `4XX` range points to errors originating from the client's side. Meanwhile, response codes within the `5XX` range indicates problems on the server's side.
The error response body is formatted like this:
```json
{
"message": ["Invalid address format"], // route specific error message
"error": "Bad Request", // error type
"statusCode": 400 // http response code
}
```
Let's go through every error code that we can respond with:
| Error Code | Error Type | Description |
| ---------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **400** | Bad Request | Bad requests generally mean the client has passed invalid or malformed parameters. Error messages in the response could help in evaluating the error. |
| **401** | Unauthorized | When a client attempts to access resources that require authorization credentials but the client lacks proper authentication in the request, the server responds with 401. |
| **403** | Forbidden | When a client attempts to access resources with valid credentials but doesn't have the privilege to perform that action, the server responds with 403. |
| **404** | Not Found | The 404 error is mostly returned when the client requests with either mistyped URL, or the passed resource is moved or deleted, or the resource doesn't exist. |
| **500** | Internal Server Error | The 500 error is a generic server-side error that is returned for any uncaught and unexpected issues on the server side. This should be very rare, and you may reach out to us if the problem persists for a longer duration. |
| **502** | Bad Gateway | This is an internal error indicating invalid response received by the client-facing proxy or gateway from the upstream server. |
| **503** | Service Unavailable | The 503 error is returned for certain routes on a particular Subnet. This indicates an internal problem with our Subnet node, and may not necessarily mean the Subnet is down or affected. |
### Pagination
For endpoints that return large datasets, the Metrics API employs pagination to manage the results. When querying for lists of data, you may receive a nextPageToken in the response, which can be used to request the next page of data.
Example response with pagination:
```json
{
"results": [...],
"nextPageToken": "3d22deea-ea64-4d30-8a1e-c2a353b67e90"
}
```
To retrieve the next set of results, include the nextPageToken in your subsequent request:
```bash
curl -H "Content-Type: Application/json" \
"https://popsicle-api.avax.network/v1/avg_tps/{chainId}?pageToken=3d22deea-ea64-4d30-8a1e-c2a353b67e90"
```
### Pagination Details
#### Page Token Structure
The `nextPageToken` is a UUID-based token provided in the response when additional pages of data are available. This token serves as a pointer to the next set of data.
* **UUID Generation**: The `nextPageToken` is generated uniquely for each pagination scenario, ensuring security and ensuring predictability.
* **Expiration**: The token is valid for 24 hours from the time it is generated. After this period, the token will expire, and a new request starting from the initial page will be required.
* **Presence**: The token is only included in the response when there is additional data available. If no more data exists, the token will not be present.
#### Integration and Usage
To use the pagination system effectively:
* Check if the `nextPageToken` is present in the response.
* If present, include this token in the subsequent request to fetch the next page of results.
* Ensure that the follow-up request is made within the 24-hour window after the token was generated to avoid token expiration.
By utilizing the pagination mechanism, you can efficiently manage and navigate through large datasets, ensuring a smooth data retrieval process.
### Swagger API Reference
You can explore the full API definitions and interact with the endpoints in the Swagger documentation at:
[https://popsicle-api.avax.network/api](https://popsicle-api.avax.network/api)
# Ban a user
post /users/{user_id}/ban
Marks the given user as banned, which means that all their sessions are revoked and they are not allowed to sign in again.
# Count users
get /users/count
Returns a total count of all users that match the given filtering criteria.
# Create a new user
post /users
Creates a new user. Your user management settings determine how you should setup your user model.
Any email address and phone number created using this method will be marked as verified.
Note: If you are performing a migration, check out our guide on [zero downtime migrations](https://clerk.com/docs/deployments/migrate-overview).
A rate limit rule of 20 requests per 10 seconds is applied to this endpoint.
# Delete a user
delete /users/{user_id}
Delete the specified user
# Delete user profile image
delete /users/{user_id}/profile_image
Delete a user's profile image
# Disable a user's MFA methods
delete /users/{user_id}/mfa
Disable all of a user's MFA methods (e.g. OTP sent via SMS, TOTP on their authenticator app) at once.
# List all users
get /users
Returns a list of all users.
The users are returned sorted by creation date, with the newest users appearing first.
# Lock a user
post /users/{user_id}/lock
Marks the given user as locked, which means they are not allowed to sign in again until the lock expires.
Lock duration can be configured in the instance's restrictions settings.
# Merge and update a user's metadata
patch /users/{user_id}/metadata
Update a user's metadata attributes by merging existing values with the provided parameters.
This endpoint behaves differently than the *Update a user* endpoint.
Metadata values will not be replaced entirely.
Instead, a deep merge will be performed.
Deep means that any nested JSON objects will be merged as well.
You can remove metadata keys at any level by setting their value to `null`.
# Retrieve a user
get /users/{user_id}
Retrieve the details of a user
# Retrieve all memberships for a user
get /users/{user_id}/organization_memberships
Retrieve a paginated list of the user's organization memberships
# Retrieve the OAuth access token of a user
get /users/{user_id}/oauth_access_tokens/{provider}
Fetch the corresponding OAuth access token for a user that has previously authenticated with a particular OAuth provider.
For OAuth 2.0, if the access token has expired and we have a corresponding refresh token, the access token will be refreshed transparently the new one will be returned.
# Set user profile image
post /users/{user_id}/profile_image
Update a user's profile image
# Unban a user
post /users/{user_id}/unban
Removes the ban mark from the given user.
# Unlock a user
post /users/{user_id}/unlock
Removes the lock from the given user.
# Update a user
patch /users/{user_id}
Update a user's attributes.
You can set the user's primary contact identifiers (email address and phone numbers) by updating the `primary_email_address_id` and `primary_phone_number_id` attributes respectively.
Both IDs should correspond to verified identifications that belong to the user.
You can remove a user's username by setting the username attribute to null or the blank string "".
This is a destructive action; the identification will be deleted forever.
Usernames can be removed only if they are optional in your instance settings and there's at least one other identifier which can be used for authentication.
This endpoint allows changing a user's password. When passing the `password` parameter directly you have two further options.
You can ignore the password policy checks for your instance by setting the `skip_password_checks` parameter to `true`.
You can also choose to sign the user out of all their active sessions on any device once the password is updated. Just set `sign_out_of_other_sessions` to `true`.
# Verify a TOTP or backup code for a user
post /users/{user_id}/verify_totp
Verify that the provided TOTP or backup code is valid for the user.
Verifying a backup code will result it in being consumed (i.e. it will
become invalid).
Useful for custom auth flows and re-verification.
# Verify the password of a user
post /users/{user_id}/verify_password
Check that the user's password matches the supplied input.
Useful for custom auth flows and re-verification.
# Create a Svix app
post /webhooks/svix
Create a Svix app and associate it with the current instance
# Create a Svix Dashboard URL
post /webhooks/svix_url
Generate a new url for accessing the Svix's management dashboard for that particular instance
# Delete a Svix app
delete /webhooks/svix
Delete a Svix app and disassociate it from the current instance
# Delete plants
delete /plants/{id}
Deletes a single plant based on the ID supplied
# Track ERC-20 Transfers
In a smart contract, events serve as notifications of specific occurrences, like transactions, or changes in ownership. Each event is uniquely identified by its event signature, which is calculated using the keccak 256 hash of the event name and its input argument types.
For example, for an ERC-20 transfer event, the event signature is determined by taking the hash of `Transfer(address,address,uint256)`. To compute this hash yourself, you can use an online `keccak-256` converter and you’ll see that the hexadecimal representation of Transfer(address,address,uint256) is 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. For a full list of signatures check [https://www.4byte.directory/event-signatures/](https://www.4byte.directory/event-signatures/).
Take into consideration that the Transfer event for ERC-20 and ERC-721 tokens is similar. Here is the Transfer event prototype on each standard:
* **ERC20**:\
`event Transfer(address indexed _from, address indexed _to, uint256 _value);`
* **ERC721**:\
`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`
These two signatures are indeed the same when you hash them to identify `Transfer` events. The example below illustrates how to set up filtering to receive transfer events.
In this example, we will monitor all the USDT transfers on the C-chain. If we go to any block explorer, select a USDT transaction, and look at `Topic 0` from the transfer event, we can get the signature.
With the event signature, we can create the webhook as follows:
```bash
curl --location '
--header 'x-glacier-api-key: ' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://webhook.site/961a0d1b-a7ed-42fd-9eab-d7e4c7eb1227",
"chainId": "43114",
"eventType": "address_activity",
"metadata": {
"addresses": ["0x54C800d2331E10467143911aabCa092d68bF4166"],
"includeInternalTxs": false,
"includeLogs": true,
"eventSignatures": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
]
},
"name": "Dokyo NFT",
"description": "Dokyo NFT"
}'
```
Whenever an NFT is transferred you’ll receive a payload like this:
```json
{
"webhookId": "6d1bd383-aa8d-47b5-b793-da6d8a115fde",
"eventType": "address_activity",
"messageId": "6a364b45-47a2-45af-97c3-0ddc2e87ad36",
"event": {
"transaction": {
"blockHash": "0x30da6a8887bf2c26b7921a1501abd6e697529427e4a4f52a9d4fc163a2344b46",
"blockNumber": "42649820",
"from": "0x0000333883f313AD709f583D0A3d2E18a44EF29b",
"gas": "245004",
"gasPrice": "30000000000",
"maxFeePerGas": "30000000000",
"maxPriorityFeePerGas": "30000000000",
"txHash": "0x2f1a9e2b8719536997596d878f21b70f2ce0901287aa3480d923e7ffc68ac3bc",
"txStatus": "1",
"input": "0xafde1b3c0000000000000000000000000…0000000000000000000000000000000000",
"nonce": "898",
"to": "0x398baa6ffc99126671ab6be565856105a6118a40",
"transactionIndex": 0,
"value": "0",
"type": 0,
"chainId": "43114",
"receiptCumulativeGasUsed": "163336",
"receiptGasUsed": "163336",
"receiptEffectiveGasPrice": "30000000000",
"receiptRoot": "0xdf05c214cee5ff908744e13a3b2879fdba01c9c7f95073670cb23ed735126178",
"contractAddress": "0x0000000000000000000000000000000000000000",
"blockTimestamp": 1709930290
},
"logs": [
{
"address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"topic1": "0x0000000000000000000000008cdd7a500f21455361cf1c2e01c0525ce92481b2",
"topic2": "0x0000000000000000000000000000333883f313ad709f583d0a3d2e18a44ef29b",
"topic3": null,
"data": "0x000000000000000000000000000000000000000000000001a6c5c6f4f4f6d060",
"transactionIndex": 0,
"logIndex": 0,
"removed": false
},
{
"address": "0x54C800d2331E10467143911aabCa092d68bF4166",
"topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
"topic1": "0x0000000000000000000000000000333883f313ad709f583d0a3d2e18a44ef29b",
"topic2": "0x0000000000000000000000000000000000000000000000000000000000000000",
"topic3": "0x0000000000000000000000000000000000000000000000000000000000001350",
"data": "0x",
"transactionIndex": 0,
"logIndex": 1,
"removed": false
},
{
"address": "0x54C800d2331E10467143911aabCa092d68bF4166",
"topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"topic1": "0x0000000000000000000000000000333883f313ad709f583d0a3d2e18a44ef29b",
"topic2": "0x0000000000000000000000008cdd7a500f21455361cf1c2e01c0525ce92481b2",
"topic3": "0x0000000000000000000000000000000000000000000000000000000000001350",
"data": "0x",
"transactionIndex": 0,
"logIndex": 2,
"removed": false
},
{
"address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"topic1": "0x0000000000000000000000008cdd7a500f21455361cf1c2e01c0525ce92481b2",
"topic2": "0x00000000000000000000000087f45335268512cc5593d435e61df4d75b07d2a2",
"topic3": null,
"data": "0x000000000000000000000000000000000000000000000000087498758a04efb0",
"transactionIndex": 0,
"logIndex": 3,
"removed": false
},
{
"address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"topic1": "0x0000000000000000000000008cdd7a500f21455361cf1c2e01c0525ce92481b2",
"topic2": "0x000000000000000000000000610512654af4fa883bb727afdff2dd78b65342b7",
"topic3": null,
"data": "0x000000000000000000000000000000000000000000000000021d261d62813bec",
"transactionIndex": 0,
"logIndex": 4,
"removed": false
},
{
"address": "0x398BAa6FFc99126671Ab6be565856105a6118A40",
"topic0": "0x50273fa02273cceea9cf085b42de5c8af60624140168bd71357db833535877af",
"topic1": null,
"topic2": null,
"topic3": null,
"data": "0x0000000000009911a89f400000000000000000000…0000010",
"transactionIndex": 0,
"logIndex": 5,
"removed": false
}
]
}
}
```
# Ethers vs Webhooks
Reacting to real-time events from Avalanche smart contracts allows for immediate responses and automation, improving user experience and streamlining application functionality. It ensures that applications stay synchronized with the blockchain state.
This article demonstrates how to monitor smart contract events using Ethers.js and Avacloud Webhooks. The comparison shows that Avacloud webhooks are easier and faster to integrate, delivering better uptime and reliability.
## Why webhooks
* Get everything you need in the payload, there is no need for extra requests
* There is no complicated logic to handle disconnections and to re-sync and recover missed blocks or transactions.
* Reduce complexity, cost, and time to market.
Ethers.js Blockchain Listener
```javascript
const ethers = require("ethers");
const ABI = require("./usdc-abi.json");
require("dotenv").config();
const NODE_URL = "wss://api.avax.network/ext/bc/C/ws";
async function getTransfer() {
try {
const usdcAddress = "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"; // USDC contract
const network = new ethers.Network("avalanche", 43114);
const provider = new ethers.WebSocketProvider(NODE_URL, network);
const contract = new ethers.Contract(usdcAddress, ABI, provider);
contract.on("Transfer", (from, to, value, event) => {
let transferEvent = {
from: from,
to: to,
value: value,
eventData: event
};
console.log("*******");
console.log(transferEvent);
console.log(">>>>>Event Data:", transferEvent.eventData);
});
} catch (error) {
console.error("Error:", error.message);
}
}
```
Output
```json
{
from: '0xfD93389fc075ff66045361423C2df68119c086Ab',
to: '0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640',
value: 9999999999n,
eventData: ContractEventPayload {
filter: 'Transfer',
emitter: Contract {
target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
interface: [Interface],
runner: WebSocketProvider {},
filters: {},
fallback: null,
[Symbol(_ethersInternal_contract)]: {}
},
log: EventLog {
provider: WebSocketProvider {},
transactionHash: '0x5f90569fd43a894a01c3f85067ad509567498e95efeefc826b97d30b9086d1f9',
blockHash: '0x8c99e8555249b2460b7e50ef987d126655efb8bd6332512f010941567fa3f0b3',
blockNumber: 19744421,
removed: false,
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
data: '0x00000000000000000000000000000000000000000000000000000002540be3ff',
topics: [Array],
index: 17,
transactionIndex: 2,
interface: [Interface],
fragment: [EventFragment],
args: [Result]
},
args: Result(3) [
'0xfD93389fc075ff66045361423C2df68119c086Ab',
'0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640',
9999999999n
],
fragment: EventFragment {
type: 'event',
inputs: [Array],
name: 'Transfer',
anonymous: false
}
}
}
```
Get undecoded raw data and then you need to call other API endpoints to get more data, for example, to get the current balance you need to make an API call, to get the token decimals you need to make an API call, to get the logo you need to make an API, you get it.
**Webhooks offer key advantages over manual parsing**
They provide real-time updates, reduce resource use, and enhance efficiency, allowing users to focus on building around these events rather than parsing data.
Listening to the same contract using AvaCloud Webhooks
```json
{
"webhookId": "c737d18a-fb40-4268-8efe-7154e73df604",
"eventType": "address_activity",
"messageId": "e5e2fc96-9aa3-43a1-9969-79f0830fe0ee",
"event": {
"transaction": {
"blockHash": "0xbc6ee8c2213262bc25dc2624bf4268e0583efa173bf9812ae888f15875cf0af1",
"blockNumber": "45689091",
"from": "0x3D3Bd891E386bFb248A9edC52cB35889ac003Db8",
"gas": "66372",
"gasPrice": "26000000000",
"maxFeePerGas": "26000000000",
"maxPriorityFeePerGas": "26000000000",
"txHash": "0x1d8f3c4f99191aec5e7cc10c58551e04e2f9ceaf5dc311b3828ab5b93435921d",
"txStatus": "1",
"input": "0xa9059cbb000000000000000000000000ffb3118124cdaebd9095fa9a479895042018cac20000000000000000000000000000000000000000000000000000000006970300",
"nonce": "0",
"to": "0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7",
"transactionIndex": 7,
"value": "0",
"type": 0,
"chainId": "43114",
"receiptCumulativeGasUsed": "651035",
"receiptGasUsed": "44026",
"receiptEffectiveGasPrice": "26000000000",
"receiptRoot": "0x455be081717eed467de449ad5cd8075caf23f2d52acbd0fc4489aff7bedcddbf",
"erc20Transfers": [
{
"transactionHash": "0x1d8f3c4f99191aec5e7cc10c58551e04e2f9ceaf5dc311b3828ab5b93435921d",
"type": "ERC20",
"from": "0x3D3Bd891E386bFb248A9edC52cB35889ac003Db8",
"to": "0xffB3118124cdaEbD9095fA9a479895042018cac2",
"value": "110560000",
"blockTimestamp": 1716238687,
"logIndex": 0,
"erc20Token": {
"address": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
"name": "TetherToken",
"symbol": "USDt",
"decimals": 6,
"valueWithDecimals": "110.56"
}
}
],
"erc721Transfers": [],
"erc1155Transfers": [],
"internalTransactions": [
{
"from": "0x3D3Bd891E386bFb248A9edC52cB35889ac003Db8",
"to": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "44026",
"gasLimit": "66372",
"transactionHash": "0x1d8f3c4f99191aec5e7cc10c58551e04e2f9ceaf5dc311b3828ab5b93435921d"
},
{
"from": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
"to": "0xBA2a995Bd4ab9e605454cCEf88169352cd5F75A6",
"internalTxType": "DELEGATECALL",
"value": "0",
"gasUsed": "15096",
"gasLimit": "36898",
"transactionHash": "0x1d8f3c4f99191aec5e7cc10c58551e04e2f9ceaf5dc311b3828ab5b93435921d"
}
],
"blockTimestamp": 1716238687
},
"logs": [
{
"address": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
"topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"topic1": "0x0000000000000000000000003d3bd891e386bfb248a9edc52cb35889ac003db8",
"topic2": "0x000000000000000000000000ffb3118124cdaebd9095fa9a479895042018cac2",
"topic3": null,
"data": "0x0000000000000000000000000000000000000000000000000000000006970300",
"transactionIndex": 7,
"logIndex": 14,
"removed": false
}
]
}
}
```
### Listening to native transactions
To monitor native transactions for specific addresses in an Ethereum blockchain, you need to follow these steps:
1. Listen for Every New Block: Set up a listener for new blocks.
2. Go Through All Transactions in Each Block: For each block, iterate through all transactions.
3. Check If from or to Matches Your List of Addresses: Filter the transactions to see if either the sender (from) or the recipient (to) matches your list of addresses.
4. Handle Transactions: Implement logic to handle the transactions that match your criteria.
### How Avacloud webhooks are better Than Ethers.js
Ethers.js is a good alternative for setting blockchain listeners to monitor smart contract events. However, if you start working with this library, you will quickly notice its limitations.
| Feature | Ethers/web3.js | Webhooks |
| :------------------------- | :------------- | :------- |
| Real-time events | Yes | Yes |
| Enriched payload | No | Yes |
| 100% reliability | No | Yes |
| Multiple addresses | No | Yes |
| Listen to wallet addresses | No | Yes |
### Resume capabilities
Ethers.js does not natively support resuming operations if the connection is interrupted. When this happens, the blockchain continues to add new blocks, and any blocks that were added during the downtime can be missed. To address this, developers must implement custom code to track the last processed block and manually resume from that point to ensure no data is lost.
In contrast, Webhooks are designed to handle interruptions more gracefully. They typically have built-in mechanisms to ensure that events are not lost, even if the connection is disrupted. When the connection is restored, Webhooks can automatically resume from where they left off, ensuring that no events are missed without requiring additional custom code. This makes Webhooks a more reliable solution for continuous data processing and real-time event handling.
# Get plants
get /plants
Returns all plants from the system that the user has access to
# Getting Started
Creating a webhook using the AvaCloud portal
There are many websites you can use to test out webhooks. For example, you can use [https://webhook.site/](https://webhook.site/) and copy your unique URL. Once you have the URL, you can test using the following steps:
1. Navigate to the [Avacloud Dashboard](https://app.avacloud.io/) and click on **Web3 Data API**
2. Click **Create Webhook**
3. Fill out the form with the unique URL generated in `https://webhook.site/` and the address you want to monitor. In this example, we want to monitor USDC on the mainnet. The address for the USDC contract is the C-chain is `0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E`.
4. Click **Create** and go to \`[https://webhook.site/](https://webhook.site/), you should see something like this:
## Managing webhooks using Glacier API
You can programmatically manage webhooks using Glacier API. For example:
1. Navigate to the [Avacloud Dashboard](https://app.avacloud.io/) and click on **Web3 Data API**
2. Click on **Add API Key**
3. Copy your API key and use it to create a webhook. For example:
```bash
curl --location 'https://glacier-api.avax.network/v1/webhooks' \
--header 'Content-Type: application/json' \
--header 'x-glacier-api-key: ' \
--data '{
"url": "https://webhook.site/af5cfd05-d104-4573-8ff0-6f11dffbf1eb",
"chainId": "43114",
"eventType": "address_activity",
"metadata": {
"addresses": ["0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"]
},
"name": "Dokyo",
"description": "Dokyo NFT"
"includeInternalTxs": true,
"includeLogs": True
}'
```
Use all Glacier methods at your convenience to `create`, `update`, `delete` or `list` the webhooks in your AvaCloud account.
### Local testing with a Node.js Express app
If we want to test the webhook in our computer and we are behind a proxy/NAT device or a firewall we need a tool like Ngrok. Avacloud will trigger the webhook and make a POST to the Ngrok cloud, then the request is forwarded to your local Ngrok client who in turn forwards it to the Node.js app listening on port 8000.
Go to [https://ngrok.com/](https://ngrok.com/) create a free account, download the binary, and connect to your account. Create a Node.js app with Express and paste the following code to receive the webhook:
```javascript
const express = require('express');
const app = express();
app.post('/callback', express.json({ type: 'application/json' }), (request, response) => {
const { body, headers } = request;
// Handle the event
switch (body.eventType) {
case 'address_activity':
console.log("*** Address_activity ***");
console.log(body);
break;
// ... handle other event types
default:
console.log(`Unhandled event type ${body}`);
}
// Return a response to acknowledge receipt of the event
response.json({ received: true });
});
const PORT = 8000;
app.listen(PORT, () => console.log(`Running on port ${PORT}`));
```
Run the app with the following command:
```shell
node app.js
The Express app will be listening on port 8000. To start an HTTP tunnel forwarding to your local port 8000 with Ngrok, run this next:
```
```shell
./ngrok http 8000
```
You should see something like this:
```bash
ngrok (Ctrl+C to quit)
Take our ngrok in production survey! https://forms.gle/aXiBFWzEA36DudFn6
Session Status online
Account javier.toledo@avalabs.org (Plan: Free)
Update update available (version 3.7.0, Ctrl-U to update)
Version 3.5.0
Region United States (us)
Latency -
Web Interface http://127.0.0.1:4040
Forwarding https://825a-2600-1700-5220-11a0-385f-7786-5e74-32cb.ngrok-free.app -> http://localhost:8000
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
```
Copy the HTTPS forwarding URL and append the `/callbackpath` and type the address you want to monitor.
If we transfer AVAX to the address AvaCloud will detect the payment and the webhook will be triggered. Now we can receive the event on our local server. The response should be something like this:
```json
{
"webhookId": "8ca68f98-18e5-47fb-a669-9ba7a6ed32b0",
"eventType": "address_activity",
"messageId": "ad66c866-17b4-44f7-9485-32211170da86",
"event": {
"transaction": {
"blockHash": "0x924ab683b4eba825410b8f233297927aa91af9483fe2d7dd799afbe0e70ea2db",
"blockNumber": "42776050",
"from": "0x4962aE47413a39fe219e17679124DF0086f0C369",
"gas": "296025",
"gasPrice": "35489466312",
"maxFeePerGas": "35489466312",
"maxPriorityFeePerGas": "1500000000",
"txHash": "0x9d3b6efae152cd17a30e5522b07d7217a9809a4a437b3269ded7474cfdecd167",
"txStatus": "1",
"input": "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000065ef6db400000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000009b6e64a8ec600000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000009b6e64a8ec60000000000000000000000000000000000000000000000000001d16b69c3febc194200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042b31f66aa3c1e785363f0875a1b74e27b85fd66c70001f4b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e000bb8d586e7f844cea2f87f50152665bcbc2c279d8d70000000000000000000000000000000000000000000000000000000000000",
"nonce": "0",
"to": "0x4dae2f939acf50408e13d58534ff8c2776d45265",
"transactionIndex": 17,
"value": "700000000000000000",
"type": 2,
"chainId": "43114",
"receiptCumulativeGasUsed": "2215655",
"receiptGasUsed": "244010",
"receiptEffectiveGasPrice": "28211132966",
"receiptRoot": "0x40d0ea00b2ce9e72a4bdebfbdc8dd4d73ebecbf80f1c107993eb78a5d28a44bf",
"contractAddress": "0x0000000000000000000000000000000000000000",
"blockTimestamp": 1710189471
},
"logs": [
{
"address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"topic0": "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c",
"topic1": "0x0000000000000000000000004dae2f939acf50408e13d58534ff8c2776d45265",
"topic2": null,
"topic3": null,
"data": "0x00000000000000000000000000000000000000000000000009b6e64a8ec60000",
"transactionIndex": 17,
"logIndex": 39,
"removed": false
},
{
"address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"topic1": "0x000000000000000000000000fae3f424a0a47706811521e3ee268f00cfb5c45e",
"topic2": "0x0000000000000000000000004dae2f939acf50408e13d58534ff8c2776d45265",
"topic3": null,
"data": "0x00000000000000000000000000000000000000000000000000000000020826ca",
"transactionIndex": 17,
"logIndex": 40,
"removed": false
},
{
"address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"topic1": "0x0000000000000000000000004dae2f939acf50408e13d58534ff8c2776d45265",
"topic2": "0x000000000000000000000000fae3f424a0a47706811521e3ee268f00cfb5c45e",
"topic3": null,
"data": "0x00000000000000000000000000000000000000000000000009b6e64a8ec60000",
"transactionIndex": 17,
"logIndex": 41,
"removed": false
},
{
"address": "0xfAe3f424a0a47706811521E3ee268f00cFb5c45E",
"topic0": "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67",
"topic1": "0x0000000000000000000000004dae2f939acf50408e13d58534ff8c2776d45265",
"topic2": "0x0000000000000000000000004dae2f939acf50408e13d58534ff8c2776d45265",
"topic3": null,
"data": "0x00000000000000000000000000000000000000000000000009b6e64a8ec60000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffdf7d93600000000000000000000000000000000000000000000751b593c19962c0ca27000000000000000000000000000000000000000000000000006d2063b8b0f00effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc606b",
"transactionIndex": 17,
"logIndex": 42,
"removed": false
},
{
"address": "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70",
"topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"topic1": "0x000000000000000000000000a7141c79d3d4a9ad67ba95d2b97fe7eed9fb92b3",
"topic2": "0x0000000000000000000000004962ae47413a39fe219e17679124df0086f0c369",
"topic3": null,
"data": "0x000000000000000000000000000000000000000000000001d75d7654d90a2700",
"transactionIndex": 17,
"logIndex": 43,
"removed": false
},
{
"address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"topic1": "0x0000000000000000000000004dae2f939acf50408e13d58534ff8c2776d45265",
"topic2": "0x000000000000000000000000a7141c79d3d4a9ad67ba95d2b97fe7eed9fb92b3",
"topic3": null,
"data": "0x00000000000000000000000000000000000000000000000000000000020826ca",
"transactionIndex": 17,
"logIndex": 44,
"removed": false
},
{
"address": "0xA7141C79d3d4a9ad67bA95D2b97Fe7EeD9fB92B3",
"topic0": "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67",
"topic1": "0x0000000000000000000000004dae2f939acf50408e13d58534ff8c2776d45265",
"topic2": "0x0000000000000000000000004962ae47413a39fe219e17679124df0086f0c369",
"topic3": null,
"data": "0x00000000000000000000000000000000000000000000000000000000020826cafffffffffffffffffffffffffffffffffffffffffffffffe28a289ab26f5d90000000000000000000000000000000000000f410a3539e2dfdf2b8bb00e7cab2f00000000000000000000000000000000000000000000000099a49d85f5d9286a000000000000000000000000000000000000000000000000000000000004375d",
"transactionIndex": 17,
"logIndex": 45,
"removed": false
}
]
}
}
```
# Monitoring multiple addresses
A single webhook can monitor multiple addresses, you don't need to create one webhook per address.
In the free plan, you add up to 5 addresses per webhook. If you need more than that you can upgrade your plan.
### Creating the webhook
Let's start by creating a new webhook to monitor all USDC and USDT activity:
```bash
curl --location 'https://glacier-api.avax.network/v1/webhooks' \
--header 'x-glacier-api-key: ' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://webhook.site/4eb31e6c-a088-4dcb-9a5d-e9341624b584",
"chainId": "43114",
"eventType": "address_activity",
"includeInternalTxs": true,
"includeLogs": true,
"metadata": {
"addresses": [
"0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"
]
},
"name": "Tokens",
"description": "Track tokens"
}
```
It returns the following:
```json
{
"id": "401da7d9-d6d7-46c8-b431-72ff1e1543f4",
"eventType": "address_activity",
"chainId": "43114",
"metadata": {
"addresses": [
"0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"
]
},
"includeInternalTxs": true,
"includeLogs": true,
"url": "https://webhook.site/4eb31e6c-a088-4dcb-9a5d-e9341624b584",
"status": "active",
"createdAt": 1715621587726,
"name": "Tokens",
"description": "Track tokens"
}
```
### Adding addresses to monitor
With the webhook `id` we can add more addresses. In this case, let's add the contract addresses for JOE and PNG:
```bash
curl --location --request PATCH 'https://glacier-api.avax.network/v1/webhooks/401da7d9-d6d7-46c8-b431-72ff1e1543f4/addresses' \
--header 'x-glacier-api-key: ' \
--header 'Content-Type: application/json' \
--data '{
"addresses": [
"0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd",
"0x60781C2586D68229fde47564546784ab3fACA982"
]
}
```
Following that, we will begin to receive events from the four smart contracts integrated into the webhook: USDC, USDT, JOE, and PNG.
### Deleting addresses
To remove addresses, simply send an array:
```bash
curl --location --request DELETE 'https://glacier-api.avax.network/v1/webhooks/401da7d9-d6d7-46c8-b431-72ff1e1543f4/addresses' \
--header 'x-glacier-api-key:
**Webhooks vs. WebSockets:**\
The difference between webhooks and WebSockets is that webhooks can only facilitate one-way communication between two services. In contrast, WebSockets can facilitate two-way communication between a user and a service, recognizing events and displaying them to the user as they occur.
### Event structure
The Event structure always begins with the following parameters:
```json
{
"webhookId": "6d1bd383-aa8d-47b5-b793-da6d8a115fde",
"eventType": "address_activity",
"messageId": "8e4e7284-852a-478b-b425-27631c8d22d2",
"event": {
}
}
```
**Parameters:**
* `webhookId`: Unique identifier for the webhook in your account.
* `eventType`: The event that caused the webhook to be triggered. In the future, there will be multiple types of events, for the time being only the address\_activity event is supported. The address\_activity event gets triggered whenever the specified addresses participate in a token or AVAX transaction.
* `messageId`: Unique identifier per event sent.
* `event`: Event payload. It contains details about the transaction, logs, and traces. By default logs and internal transactions are not included, if you want to include them use `"includeLogs": true`, and `"includeInternalTxs": true`.
### Address Activity webhook
The address activity webhook allows you to track any interaction with an address (any address). Here is an example of this type of event:
```json
{
"webhookId": "263942d1-74a4-4416-aeb4-948b9b9bb7cc",
"eventType": "address_activity",
"messageId": "94df1881-5d93-49d1-a1bd-607830608de2",
"event": {
"transaction": {
"blockHash": "0xbd093536009f7dd785e9a5151d80069a93cc322f8b2df63d373865af4f6ee5be",
"blockNumber": "44568834",
"from": "0xf73166f0c75a3DF444fAbdFDC7e5EE4a73fA51C7",
"gas": "651108",
"gasPrice": "31466275484",
"maxFeePerGas": "31466275484",
"maxPriorityFeePerGas": "31466275484",
"txHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4",
"txStatus": "1",
"input": "0xb80c2f090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e000000000000000000000000000000000000000000000000006ca0c737b131f2000000000000000000000000000000000000000000000000000000000011554e000000000000000000000000000000000000000000000000000000006627dadc0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000004600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000006ca0c737b131f2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c70000000000000000000000000000000000000000000000000000000000000001000000000000000000000000be882fb094143b59dc5335d32cecb711570ebdd40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000be882fb094143b59dc5335d32cecb711570ebdd400000000000000000000000000000000000000000000000000000000000000010000000000000000000027100e663593657b064e1bae76d28625df5d0ebd44210000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c7000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e0000000000000000000000000000000000000000000000000000000000000bb80000000000000000000000000000000000000000000000000000000000000000",
"nonce": "4",
"to": "0x1dac23e41fc8ce857e86fd8c1ae5b6121c67d96d",
"transactionIndex": 0,
"value": "30576074978046450",
"type": 0,
"chainId": "43114",
"receiptCumulativeGasUsed": "212125",
"receiptGasUsed": "212125",
"receiptEffectiveGasPrice": "31466275484",
"receiptRoot": "0xf355b81f3e76392e1b4926429d6abf8ec24601cc3d36d0916de3113aa80dd674",
"erc20Transfers": [
{
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4",
"type": "ERC20",
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"value": "30576074978046450",
"blockTimestamp": 1713884373,
"logIndex": 2,
"erc20Token": {
"address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"name": "Wrapped AVAX",
"symbol": "WAVAX",
"decimals": 18,
"valueWithDecimals": "0.030576074978046448"
}
},
{
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4",
"type": "ERC20",
"from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"to": "0xf73166f0c75a3DF444fAbdFDC7e5EE4a73fA51C7",
"value": "1195737",
"blockTimestamp": 1713884373,
"logIndex": 3,
"erc20Token": {
"address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"name": "USD Coin",
"symbol": "USDC",
"decimals": 6,
"valueWithDecimals": "1.195737"
}
},
{
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4",
"type": "ERC20",
"from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"to": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"value": "30576074978046450",
"blockTimestamp": 1713884373,
"logIndex": 4,
"erc20Token": {
"address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"name": "Wrapped AVAX",
"symbol": "WAVAX",
"decimals": 18,
"valueWithDecimals": "0.030576074978046448"
}
}
],
"erc721Transfers": [],
"erc1155Transfers": [],
"internalTransactions": [
{
"from": "0xf73166f0c75a3DF444fAbdFDC7e5EE4a73fA51C7",
"to": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"internalTxType": "CALL",
"value": "30576074978046450",
"gasUsed": "212125",
"gasLimit": "651108",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xF2781Bb34B6f6Bb9a6B5349b24de91487E653119",
"internalTxType": "DELEGATECALL",
"value": "30576074978046450",
"gasUsed": "176417",
"gasLimit": "605825",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "9750",
"gasLimit": "585767",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6",
"internalTxType": "DELEGATECALL",
"value": "0",
"gasUsed": "2553",
"gasLimit": "569571",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "CALL",
"value": "30576074978046450",
"gasUsed": "23878",
"gasLimit": "566542",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "25116",
"gasLimit": "540114",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "81496",
"gasLimit": "511279",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "491",
"gasLimit": "501085",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"to": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "74900",
"gasLimit": "497032",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "32063",
"gasLimit": "463431",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6",
"internalTxType": "DELEGATECALL",
"value": "0",
"gasUsed": "31363",
"gasLimit": "455542",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "2491",
"gasLimit": "430998",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"to": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "7591",
"gasLimit": "427775",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "CALL",
"value": "0",
"gasUsed": "6016",
"gasLimit": "419746",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421",
"to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "491",
"gasLimit": "419670",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "3250",
"gasLimit": "430493",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6",
"internalTxType": "DELEGATECALL",
"value": "0",
"gasUsed": "2553",
"gasLimit": "423121",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d",
"to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"internalTxType": "STATICCALL",
"value": "0",
"gasUsed": "1250",
"gasLimit": "426766",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
},
{
"from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6",
"internalTxType": "DELEGATECALL",
"value": "0",
"gasUsed": "553",
"gasLimit": "419453",
"transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4"
}
],
"blockTimestamp": 1713884373
}
}
}
```
In the free plan webhooks are automatically disabled after delivering 10 consecutive unsuccessful events.
# Post plants
post /plants
Creates a new plant in the store
# Send Push notification
In this tutorial, we'll explore how to send push notifications to a user's wallet whenever they receive a transaction containing tokens. It's a handy way to keep users informed about their account activity.
Note: This hypothetical example is not for real-world production use. We're simplifying things here for demonstration purposes, so there's no thorough error handling.
We'll be using [OneSignal](https://onesignal.com) for sending push notifications, but you can also achieve similar functionality with other services like [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) or [AWS Pinpoint](https://aws.amazon.com/pinpoint/).
Now, let's dive into the details!
### Step 1 - OneSignal Setup
The first step is to create a free account in [OneSignal](https://onesignal.com). For this example, we are going to use Web push but it works similarly for mobile.
To get started, the first step is to create a free account on OneSignal. Once you've signed up and logged in, we'll proceed to create a new OneSignal App.
For this example, we'll focus on using Web push notifications, but keep in mind that the process is quite similar for mobile apps.
Create a new OneSignal App and select Web. Once your app is created, you'll be provided with an App ID and API Key. Keep these credentials handy as we'll need them later to integrate OneSignal with our code.
Next, click on Configure Your Platform and select Web, select Code Custom, and set the site URL `http://localhost:3000` and enable both toggles for local development.
This will generate a code snippet to add to your code. Download the OneSignal SDK files and copy them to the top-level root of your directory.
### Step 2 - Frontend Setup
In a real-world scenario, your architecture typically involves customers signing up for subscriptions within your Web or Mobile App. To ensure these notifications are sent out, your app needs to register with a push notification provider such as OneSignal.
To maintain privacy and security, we'll be using a hash of the wallet address as the `externalID` instead of directly sharing the addresses with OneSignal. This `externalID` will then be mapped to an address in our database. So, when our backend receives a webhook for a specific address, it can retrieve the corresponding `externalID` and send a push notification accordingly.
![OneSignal Architecture](https://mintlify.s3.us-west-1.amazonaws.com/avalabs-47ea3976/images/onesignal-architecture.png)
For the sake of simplicity in our demonstration, we'll present a basic scenario where our frontend app retrieves the wallet address and registers it with OneSignal. Additionally, we'll simulate a database using an array within the code. Download the [sample code](https://github.com/javiertc/webhookdemo) and you'll see `client/inde.html` with this content.
```html
Avalanche push notifications
```
Run the project using Nodejs.
```bash
npm install express axios path body-parser dotenv
node app.js
```
Open a Chrome tab and type `http://localhost:3000`, you should see something like this. Then click on Connect and accept receiving push notifications. If you are using MacOS, check in **System Settings** > **Notifications** that you have enabled notifications for the browser.
If everything runs correctly your browser should be registered in OneSignal. To check go to **Audience** > **Subscriptions** and verify that your browser is registered.
### Step 3 - Backend Setup
Now, let's configure the backend to manage webhook events and dispatch notifications based on the incoming data. Here's the step-by-step process:
1. **Transaction Initiation:** When someone starts a transaction with your wallet as the destination, AvaCloud webhooks detect the transaction and generate an event.
2. **Event Triggering:** The backend receives the event triggered by the transaction, containing the destination address.
3. **ExternalID Retrieval:** Using the received address, the backend retrieves the corresponding `externalID` associated with that wallet.
4. **Notification Dispatch:** The final step involves sending a notification through OneSignal, utilizing the retrieved `externalID`.
![OneSignal Backend](https://mintlify.s3.us-west-1.amazonaws.com/avalabs-47ea3976/images/onesignal-backend.png)
#### 3.1 - Use Ngrok to tunnel the traffic to localhost
If we want to test the webhook in our computer and we are behind a proxy/NAT device or a firewall we need a tool like Ngrok. Glacier will trigger the webhook and make a POST to the Ngrok cloud, then the request is forwarded to your local Ngrok client who in turn forwards it to the Node.js app listening on port 3000.
Go to [https://ngrok.com/](https://ngrok.com/) create a free account, download the binary, and connect to your account. Create a Node.js app with Express and paste the following code to receive the webhook:
To start an HTTP tunnel forwarding to your local port 3000 with Ngrok, run this next:
```bash
./ngrok http 3000
```
You should see something like this:
```
ngrok (Ctrl+C to quit)
Take our ngrok in production survey! https://forms.gle/aXiBFWzEA36DudFn6
Session Status online
Account javier.toledo@avalabs.org (Plan: Free)
Version 3.8.0
Region United States (us)
Latency 48ms
Web Interface http://127.0.0.1:4040
Forwarding https://c902-2600-1700-5220-11a0-813c-d5ac-d72c-f7fd.ngrok-free.app -> http://localhost:3000
Connections ttl opn rt1 rt5 p50 p90
33 0 0.00 0.00 5.02 5.05
HTTP Requests
-------------
```
#### 3.2 - Create the webhook
The webhook can be created using the [Avacloud Dashboard](https://app.avacloud.io/) or Glacier API. For convenience, we are going to use cURL. For that copy the forwarding URL generated by Ngrok and append the `/callbackpath` and our address.
```bash
curl --location 'https://glacier-api-dev.avax.network/v1/webhooks' \
--header 'x-glacier-api-key: ' \
--header 'Content-Type: application/json' \
--data '{
"url": " https://c902-2600-1700-5220-11a0-813c-d5ac-d72c-f7fd.ngrok-free.app/callback",
"chainId": "43113",
"eventType": "address_activity",
"includeInternalTxs": true,
"includeLogs": true,
"metadata": {
"addresses": ["0x8ae323046633A07FB162043f28Cea39FFc23B50A"]
},
"name": "My wallet",
"description": "My wallet"
}'
```
Don't forget to add your API Key. If you don't have one go to the [Avacloud Dashboard](https://app.avacloud.io/) and create a new one.
#### 3.3 - The backend
To run the backend we need to add the environment variables in the root of your project. For that create an `.env` file with the following values:
```
PORT=3000
ONESIGNAL_API_KEY=
APP_ID=
```
To get the APP ID from OneSignal go to **Settings** > **Keys and IDs**
Since we are simulating the connection to a database to retrieve the externalID, we need to add the wallet address and the OneSignal externalID to the myDB array.
```javascript
//simulating a DB
const myDB = [
{ name: 'wallet1', address: '0x8ae323046633A07FB162043f28Cea39FFc23B50A', externalID: '9c96e91d40c7a44c763fb55960e12293afbcfaf6228860550b0c1cc09cd40ac3' },
{ name: 'wallet2', address: '0x1f83eC80D755A87B31553f670070bFD897c40CE0', externalID: '0xd39d39c99305c6df2446d5cc3d584dc1eb041d95ac8fb35d4246f1d2176bf330' }
];
```
The code handles a webhook event triggered when a wallet receives a transaction, performs a lookup in the simulated "database" using the receiving address to retrieve the corresponding OneSignal `externalID`, and then sends an instruction to OneSignal to dispatch a notification to the browser, with OneSignal ultimately delivering the web push notification to the browser.
```javascript
require('dotenv').config();
const axios = require('axios');
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
const port = process.env.PORT || 3000;
// Serve static website
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, './client')));
//simulating a DB
const myDB = [
{ name: 'wallet1', address: '0x8ae323046633A07FB162043f28Cea39FFc23B50A', externalID: '9c96e91d40c7a44c763fb55960e12293afbcfaf6228860550b0c1cc09cd40ac3' },
{ name: 'wallet2', address: '0x1f83eC80D755A87B31553f670070bFD897c40CE0', externalID: '0xd39d39c99305c6df2446d5cc3d584dc1eb041d95ac8fb35d4246f1d2176bf330' }
];
app.post('/callback', async (req, res) => {
const { body } = req;
try {
res.sendStatus(200);
handleTransaction(body.event.transaction).catch(error => {
console.error('Error processing transaction:', error);
});
} catch (error) {
console.error('Error processing transaction:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Handle transaction
async function handleTransaction(transaction) {
console.log('*****Transaction:', transaction);
const notifications = [];
const erc20Transfers = transaction?.erc20Transfers || [];
for (const transfer of erc20Transfers) {
const externalID = await getExternalID(transfer.to);
const { symbol, valueWithDecimals } = transfer.erc20Token;
notifications.push({
type: transfer.type,
sender: transfer.from,
receiver: transfer.to,
amount: valueWithDecimals,
token: symbol,
externalID
});
}
if (transaction?.networkToken) {
const { tokenSymbol, valueWithDecimals } = transaction.networkToken;
const externalID = await getExternalID(transaction.to);
notifications.push({
sender: transaction.from,
receiver: transaction.to,
amount: valueWithDecimals,
token: tokenSymbol,
externalID
});
}
if (notifications.length > 0) {
sendNotifications(notifications);
}
}
//connect to DB and return externalID
async function getExternalID(address) {
const entry = myDB.find(entry => entry.address.toLowerCase() === address.toLowerCase());
return entry ? entry.externalID : null;
}
// Send notifications
async function sendNotifications(notifications) {
for (const notification of notifications) {
try {
const data = {
include_aliases: { external_id: [notification.externalID.toLowerCase()] },
target_channel: 'push',
isAnyWeb: true,
contents: { en: `You've received ${notification.amount} ${notification.token}` },
headings: { en: 'Core wallet' },
name: 'Notification',
app_id: process.env.APP_ID
};
console.log('data:', data);
const response = await axios.post('https://onesignal.com/api/v1/notifications', data, {
headers: {
Authorization: `Bearer ${process.env.ONESIGNAL_API_KEY}`,
'Content-Type': 'application/json'
}
});
console.log('Notification sent:', response.data);
} catch (error) {
console.error('Error sending notification:', error);
// Optionally, implement retry logic here
}
}
}
// Start the server
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`);
});
```
You can now start your backend server by running:
```Shell
node app.js
```
Send AVAX from another wallet to the wallet being monitored by the webhook and you should receive a notification with the amount of Avax received. You can try it with any other ERC20 token as well.
### Conclusion
In this tutorial, we've set up a frontend to connect to the Core wallet and enable push notifications using OneSignal. We've also implemented a backend to handle webhook events and send notifications based on the received data. By integrating the frontend with the backend, users can receive real-time notifications for blockchain events.
# Rate Limits
Rate limiting is managed through a weighted scoring system, known as Compute Units (CUs). Each API request consumes a specified number of CUs, determined by the complexity of the request. This system is designed to accommodate basic requests while efficiently handling more computationally intensive operations.
## Rate Limit Tiers
The maximum CUs (rate-limiting score) for a user depends on their subscription level and is delineated in the following table:
| Subscription Level | Per Minute Limit (CUs) | Per Day Limit (CUs) |
| :----------------- | :--------------------- | :------------------ |
| Unauthenticated | 6,000 | 1,200,000 |
| Free | 8,000 | 2,000,000 |
| Base | 10,000 | 3,750,000 |
| Growth | 14,000 | 11,200,000 |
| Pro | 20,000 | 25,000,000 |
To update your subscription level use the [AvaCloud Portal](https://app.avacloud.io/)
Note: Rate limits apply collectively across both Webhooks and Data APIs, with usage from each counting toward your total CU limit.
## Rate Limit Categories
The CUs for each category are defined in the following table:
{/* webhooks weights value table start */}
| Weight | CU Value |
| :----- | :------- |
| Free | 1 |
| Small | 10 |
| Medium | 20 |
| Large | 50 |
| XL | 100 |
| XXL | 200 |
{/* webhooks weights value table end */}
## Rate Limits for Webhook Endpoints
The CUs for each route are defined in the table below:
{/* webhooks execution weights table start */}
| Endpoint | Method | Weight | CU Value |
| :------------------------------------------ | :----- | :----- | :------- |
| `/v1/webhooks` | POST | Medium | 20 |
| `/v1/webhooks` | GET | Small | 10 |
| `/v1/webhooks/{id}` | GET | Small | 10 |
| `/v1/webhooks/{id}` | DELETE | Medium | 20 |
| `/v1/webhooks/{id}` | PATCH | Medium | 20 |
| `/v1/webhooks:generateOrRotateSharedSecret` | POST | Medium | 20 |
| `/v1/webhooks:getSharedSecret` | GET | Small | 10 |
| `/v1/webhooks/{id}/addresses` | PATCH | Medium | 20 |
| `/v1/webhooks/{id}/addresses` | DELETE | Medium | 20 |
| `/v1/webhooks/{id}/addresses` | GET | Medium | 20 |
{/* webhooks execution weights table end */}
All rate limits, weights, and CU values are subject to change.
# Retry mechanism
We want to make sure you receive all your webhook messages, even if there are temporary issues. That’s why we’ve set up a retry system to resend messages if they don’t get through the first time.
## How it works
1. **First attempt:**
When we send a webhook message to your server, we expect an immediate response with a `200` status code. This indicates that you’ve successfully received the message. Your server should return the `200` status code first and then proceed to process the event. Avoid processing the event before returning the `200` response, as this can lead to timeouts and potential webhook deactivation.
2. **If we don't get a `200` response due to:**
* Your server might be down.
* There could be network issues.
* Your server returns an error status code (`400` or higher).
3. **Retrying the message:**
We'll try sending the message **three more times** if we don't get the right response.
![title](https://mintlify.s3.us-west-1.amazonaws.com/avalabs-47ea3976/images/retries.png)
**Timing between retries:**
* **First retry:** We'll wait **15 seconds** after the initial attempt before trying again.
* **Second retry:** If needed, we'll wait another **15 seconds** before the third attempt.
* **Third retry:** This will be our third and final attempt.
4. **Timeout**: We’ll wait a final 15 seconds after the third retry. If we don’t receive a `200` response, the message will be marked as failed, and we’ll start sending the next event. Starting from the first attempt, your server has up to 1 minute to respond with a `200` status.
### Failure limits before deactivation:
A webhook subscription will be deactivated if the total number of message delivery failures from subscription creation reaches a threshold depending on your plan tier.
* **Free Plan users:** 1 failed message delivery
* **Paid Plan users:** 100 failed message deliveries
### What you can do
**Ensure server availability:**
* Keep your server running smoothly to receive webhook messages without interruption.
* Implement logging for incoming webhook requests and your server's responses to help identify any issues quickly.
### Design for idempotency:
Set up your webhook handler so it can safely process the same message multiple times without causing errors or unwanted effects. This way, if retries occur, they won't negatively impact your system.
# Webhook Signature
To make your webhooks extra secure, you can verify that they originated from AvaCloud by generating an HMAC SHA-256 hash code using your Authentication Token and request body. You can get the signing secret through the AvaCloud portal or Glacier API.
### Find your signing secret
**Using the AvaCloud portal**\
Navigate to the webhook section and click on Generate Signing Secret. Create the secret and copy it to your code.
**Using Glacier API**\
The following endpoint retrieves a shared secret:
```bash
curl --location 'https://glacier-api.avax.network/v1/webhooks:getSharedSecret' \
--header 'x-glacier-api-key: ' \
```
### Validate the signature received
Every outbound request will include an authentication signature in the header. This signature is generated by:
1. **Canonicalizing the JSON Payload**: This means arranging the JSON data in a standard format.
2. **Generating a Hash**: Using the HMAC SHA256 hash algorithm to create a hash of the canonicalized JSON payload.
To verify that the signature is from AvaCloud, follow these steps:
1. Generate the HMAC SHA256 hash of the received JSON payload.
2. Compare this generated hash with the signature in the request header.
This process, known as verifying the digital signature, ensures the authenticity and integrity of the request.
**Example Request Header**
```
Content-Type: application/json;
x-signature: your-hashed-signature
```
### Example Signature Validation Function
This Node.js code sets up an HTTP server using the Express framework. It listens for POST requests sent to the `/callback` endpoint. Upon receiving a request, it validates the signature of the request against a predefined `signingSecret`. If the signature is valid, it logs match; otherwise, it logs no match. The server responds with a JSON object indicating that the request was received.
```JavaScript Node
const express = require('express');
const crypto = require('crypto');
const { canonicalize } = require('json-canonicalize');
const app = express();
app.use(express.json({limit: '50mb'}));
const signingSecret = 'c13cc017c4ed63bcc842c8edfb49df37512280326a32826de3b885340b8a3d53';
function isValidSignature(signingSecret, signature, payload) {
const canonicalizedPayload = canonicalize(payload);
const hmac = crypto.createHmac('sha256', Buffer.from(signingSecret, 'hex'));
const digest = hmac.update(canonicalizedPayload).digest('base64');
console.log("signature: ", signature);
console.log("digest", digest);
return signature === digest;
}
app.post('/callback', express.json({ type: 'application/json' }), (request, response) => {
const { body, headers } = request;
const signature = headers['x-signature'];
// Handle the event
switch (body.evenType) {
case 'address_activity':
console.log("*** Address_activity ***");
console.log(body);
if (isValidSignature(signingSecret, signature, body)) {
console.log("match");
} else {
console.log("no match");
}
break;
// ... handle other event types
default:
console.log(`Unhandled event type ${body}`);
}
// Return a response to acknowledge receipt of the event
response.json({ received: true });
});
const PORT = 8000;
app.listen(PORT, () => console.log(`Running on port ${PORT}`));
```
```python Python
from flask import Flask, request, jsonify
import hmac
import hashlib
import base64
import json
app = Flask(__name__)
SIGNING_SECRET = 'c13cc017c4ed63bcc842c8edfb49df37512280326a32826de3b885340b8a3d53'
def canonicalize(payload):
"""Function to canonicalize JSON payload"""
# In Python, canonicalization can be achieved by using sort_keys=True in json.dumps
return json.dumps(payload, separators=(',', ':'), sort_keys=True)
def is_valid_signature(signing_secret, signature, payload):
canonicalized_payload = canonicalize(payload)
hmac_obj = hmac.new(bytes.fromhex(signing_secret), canonicalized_payload.encode('utf-8'), hashlib.sha256)
digest = base64.b64encode(hmac_obj.digest()).decode('utf-8')
print("signature:", signature)
print("digest:", digest)
return signature == digest
@app.route('/callback', methods=['POST'])
def callback_handler():
body = request.json
signature = request.headers.get('x-signature')
# Handle the event
if body.get('eventType') == 'address_activity':
print("*** Address_activity ***")
print(body)
if is_valid_signature(SIGNING_SECRET, signature, body):
print("match")
else:
print("no match")
else:
print(f"Unhandled event type {body}")
# Return a response to acknowledge receipt of the event
return jsonify({"received": True})
if __name__ == '__main__':
PORT = 8000
print(f"Running on port {PORT}")
app.run(port=PORT)
```
```go Go
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"sort"
"strings"
)
const signingSecret = "c13cc017c4ed63bcc842c8edfb49df37512280326a32826de3b885340b8a3d53"
// Canonicalize function sorts the JSON keys and produces a canonicalized string
func Canonicalize(payload map[string]interface{}) (string, error) {
var sb strings.Builder
var keys []string
for k := range payload {
keys = append(keys, k)
}
sort.Strings(keys)
sb.WriteString("{")
for i, k := range keys {
v, err := json.Marshal(payload[k])
if err != nil {
return "", err
}
sb.WriteString(fmt.Sprintf("\"%s\":%s", k, v))
if i < len(keys)-1 {
sb.WriteString(",")
}
}
sb.WriteString("}")
return sb.String(), nil
}
func isValidSignature(signingSecret, signature string, payload map[string]interface{}) bool {
canonicalizedPayload, err := Canonicalize(payload)
if err != nil {
fmt.Println("Error canonicalizing payload:", err)
return false
}
key, err := hex.DecodeString(signingSecret)
if err != nil {
fmt.Println("Error decoding signing secret:", err)
return false
}
h := hmac.New(sha256.New, key)
h.Write([]byte(canonicalizedPayload))
digest := h.Sum(nil)
encodedDigest := base64.StdEncoding.EncodeToString(digest)
fmt.Println("signature:", signature)
fmt.Println("digest:", encodedDigest)
return signature == encodedDigest
}
func callbackHandler(w http.ResponseWriter, r *http.Request) {
var body map[string]interface{}
err := json.NewDecoder(r.Body).Decode(&body)
if err != nil {
fmt.Println("Error decoding body:", err)
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
signature := r.Header.Get("x-signature")
eventType, ok := body["eventType"].(string)
if !ok {
fmt.Println("Error parsing eventType")
http.Error(w, "Invalid event type", http.StatusBadRequest)
return
}
switch eventType {
case "address_activity":
fmt.Println("*** Address_activity ***")
fmt.Println(body)
if isValidSignature(signingSecret, signature, body) {
fmt.Println("match")
} else {
fmt.Println("no match")
}
default:
fmt.Printf("Unhandled event type %s\n", eventType)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]bool{"received": true})
}
func main() {
http.HandleFunc("/callback", callbackHandler)
fmt.Println("Running on port 8000")
http.ListenAndServe(":8000", nil)
}
```
```rust Rust
use actix_web::{web, App, HttpServer, HttpResponse, Responder, post};
use serde::Deserialize;
use hmac::{Hmac, Mac};
use sha2::Sha256;
use base64::encode;
use std::collections::BTreeMap;
type HmacSha256 = Hmac;
const SIGNING_SECRET: &str = "c13cc017c4ed63bcc842c8edfb49df37512280326a32826de3b885340b8a3d53";
#[derive(Deserialize)]
struct EventPayload {
eventType: String,
// Add other fields as necessary
}
// Canonicalize the JSON payload by sorting keys
fn canonicalize(payload: &BTreeMap) -> String {
serde_json::to_string(payload).unwrap()
}
fn is_valid_signature(signing_secret: &str, signature: &str, payload: &BTreeMap) -> bool {
let canonicalized_payload = canonicalize(payload);
let mut mac = HmacSha256::new_from_slice(signing_secret.as_bytes())
.expect("HMAC can take key of any size");
mac.update(canonicalized_payload.as_bytes());
let result = mac.finalize();
let digest = encode(result.into_bytes());
println!("signature: {}", signature);
println!("digest: {}", digest);
digest == signature
}
#[post("/callback")]
async fn callback(body: web::Json>, req: web::HttpRequest) -> impl Responder {
let signature = req.headers().get("x-signature").unwrap().to_str().unwrap();
if let Some(event_type) = body.get("eventType").and_then(|v| v.as_str()) {
match event_type {
"address_activity" => {
println!("*** Address_activity ***");
println!("{:?}", body);
if is_valid_signature(SIGNING_SECRET, signature, &body) {
println!("match");
} else {
println!("no match");
}
}
_ => {
println!("Unhandled event type: {}", event_type);
}
}
} else {
println!("Error parsing eventType");
return HttpResponse::BadRequest().finish();
}
HttpResponse::Ok().json(serde_json::json!({ "received": true }))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(callback)
})
.bind("0.0.0.0:8000")?
.run()
.await
}
```
```TypeScript AvaCloud SDK
import { isValidSignature } from '@avalabs/avacloud-sdk/utils';
import express from 'express';
const app = express();
app.use(express.json());
const signingSecret = 'your-signing-secret'; // Replace with your signing secret
app.post('/webhook', (req, res) => {
const signature = req.headers['x-signature'];
const payload = req.body;
if (isValidSignature(signingSecret, signature, payload)) {
console.log('Valid signature');
// Process the request
} else {
console.log('Invalid signature');
}
res.json({ received: true });
});
app.listen(8000, () => console.log('Server running on port 8000'));
```
# Supported EVM Chains
### Supported L1s
Webhooks are currently enabled for the following L1s. Additional L1s will be added in the future:
**Mainnet**
| L1 | Chain ID |
| -------- | -------- |
| Beam | 4337 |
| DFK | 53935 |
| Dexalot | 432204 |
| Shrapnel | 2044 |
| Testnet | Fuji |
**Testnet**
| L1 | Chain ID |
| -------- | -------- |
| Pulsar | 431234 |
| Beam | 13337 |
| Dexalot | 432201 |
| DFK | 335 |
| WAGMI | 11111 |
| Shrapnel | 2038 |
We will continue expanding this list by adding new L1s.
# Add addresses to webhook
patch /v1/webhooks/{id}/addresses
Add addresses to webhook.
# Create a webhook
post /v1/webhooks
Create a new webhook.
# Deactivate a webhook
delete /v1/webhooks/{id}
Deactivates a webhook by ID.
# Generate a shared secret
post /v1/webhooks:generateOrRotateSharedSecret
Generates a new shared secret.
# Get a shared secret
get /v1/webhooks:getSharedSecret
Get a previously generated shared secret.
# Get a webhook by ID
get /v1/webhooks/{id}
Retrieves a webhook by ID.
# List adresses by webhook
get /v1/webhooks/{id}/addresses
List adresses by webhook.
# List webhooks
get /v1/webhooks
Lists webhooks for the user.
# Remove addresses from webhook
delete /v1/webhooks/{id}/addresses
Remove addresses from webhook.
# Update a webhook
patch /v1/webhooks/{id}
Updates an existing webhook.