How to migrate from BCD to TzKT API

A complete guide on how to replace Better Call Dev API with the according TzKT endpoints

Michael
Michael   Follow

# How to migrate from BCD to TzKT API

With the recent major update (opens new window) of TzKT API we can finally offer a fully-fledged alternative to BCD users. It is performant, stable, feature-rich, and covers much more use cases than ever.

# Main differences

# Multinetwork API 🠒 subdomain per network

In BCD network is part of the query string while on TzKT you have to use different subdomains api.{network}.tzkt.io (api.tzkt.io is actually an alias for api.mainnet.tzkt.io).

# Page size: 10 🠒 10000

In BCD we were forced to dramatically reduce the page size in order to handle the load, in TzKT you can do far fewer requests. But don't relax too much, you'll still need pagination 😃

# Metadata: snake case 🠒 camel case

In BCD we do parse and validate TZIP-12|TZIP-21 fields and return them in a structured manner. Unfortunately, since our entire API layout has snake case it ended up with metadata fields differ from their original key names, e.g. thumbnailUri (original) 🠒 thumbnail_uri (bcd). We realized it too late, when changing it would break many API clients.
In TzKT token (and contract) metadata is returned as is, without any modifications, thus you will deal with camel case (as per TZIP-12|TZIP-21 standards).

# Storage & parameters: Miguel 🠒 humanified JSON

The format BCD uses to encode contract storage and operation parameters is basically a transformed Micheline extended with certain extra information required to render the rich user interface. We call it Miguel and it's very far from being a human-readable target and even machine-readable format, it's designed for a very specific purpose.
On the contrary, TzKT APi provides a "humanified" JSON representation of the Michelson structures, no matter how complex and nested they are. Easy to read, easy to parse.

# Top 10 BCD endpoints

Here is a short migration guide for top 10 popular API endpoints.

# /v1/account/{network}/{address}/token_balances

Returns a list of tokens (balance + metadata) a particular account owns.
The accorging TzKT replacement for this call is /v1/tokens/balances (opens new window).

# Pagination

BCD has a strict limitation on how many items you can request at once, TzKT is much more democratic in that sense, but still you have to iterate over the balances in case an account owns several thousands of tokens.

- https://api.better-call.dev/v1/account/mainnet/tz1QcqnzZ8pa6VuE4MSeMjsJkiW94wNrPbgX/token_balances?offset=610&size=1  
+ https://api.tzkt.io/v1/tokens/balances?account=tz1QcqnzZ8pa6VuE4MSeMjsJkiW94wNrPbgX&limit=1&offset=250

You might also noticed that BCD also adds the total number of tokens to the response, here's how (opens new window) you can get that summary with TzKT:

https://api.tzkt.io/v1/tokens/balances/count?account=tz1QcqnzZ8pa6VuE4MSeMjsJkiW94wNrPbgX
# Filter by contract

If you are building a multi-level token navigation you might want to group balances by dapp and allow users to "drill down" to see tokens for that particular contract(s). Filtering by token contract address is what you need in that case.

- https://api.better-call.dev/v1/account/mainnet/tz1Uza3jMhipiQMtmeEidizjbSqnqZMQyRv7/token_balances?contract=KT1K4jn23GonEmZot3pMGth7unnzZ6EaMVjY&1644384642198  
+ https://api.tzkt.io/v1/tokens/balances?account=tz1Uza3jMhipiQMtmeEidizjbSqnqZMQyRv7&token.contract=KT1K4jn23GonEmZot3pMGth7unnzZ6EaMVjY
# Filter by balance

Hiding null balances is an essential thing if you are handling numerous tokens, it allows to show the user only the relevant information.

- /v1/account/mainnet/tz1WHUWCMqVqq5AagGN5DrytqWz88tYgedUr/token_balances?size=50&hide_empty=true  
+ https://api.tzkt.io/v1/tokens/balances?account=tz1WHUWCMqVqq5AagGN5DrytqWz88tYgedUr&limit=50&balance.gt=0
# More use cases

Read our recent article (opens new window) to get more insights on what you can do with the new TzKT API.

NOTE

When working with token metadata it's highly recommended to use deep selectors to reduce the network traffic, e.g.:

https://api.tzkt.io/v1/tokens/transfers?token.id=778919&sort.desc=id&limit=2&select=from.address%20as%20src,to.address%20as%20dst,amount,token.metadata.symbol%20as%20symbol,token.metadata.decimals%20as%20decimals

