How to Get the Minters of an NFT Collection

Learn how to get the “day 1" minters of an NFT collection. The addresses that originally minted an NFT collection (first owners).

Don’t have an API key?

Start using this API in your app today.

📘

API Endpoint

This tutorial uses the getAssetTransfers and getOwnersForContract endpoints.

Introduction

If you are an owner of an NFT collection, you might want to reward the first minters of your NFT for supporting your project from day 1. In this tutorial, we will show you how to find the original minters of an NFT collection, as well as check which minters are still holding that NFT. To accomplish this, we will be using the getAssetTransfers and verifyNftOwnership endpoints. Let's get started!

📘

NOTE

"Original Minters" or "Day 1 Minters" refers to the wallet addresses that minted the NFT from the NFT contract.

If someone bought the NFT from a secondary marketplace like Opensea, they are not considered to be an original minter or day 1 minter.

Setting up the project


Install Node and npm

In case you haven't already, install node and npm on your local machine.

Make sure that node is at least v14 or higher by typing the following in your terminal:

node -v

Create an Alchemy app


In case you haven't already, sign up for a free Alchemy account.

2880

Alchemy's account dashboard where developers can create a new app on the Ethereum blockchain.

Next, navigate to the Alchemy Dashboard and create a new app.

Make sure you set the chain to Ethereum and the network to Mainnet. Once the app is created, click on your app's View Key button on the dashboard.

Take note of the HTTP URL.

The URL will be in this form: https://eth-mainnet.g.alchemy.com/v2/xxxxxxxxx

You will need this later.


Create a node project

Let's now create an empty repository and install all node dependencies.

Run the following commands in order to create your node project and install Alchemy SDK.

mkdir nft-minter-getter && cd nft-minter-getter
npm init -y
npm install --save alchemy-sdk
touch main.js

This will create a repository named nft-minter-getter that holds all your files and dependencies.

Next, open this repo in your favorite code editor.

Now our project is set up and we are ready to write code. We will write all our code in the main.js file.

Writing the script

For the sake of this tutorial, we will be finding the minters of the Bored Ape Yacht Club NFT. To get the minter addresses, we will use the getAssetTransfers method.

  • We can pass a fromAddress and a contractAddress to this method specifying that the contractAddress is the address of an ERC721 token. It will then return all the transfers of NFTs for that contractAddress with the specified fromAddress.
  • Then we can specify the fromAddress to be 0x0000000000000000000000000000000000000000 (null address) to get the mint transactions because during a mint the token is transferred from the null address to the minter's address. We will then filter out the to addresses from these transactions, which will be the addresses of the minters of the NFT collection.

Add the following code to the main.js file:

// importing the Alchemy SDK
const { Alchemy, Network } = require("alchemy-sdk");

// configuring the Alchemy SDK
const config = {
  apiKey: "demo", // Replace with your Alchemy API Key.
  network: Network.ETH_MAINNET, // Replace with the network your NFT contract is deployed to.
};
const alchemy = new Alchemy(config);

// This is the address of the BAYC NFT Contract
const nftAddress = "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D"; // Replace with your NFT contract address.

// Creating an empty array to store the minters of the NFT Collection
let minters = [];

let pageKey; // Variable for pageKey that will be used to paginate through the API call.

