TzKT API
Fetching information from the Tezos blockchain
TzKT is a Tezos blockchain indexer.
We have talked about how a blockchain includes a lot of transactions and information. Working directly on the whole blockchain to fetch and process data will be time-consuming. The TzKT indexer can reduce this time and in addition, offers high-level functionalities, which are not provided by the Tezos JSON/RPC interface.
You can install or build it, and configure it for the testnet.
At this point, we want to learn how to work with the TzKT API. Therefore, we will work with a public endpoint.
You want an overview of the provided TzKT APIs and staging environments for testing, take a look here.
For the mainnet, we have:
https://api.tzkt.io/
and for Hangzhounet, we can use:
https://api.hangzhou2net.tzkt.io/
Fetching data with Node.js
Because TzKT offers a REST API, we could work with any language or tool which supports HTTP requests. Here we want to work with Node.js and request.
Create a folder and an index.js
in it:
const request = require('request');
request('https://api.hangzhou2net.tzkt.io/v1/accounts?limit=10&select=balance&sort.desc=balance', (err, res, body) => {
console.log(body);
});
Now run:
$ npm install request
and
$ node index.js
We limit the output to 10 accounts, and we want to fetch the account balance for those 10.
It is important that you first think about what you want to get before making a request because it will impact the performance.
You can specify select
, and use select.fields
or select.values
to fetch the fields or the values. As you can see in the request
, we can also tell TzKT API to sort
the balances. This will give us the 10 richest accounts.
Using data filters on the Tezos API, instead of on the client, helps increase performance and reduce the risk of errors by developing on the TzKT API. Thus, applying them can be understood as a best practice for developers.
Additionally, we can apply some filters through the API:
const request = require('request');
request('https://api.hangzhou2net.tzkt.ioo/v1/accounts?balance.gt=10000000000000', (err, res, body) => {
console.log(body);
});
This finds you an account with a balance greater than 10 million tez.
The amount of endpoints TzKT offers is huge. Each is documented, and you can find in the QUERY PARAMETERS filters to apply and in the RESPONSE SCHEMA the fields you can fetch. Please take your time and have a look into the documentation, e.g. look for different query modes:

Let us fetch the storage of our certification smart contract:
const request = require('request');
request('https://api.hangzhou2net.tzkt.io/v1/contracts/KT1SgX1ZehTysGcNoSwypwg6Xs5Cj7cpokwK/storage',
(err, res, body) => {
console.log(JSON.parse(body));
});
You see that the response is a JSON, which we can parse:
{
"certified":[
"tz1YTVs4uDn1jEQ2Z4RJyR47zAzhXv1b9Q7t",
"KT1SgX1ZehTysGcNoSwypwg6Xs5Cj7cpokwK",
"tz1ZRdP17rsZ1DqUbrraQhbcacVHyh4E6Dkw",
"tz1Q2zkgZENNF2g95NN7g1CtxAqKynWViSeN"
],
"certifier":"tz1WNmpLG56yT4XixkCFe5M5NpZ7gPgVpVqc"
}
TzKT offers deep selection, so we don't need to fetch the whole storage if we are interested in knowing the certifier:
https://api.hangzhou2net.tzkt.io/v1/contracts/KT1SgX1ZehTysGcNoSwypwg6Xs5Cj7cpokwK/storage?path=certifier
We do not have such a complex data structure but you could access deeper fields with path=certifier.field
, etc. - if there were any.
Now the question arises: If we want to track the activity on our smart contract, how often do we need to conduct this request?
It is a recommended best practice when using the TzKT API to not conduct the same data request until there has been a change. Being aware of how often data updates occur, helps you define how often you want to request data.
You know that each transaction needs to be included in a block. So we don't need to fetch data more often than blocks appear. We can fetch the protocol constants and use the timeBetweenBlocks value to estimate the arrival time for the next block. Notice that usually you would also wait for more than one block to confirm a transaction.
With request-promise:
$ npm install request
we can do:
const request = require('request-promise');
const timeBetweenBlocks = () => {
return request("https://api.hangzhou2net.tzkt.ioo/v1/protocols/current")
.then(body => JSON.parse(body).constants.timeBetweenBlocks);
}
const blockTime = () => {
return request('https://api.hangzhou2net.tzkt.io/v1/head')
.then(body => Date.parse(JSON.parse(body).timestamp));
}
const contractStorage = (address) => {
return request('https://api.hangzhou2net.tzkt.io/v1/contracts/' + address + '/storage')
.then(body => JSON.parse(body));
}
const sleep = (milliseconds) => new Promise(resolve => setTimeout(resolve, milliseconds));
async function main() {
const timeBlocks = await timeBetweenBlocks(),
timeRequest = 5000;
let nextBlockTime = await blockTime() + timeBlocks;
while(true) {
if(Date.now() >= nextBlockTime) {
nextBlockTime = await blockTime() + timeBlocks;
console.log(await contractStorage("KT1SgX1ZehTysGcNoSwypwg6Xs5Cj7cpokwK"));
}
await sleep(timeRequest);
}
}
main();
This will wait for timeBetweenBlocks
from the timestamp of the last recent block before fetching the timestamp of the most recent block. Also, even after the time passed, it won't do more than one request every five seconds. This should be enough to have accurate storage.
Notice, we didn't catch any error above, but you should always check for the response if you work with an API.
You can also try the WebSocket API JS simple client and subscribe to events. This will reduce the traffic further and make the data available in real-time.
We can also look for specific entrypoint calls or parameters in the transactions. Let us confirm with filtering that tz1YTVs4uDn1jEQ2Z4RJyR47zAzhXv1b9Q7t
is certified by tz1WNmpLG56yT4XixkCFe5M5NpZ7gPgVpVqc
:
https://api.hangzhou2net.tzkt.io/v1/operations/transactions?target=KT1SgX1ZehTysGcNoSwypwg6Xs5Cj7cpokwK¶meter=tz1YTVs4uDn1jEQ2Z4RJyR47zAzhXv1b9Q7t&select=sender
which will give you:
[{"address":"tz1WNmpLG56yT4XixkCFe5M5NpZ7gPgVpVqc"}]
Let us do something else not easy to do without the TzKT API and ask for the number of smart contracts in Hangzhounet:
https://api.hangzhou2net.tzkt.io/v1/accounts/count?type=contract
Another important request you will want to do is to fetch the supported protocols:
https://api.hangzhou2net.tzkt.io/v1/protocols
this will give you:
[
{
"code":-1,
"hash":"PrihK96nBAFSxVL1GLJTVhu9YnzkMFiBeuJRPA8NwuZVZCE1L6i",
"firstLevel":0,
"lastLevel":0,
"constants":{
...
}
},
{
"code":0,
"hash":"Ps9mPmXaRzmzk35gbAYNCAw6UXdE2qoABTHbN2oEEc1qM7CwT9P,
"firstLevel":1,
"lastLevel":1,
"constants":{
...
}
},
{
"code":1,
"hash":"PtGRANADsDU8R9daYKAgWnQYAJ64omN1o3KMGVCykShA97vQbvV",
"firstLevel":2,
"constants":{
...
}
},
{
"code":1,
"hash":"PtHangz2aRngywmSRGGvrcTyMbbdpWdpFKuS4uMWxg2RaH9i1qx",
"firstLevel":2,
"constants":{
...
}
}
]
There you see the protocol constrains in the constants
and the regarding protocol hash:
- PrihK96nBAFSxVL1GLJTVhu9YnzkMFiBeuJRPA8NwuZVZCE1L6i: Genesis block;
- Ps9mPmXaRzmzk35gbAYNCAw6UXdE2qoABTHbN2oEEc1qM7CwT9P: Genesis protocol;
- PtGRANADsDU8R9daYKAgWnQYAJ64omN1o3KMGVCykShA97vQbvV: Granadenet protocol
- PtHangz2aRngywmSRGGvrcTyMbbdpWdpFKuS4uMWxg2RaH9i1qx: Hangzhounet protocol.
So the current protocol is PtHangz2aRngywmSRGGvrcTyMbbdpWdpFKuS4uMWxg2RaH9i1qx.
You can see supported Tezos protocols on tzkt.io and check the details, e.g. for the Hangzhounet protocol details.
- Baking Bad Blog
- Dmitry (2020): Tezos Explorer API Best Practices: #2 Request only what you actually need
- Dmitry (2020): Tezos Explorer API Best Practices: #3 Filter data on the API, not on the client
- Max (2020): TzKT v1.3.1 Released With Edo Support and Some New Features in the Tezos Explorer and API
- Max (2020): TzKT v1.4 released with improved smart contract data and WebSocket API
- Tezos Indexer GitHub Repository
- TzKT API - Tezos API Documentation