will result in

{
  "src": "tz1eVjytct5fWpp646fKTK2ULYtVPVYpFmQF",
  "dst": "KT1HbQepzV1nVGg8QVznG7z4RcHseD5kwqBn",
  "amount": "5",
  "symbol": "OBJKT",
  "decimals": 0
}

which is much clear and lightweight than joining the entire TZIP-12 JSON.

# /v1/tokens/{network}/transfers/{address}

Returns a list of token transfers (FA1.2|FA2) where either source or destination equal to the given address.
The according TzKT endpoint is /v1/tokens/transfers (opens new window).

# Pagination

BCD uses cursor pagination for this call, same can be achieved in TzKT. You should use id as cursor field, because it's non-duplicate and monotonically increasing.

- https://api.better-call.dev/v1/tokens/mainnet/transfers/tz1gvS6JzZFiVrQxJik8ZaHDHk6LRecncSVH?last_id=18823816  
+ https://api.tzkt.io/v1/tokens/transfers?anyof.from.to=tz1gvS6JzZFiVrQxJik8ZaHDHk6LRecncSVH&offset.cr=134802352
# Sorting

Depending on your task you might want to get only latest transfers (e.g. wallet workflow) or iterate from oldest to newest.

- https://api.better-call.dev/v1/tokens/mainnet/transfers/tz1gvS6JzZFiVrQxJik8ZaHDHk6LRecncSVH?sort=desc
+ https://api.tzkt.io/v1/tokens/transfers?anyof.from.to=tz1gvS6JzZFiVrQxJik8ZaHDHk6LRecncSVH&sort.desc=timestamp

# /v1/account/{network}/{address}/count

This is a wallet-specific endpoint allowing to get total number of tokens owned aggregated by token contract. This is quite heavy aggregation that does not perform good on accounts that owns thousands of tokens. In TzKT there are no direct replacements, but due to higher limits (page size = 10k) you can ususally achieve the same result with just few extra calls.

- https://api.better-call.dev/v1/account/mainnet/tz1NVUor94eFRvL5ijsJi5BD4qCoHEnrm7qn/count
+ https://api.tzkt.io/v1/tokens/balances?account=tz1NVUor94eFRvL5ijsJi5BD4qCoHEnrm7qn&select=token.contract.address 

# /v1/account/{network}/{address}

Returns general information about a particular account (works both for implicit acccounts and originated contracts).
The according TzKT call is /v1/accounts/{address} (opens new window).

- https://api.better-call.dev/v1/account/mainnet/tz2CduhCSfXqQUPf2zC7qDKtqhXLdBwmzuDq  
+ https://api.tzkt.io/v1/accounts/tz2CduhCSfXqQUPf2zC7qDKtqhXLdBwmzuDq

# /v1/contract

Returns basic info about a particular smart contract.
The respectful replacement is /v1/contracts/{address} (opens new window).

- https://api.better-call.dev/v1/contract/mainnet/KT1VEhYnyUpxtJPAdpAW8NZhhSzCAYFCf1oE  
+ https://api.tzkt.io/v1/contracts/KT1VEhYnyUpxtJPAdpAW8NZhhSzCAYFCf1oE

# /v1/contract/tokens

Lists all the tokens minted in this conract.
The accorging TzKT endpoint is /v1/tokens (opens new window).

# Pagination

In BCD it's size/offset pagination, in TzKT there are similar limit/offset fields, or you use a cursor over id (internal ID) or tokenId (only for the single contract scope).

- https://api.better-call.dev/v1/contract/mainnet/KT1ViVwoVfGSCsDaxjwoovejm1aYSGz7s2TZ/tokens?size=10&offset=10  
+ https://api.tzkt.io/v1/tokens?contract=KT1ViVwoVfGSCsDaxjwoovejm1aYSGz7s2TZ&offset=10&limit=10
# Filter by token

Sometimes you need to retrieve a particular token metadata.

- https://api.better-call.dev/v1/contract/mainnet/KT1ViVwoVfGSCsDaxjwoovejm1aYSGz7s2TZ/tokens?token_id=31995  
+ https://api.tzkt.io/v1/tokens?contract=KT1ViVwoVfGSCsDaxjwoovejm1aYSGz7s2TZ&tokenId=31995