// function to get all the minters of the NFT Collection
async function getMinters() {
  // Boolean variable to check if it is the first call to the API call.
  // We need this because the first time we make the API call, the pageKey will be undefined.
  let firstCall = true;

  // Looping through the API call until the pageKey is not null/undefined.
  while (firstCall || pageKey) {
    let res;
    if (pageKey) {
      // Making a call to the getAssetTransfers method to get all the transfers in which the `fromAddress` is 0x0000000000000000000000000000000000000000
  		// If the `fromAddress` is 0x0000000000000000000000000000000000000000, it means that the NFT was minted to the `toAddress` in the transfer.
      res = await alchemy.core.getAssetTransfers({
        fromAddress: "0x0000000000000000000000000000000000000000",
        category: ["erc721"], // Replace with the category of your NFT contract. BAYC is an ERC721 token.
        contractAddresses: [nftAddress], // Replace with the address of your NFT contract.
        pageKey: pageKey, // The pageKey is used to paginate through the API call.
      });
    } else {
      // Making a call to the getAssetTransfers method to get all the transfers in which the `fromAddress` is 0x0000000000000000000000000000000000000000
 		 // If the `fromAddress` is 0x0000000000000000000000000000000000000000, it means that the NFT was minted to the `toAddress` in the transfer.
      res = await alchemy.core.getAssetTransfers({
        fromAddress: "0x0000000000000000000000000000000000000000",
        category: ["erc721"], // Replace with the category of your NFT contract. BAYC is an ERC721 token.
        contractAddresses: [nftAddress], // Replace with the address of your NFT contract.
      });
    }

    firstCall = false; // Setting the `firstCall` variable to false, because it is not the first call anymore.

    // The API call returns an object with a `transfers` key, which is an array of all the transfers.

    // Looping through the response of the API call, that is, the transfers and pushing the `to` address to the `minters` array.
    // Because the `to` is the address that minted the NFT.
    for (let i = 0; i < res.transfers.length; i++) {
      const transfer = res.transfers[i];
      minters.push(transfer.to); // adding the `to` address to the `minters` array.
    }

    pageKey = res.pageKey; // Setting the pageKey from the response of the API call.
  }

  minters = [...new Set(minters)]; // Removing the duplicate addresses from the `minters` array. This is done because the API call returns the same `to` address multiple times if an address minted multiple NFTs.
  console.log("Number of unique minters:", minters.length);
  console.log(minters); // Printing the `minters` array.
}

async function main() {
  await getMinters(); // Calling the `getMinters` function.
}

main();

📘

NOTE

If you face a failed response error, try swapping the demo key with your personal Alchemy API key.

Here's an explanation of what the code is doing:

  • We first import the Alchemy SDK to be able to use it.
  • Then we configure the Alchemy SDK with the API key and the correct network.
  • We then define a variable that stores the contract address of the NFT whose minters we want to find.
  • We also set another variable called minters as an empty array to store the minters of the NFT Collection
  • Furthermore, we define a function called getMinters that contains the logic to get the minters of the NFT collection.
  • Inside the getMinters function we make a call to the getAssetTransfers method to get all the transfers for the specified contractAddress in which the fromAddress is 0x0000000000000000000000000000000000000000 (null address). These transfers will essentially be the mints because when an NFT is minted, it is transferred from the null address to the minter.
  • The API call returns an object with a transfers key, which is an array of all the transfer objects with the conditions we specified.
  • Each transfer object has a property called to which is the address to which the NFT was transferred (minter address).
  • Then we loop through the response of the API call (array of transfer objects) pushing the to address to the minters array.
  • We keep repeating this process until the pageKey exists. So this allows us to go through all the transfers.
  • We remove all the duplicate addresses from the minters array. This is done because the API call returns the same to address multiple times if an address minted multiple NFTs.
  • Finally, we log the number of unique minters and the minters array.

Run the script using:

node main.js

You should see an output like this:

Number of unique minters: 981
[
  '0xaba7161a7fb69c88e16ed9f455ce62b791ee4d03',
  '0xf7801b8115f3fe46ac55f8c0fdb5243726bdb66a',
  '0xce9000536c859b6807c3986b03b73b54e8747cdf',
  '0x8e05bd9fa3059ec69c15bc1a6f4d94f0ac26ce00',
  '0xdb4df088e85e5d66d2d2ab451d5ead618c124305',
  '0x024f1f8b3789bfdf9edb47f58a0d7a3afeaea804',
  '0x33569c101562e1faf5b24581057e5cee4c8288d7',
  '0x1acf3e6e5fb8f5d53469c15b48550bbcfeb22bfc',
  '0x77b75fbf5a53d1831204fe431b954f383b18b538',
  '0x79e25a6b59697366787cd6a6953c517a958e7de8',
  '0x08d816526bdc9d077dd685bd9fa49f58a5ab8e48',
  '0xce1599327c6cb91c98f876ccc4666869e4862357',
  '0x4b51b2e97ff1d85b449d84a6c87b37fb6cc299e7',
  '0x343f18e39ab281bdd574834be26b32f3c75f42dc',
  '0x881cad3d12f8bf32eded4820f27d88c7245a5396',
  '0x41955ab7d12f9f6c03de972b91d9b895d9c2eaf8',
  '0xeac5781e7ca68f1c7b6ebe854901e427049d0ab5',
  '0x571e56a49840474fe116794154e8c5cc5a2fabb9',
  '0x557c60995797fa7b47be105227a2e46148d85750',
  '0x6552be7816f797945120ee965b9903da3f0c4845',
  '0x16ffe3938b69132c72a5b0250708792db72971b4',
  '0x5080fda2a93078ded890d8591aebcea7f345c492',
  '0x92d901cbb7281a2ac9906879c904697ac481a203',
  '0xde0b910f192aa831d23eda3f76b5d9e803a7ae8d',
  '0x1212503603aaa0fc26a90f77515d2a51c998866c',
  '0x67d8c8b467081de46241ec17b7e3b9f64c4147cb',
  '0x8064eebd2d1ccd4290924665efc17c8f2da5bfc4',
  '0x21bca401c280c8492a1ca15d554382eecb92f2f3',
  '0xdf3c160116e1cf9ba92b9c4b9329755cf0d4fd20',
  '0x4d4937f2e4a79e2d09461f74fd62c05b630d7618',
  '0x87773aaccabe6928ff0f764fe2887f41929fa855',
  '0x35f0686c63f50707ea3b5bace186938e4e19f03a',
  '0xa5437c9d2f62f79ae22b1dbd0a735feec7cef6a7',
  '0x30d9898afb994e489ad138e0e85d1a90c14d53a5',
  '0xfaf878dcd715ae2eeffc33183b36a54d48061a16',
  '0xe9e9206b598f6fc95e006684fe432f100e876110',
  '0xe0f4232caed7ec605cad6092bf1c495e95e1b079',
  '0x188c30e9a6527f5f0c3f7fe59b72ac7253c62f28',
  '0x4444a136b1445b1f5e4a20656854adcf6fa3d667',
  '0x8818d0c53d6f27ccfbed2690c7a65fdb1d94f351',
  '0x7c54effe7bc3906b6ffe7390c8bf9afd67089409',
  '0x31c72d0d03adc76456d3c14a4fe19f8aa8307c16',
  '0xe2c4f8b9fbe931251699cdddf8f1ae59cf4c6d71',
  '0xb7d49adb031d6dbdf3e8e28f21c6dd3b6f231cd5',
  '0x69661a09bac77454bf74c86f893817f67385f62c',
  '0xc5c7b46843014b1591e9af24de797156cde67f08',
  '0xd5b47b80668840e7164c1d1d81af8a9d9727b421',
  '0x643ce52422b668f0e226dd21b4bb8d3a1fabca51',
  '0xabf9984f06799c7d9378628e31136b321d159318',
  '0x522fca1110b3d71a59a1c0200cf2a61f0656f804',
  '0x9909017a0f637380af916257d05c3e7dd2f6c68a',
  '0x710840430c6708f006f5c01598ceb04a79331839',
  '0x844c696fcb91ad1095deac4b8cca0da78e8f61e3',
  '0xb5a8168c066ef50d63c65501087b7185f8c36279',
  '0xd628028d8c49178b7e955b5a1a2e7c336dc40981',
  '0x0eefa9732dc7d2eb781dd7dd58041a24dfbf4019',
  '0xae37f0f328d50a0f365f80d7b71cd3a8d4532f91',
  '0x36991b237b1a2c2eafd94274f11e589d3c041c95',
  '0x2193777ea734a9d1671f875d044a3fb1b9f5c068',
  '0x7b15e6c439b27a553b65a9904ce571da6691a0fb',
  '0x2965b4b8226f2e5e9c790caba9ee40bb2a3ae411',
  '0x9bd91abdf39a167712b9f6c24d1c9805d5fb9242',
  '0xf3d9281fa183b74f32b96e1c5244596045f4ede8',
  '0xe32395e1e93928cc3e17152e5e79a3a3e705cbc4',
  '0xf9ebb27f0d246a63ac7f42c56b980a1241ae130b',
  '0xd657860fc512ec2250a7385a353f599e3276fddb',
  '0x7e22dd944050eb6988e1437cafd06c6a2e549d30',
  '0xa6c88571d0028f47adba983a7240bf12af94633e',
  '0xddb92a96054132801cae3943b2f966fc2c308ac8',
  '0xd383eeba9b4b17aa091ba925f6e96744358d0454',
  '0x02f5d9c2b5376f8b9150cf148aa88a5fdd5dcc50',
  '0xef11030761f02e47274b47c005f6b2ef3603eaae',
  '0x87e32d4631d728d6ad7ebcd133b11febc9da9b93',
  '0xadcad9758eabcfb25220046c8d574b373ed52e02',
  '0x4e012d25472225f3385baa43ed64dfec5885c51d',
  '0x735ed02655bca15ec46491c0de4946591135459b',
  '0x284f42e9de6ebf202a90a5e68181999c2eedf18c',
  '0x1cb1b37080d673e209eff4ef02a2b134130e88b7',
  '0x37d3c912a851bdc7fdc31f4a63970716b7b079b6',
  '0x5a007525265023821eb8acbddc4d17ec6f34db01',
  '0xae9deddf056811f91fb58208a0ef973e1eecc911',
  '0x5c0976f8e633ec51282aaae6fb518ae98b896e4d',
  '0x3d773915783f9d4b14e57951d3a7769ad70dc7c2',
  '0x10a2c688923f32b5f8ce70f7570080c724a59400',
  '0xd30b579be7da30c903c96c7de3729f8977e614e4',
  '0x0b742783bfac8d4b6d332e5d1b63f433fcd8c0a0',
  '0x34e010e92d6792c6bd20488365960d291fb84ba0',
  '0x7ad84e097ac8261fba7b4b1afe23ed84553a618d',
  '0x4fface9865bdcbc0b36ec881fa27803046a88736',
  '0x987df219564cd274d32b4b442ffad1a185fc0509',
  '0x47b2efa18736c6c211505aefd321bec3ac3e8779',
  '0x7b505d4ac6ab4cdf3ff86c48ebf4b3645377817f',
  '0x3006195b0dc697fd65b32b287cfbea3ee56450f5',
  '0xfab97682e0b4b1589786382eeba1b758ffae7ff9',
  '0x58d3e132ef8539a8188e340b814606aeca2e5d3e',
  '0xb889539ea95d2ce71d2d1cf17d9eb18aae4aee7d',
  '0x8efef6b061bda1a1712db316e059cbc8ebdcae4d',
  '0x77fb4fa1aba92576942ad34bc47834059b84e693',
  '0x66dd944a82a35540b407dff02e9dd638d4de94ce',
  '0x68cb53f84ad5f92be90bb21e44092b0ebd5197e2',
  ... 881 more items
]