# /v1/contract/{network}/storage

Most dapp data is typically stored in big maps, however sometimes you just need to check the storage itself, for instance if you are querying AMM pool sizes in order to determine md price.
The according TzKT endpoint is /v1/contracts/{address}/storage (opens new window)

- https://api.better-call.dev/v1/contract/mainnet/KT1VEhYnyUpxtJPAdpAW8NZhhSzCAYFCf1oE/storage  
+ https://api.tzkt.io/v1/contracts/KT1VEhYnyUpxtJPAdpAW8NZhhSzCAYFCf1oE/storage

# /v1/contract/{network}/{address}/tokens/holders?token_id={token_id}

Returns current holders of a particular token and their balances (which are greater than zero obviously).
The TzKT replacement is again /v1/tokens/balances (opens new window) but with some filters.

- https://api.better-call.dev/v1/contract/mainnet/KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton/tokens/holders?token_id=625848  
+ https://api.tzkt.io/v1/tokens/balances?token.contract=KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton&token.tokenId=625848&balance.gt=0&select.values=account.address,balance

Note that you still have to paginate in case there are more than 10k holders.

# /v1/tokens/{network}/metadata

Lists all the tokens with their metadata.
The according TzKT call is /v1/tokens (opens new window)

- https://api.better-call.dev/v1/tokens/mainnet/metadata  
+ https://api.tzkt.io/v1/tokens
# Filter by contact and token_id

This is pretty much the same you can achieve with contract/tokens endpoint, but nevertheless.

- https://api.better-call.dev/v1/tokens/mainnet/metadata?contract=KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton&token_id=508769  
+ https://api.tzkt.io/v1/tokens?contract=KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton&tokenId=508769

# /v1/bigmap/{network}/{big_map_id}/keys

Returns all keys and current values for a particular big map.
The respectfull TzKT endpoint is /v1/bigmaps/{big_map_id}/keys (opens new window)

- https://api.better-call.dev/v1/bigmap/mainnet/11934/keys  
+ https://api.tzkt.io/v1/bigmaps/11934/keys
# Filter by key

Typically you want to get the value for a particular key, in BCD it's done via searching by key and value strings (meaning that it will return all the entries where either key or value contains the string). In TzKT you can specify the path to the key|value subfield to filter by.

- https://api.better-call.dev/v1/bigmap/mainnet/11934/keys?q=tz1PSiE9EmFbQFb7K1cBxPTsbPdgotHLdy9e  
+ https://api.tzkt.io/v1/bigmaps/11934/keys?key=tz1PSiE9EmFbQFb7K1cBxPTsbPdgotHLdy9e

A more sophisticated example:

https://api.tzkt.io/v1/bigmaps/511/keys?key.address=tz1UBZUkXpKGhYsP5KtzDNqLLchwF4uHrGjw&key.nat=152

# Why we have two similar APIs

Better Call Dev is historically a developer tool focused on the UI and the very first version was serverless in the sense that it could work using just your RPC node. That was in the early days of Tezos when the entire blockchain was just few gigabytes in size. With the emerging dapp development we realized the need in our own backend to ensure the same good UX but for much larger data volumes. What we have in BCD is sometimes called BFF (backend for front), it's an API tailored to the needs of a specific application, in our case — the better-call.dev (opens new window) site.

At that time, our independence from third-party customers allowed us to introduce new features and break backward compatibility very quickly. When we got our first big external traffic — it was after HEN began to gain popularity — we realized that we were completely unprepared for this turn of events, and to stay functional we focused on scaling and maintaining the infrastructure.

Unlike BCD, our other product TzKT was API-first from the very beginning. It means much more flexible and generic queries, stable API layout with proper deprecation workflow, etc. In TzKT we took into account all the mistakes and insights gained during the development of BCD, which was a pioneer in its own way in many things. It absolutely does not mean that we stop support and work on Better Call Dev API, it means that now you can upgrade to a better alternative with minimal changes and get new features.

# What is the roadmap for BCD API

Better Call Dev API will remain public until the Ithaca upgrade, after that it will still be operational and will continue to power the BCD site, but the response layout can be changed and become backward incompatible.

If you have any questions or issues while migrating please join our Discord (opens new window) server and ask for assistance in #bcd-support channel.
Thank you for using our services 😃