So from here, we can see that there were 981 "day 1" minters of the BAYC NFT!

This is how you can get the minters of an NFT collection.

Checking if the minters still hold the NFT

To check if the minters still hold an NFT from the collection we can use the getOwnersForContract endpoint to get an array of the current owners of the NFT collection. We can then loop through the minters array and for each minter if that minter is in the currentOwners array then that minter is still the holder of the collection.

So, the complete script will look like this:

// importing the Alchemy SDK
const { Alchemy, Network } = require("alchemy-sdk");

// configuring the Alchemy SDK
const config = {
  apiKey: "demo", // Replace with your Alchemy API Key.
  network: Network.ETH_MAINNET, // Replace with the network your NFT contract is deployed to.
};
const alchemy = new Alchemy(config);

// This is the address of the BAYC NFT Contract
const nftAddress = "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D"; // Replace with your NFT contract address.

// Creating an empty array to store the minters of the NFT Collection
let minters = [];

let pageKey; // Variable for pageKey that will be used to paginate through the API call.

// function to get all the minters of the NFT Collection
async function getMinters() {
  // Boolean variable to check if it is the first call to the API call.
  // We need this because the first time we make the API call, the pageKey will be undefined.
  let firstCall = true;

  // Looping through the API call until the pageKey is undefined.
  while (firstCall || pageKey) {
    let res;
    if (pageKey) {
      // Making a call to the getAssetTransfers method to get all the transfers in which the `fromAddress` is 0x0000000000000000000000000000000000000000
      // If the `fromAddress` is 0x0000000000000000000000000000000000000000, it means that the NFT was minted to the `toAddress` in the transfer.
      res = await alchemy.core.getAssetTransfers({
        fromAddress: "0x0000000000000000000000000000000000000000",
        category: ["erc721"], // Replace with the category of your NFT contract. BAYC is an ERC721 token.
        contractAddresses: [nftAddress], // Replace with the address of your NFT contract.
        pageKey: pageKey, // The pageKey is used to paginate through the API call.
      });
    } else {
      // Making a call to the getAssetTransfers method to get all the transfers in which the `fromAddress` is 0x0000000000000000000000000000000000000000
      // If the `fromAddress` is 0x0000000000000000000000000000000000000000, it means that the NFT was minted to the `toAddress` in the transfer.
      res = await alchemy.core.getAssetTransfers({
        fromAddress: "0x0000000000000000000000000000000000000000",
        category: ["erc721"], // Replace with the category of your NFT contract. BAYC is an ERC721 token.
        contractAddresses: [nftAddress], // Replace with the address of your NFT contract.
      });
    }

    firstCall = false; // Setting the `firstCall` variable to false, because it is not the first call anymore.

    // The API call returns an object with a `transfers` key, which is an array of all the transfers.

    // Looping through the response of the API call, that is, the transfers and pushing the `to` address to the `minters` array.
    // Because the `to` is the address that minted the NFT.
    for (let i = 0; i < res.transfers.length; i++) {
      const transfer = res.transfers[i];
      minters.push(transfer.to); // adding the `to` address to the `minters` array.
    }

    pageKey = res.pageKey; // Setting the pageKey from the response of the API call.
  }

  minters = [...new Set(minters)]; // Removing the duplicate addresses from the `minters` array. This is done because the API call returns the same `to` address multiple times if an address minted multiple NFTs.
  console.log("Number of unique minters:", minters.length);
}

async function checkIfHolder() {
  // Variable to store the number of minters who are still holding an NFT from the Collection.
  let holdingDay1Minters = 0;

  // Getting the current owners of the NFT Collection.
  let currentOwnersResponse = await alchemy.nft.getOwnersForContract(
    nftAddress
  );
  let currentOwners = currentOwnersResponse.owners; // Getting the `owners` array from the response.

  // Looping through the `minters` array and checking if they still hold an NFT from the Collection
  for (let i = 0; i < minters.length; i++) {
    const minter = minters[i]; // Getting the address of the minter.

    // If the minter is present in the `currentOwners` array, it means that the minter still holds an NFT from the Collection.
    if (currentOwners.includes(minter)) {
      holdingDay1Minters++; // Incrementing the `holdingDay1Minters` variable by 1.
    }
  }

  console.log(
    "Number of day 1 minters who are still holding the NFT:",
    holdingDay1Minters
  );
  console.log(
    "Percentage of day 1 minters who are still holding the NFT:",
    ((holdingDay1Minters / minters.length) * 100).toFixed(2), // Stripping down the percentage to 2 decimal places.
    "%"
  );
}

async function main() {
  await getMinters(); // Calling the `getMinters` function.
  await checkIfHolder(); // Calling the `checkIfHolder` function.
}

main();

Here's an explanation of the new checkIfHolder function that we added:

  • The function defines a variable called holdingDay1Minters to store the number of minters who are still holders of the NFT collection.
  • Then it continues by making an API call to the getOwnersForContract method to get the current owners of the NFT collection.
  • It then loops through the original minters of the collection (stored in the minters array) and checks if each minter is still a current owner of the collection. If a minter is still a current owner, the holdingDay1Minters variable is incremented by 1.
  • At the end, we log the number and percentage of day 1 minters who still hold the NFT.

📘

NOTE

Please note that here we are checking if the minters still hold an NFT from the NFT Collection. That NFT can be any NFT that is part of the collection, not necessarily the NFT (token id) that the minter originally minted.

If you want to check that the minters still hold exactly the same NFT (token id) that they minted originally, you can use the getOwnersForToken endpoint.

Run the script using:

node main.js

You should see an output like this:

Number of unique minters: 981
Number of day 1 minters who are still holding the NFT: 90
Percentage of day 1 minters who are still holding the NFT: 9.17 %

From here we can observe that only 90 "day 1" minters of BAYC are still holding the NFT. That's about 9.17% of the initial minters!

Conclusion


Congratulations! You now know how to use the getAssetTransfers and getOwnersForContract APIs to get the "day 1" minters of an NFT collection and check if they still hold the NFT!

If you enjoyed this tutorial, give us a tweet @AlchemyPlatform.

Don't forget to join our Discord server to meet other blockchain devs, builders, and entrepreneurs.


ReadMe