Written by
Owen Venter
Published on
November 29, 2024
Copy link

How to Get Token Holders on Solana

In this guide, we will be taking a look at how to get all the holders of a fungible token such as USDC. This can be useful in situations where you want to track the holders of a token or reward holders with an airdrop.

Overview

Let's first look at how tokens, especially non-fungible tokens, work on Solana. When a developer creates a token, they use the token program to create a mint account. This mint account holds information about a specific token, such as the name, token address, and image.Once a mint account has been made, the token can be minted, and stored in a token account. A token account is an account that holds information about a specific token that is owned by a specific address. This will include details such as the mint address, owner address, and the amount of the specific token in the account. For example, an address that holds some USDC (an SPL token) will have a token account for USDC.

Account breakdown diagram


Now that we grasp how tokens and token accounts work, we can look at getting all the token holders for a given token. Every wallet that holds a specific token will have a token account for that token. This means that this token will be associated with the token accounts of all the wallets that hold the token. This is how we will figure out who all the holders are. If we could find a way to get all the token accounts associated with a token and then get the owners of these accounts, we would have a list of all the holders! 

getTokenAccounts Method

Luckily, the Helius getTokenAccounts API method allows us to do precisely this. We are able to include any token’s mint address in the API calls parameters and we will get a list back of all the the token accounts that have been created for that token. On top of this, the API also returns the owner of each token account; this owner is what we commonly refer to as a token holder. One small thing to note is that one account can have multiple token accounts for the same token. This isn't a big issue; we just need to set up some logic to deal with token accounts that share an owner. 

Implementation

Now let's dig into some code to see how we can actually do this. You will need a Helius API key to follow along. You can get one for free by heading to https://dev.helius.xyz and signing up for an account. To start, we must create a Javascript file called getTokenHolders.js. We can start this off by adding our Helius URL and importing the fs library to save our results to a JSON file.


const url = `https://mainnet.helius-rpc.com/?api-key=`;
const fs = require("fs");

Next, we will create a method to fetch all the token accounts associated with the specific token. We can start by creating a method called findHolders, which will use the getTokenAccounts method to get the needed data. You can read more about the getTokenAccounts method here. One important thing to note is that each call to the API can only return a maximum of 1000 token accounts. Most large tokens on Solana have over 100,000 token accounts. To get around this, we will use pagination to go through all the token accounts and continue to make API calls until we have fetched data relating to all existing token accounts. In the method, we will include the token mint in the parameters of the getTokenAccounts call. As we are looping through all the token accounts, we will add each unique token account owner to a list. Once the method is done running, we will save this list to a JSON file containing all the tokens' holders.


const findHolders = async () => {
  // Pagination logic
  let page = 1;
 	// allOwners will store all the addresses that hold the token
  let allOwners = new Set();

  while (true) {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        jsonrpc: "2.0",
        method: "getTokenAccounts",
        id: "helius-test",
        params: {
          page: page,
          limit: 1000,
          displayOptions: {},
					//mint address for the token we are interested in
          mint: "CKfatsPMUf8SkiURsDXs7eK6GWb4Jsd6UDbs7twMCWxo",
        },
      }),
    });

		// Check if any error in the response
      if (!response.ok) {
        console.log(
          `Error: ${response.status}, ${response.statusText}`
        );
        break;
      }

    const data = await response.json();
  	// Pagination logic. 
    if (!data.result || data.result.token_accounts.length === 0) {
      console.log(`No more results. Total pages: ${page - 1}`);
      break;
    }
    console.log(`Processing results from page ${page}`);
 		// Adding unique owners to a list of token owners. 
    data.result.token_accounts.forEach((account) =>
      allOwners.add(account.owner)
    );
    page++;
  }

  fs.writeFileSync(
    "output.json",
    JSON.stringify(Array.from(allOwners), null, 2)
  );
};

In the example above, the getTokenAccounts method is called multiple times while pagination through all the token accounts. The API response will provide the following data for each token account: 


{

"address": "CVMR1nbxTcQ7Jpa1p137t5TyKFii3Y7Vazt9fFct3tk9",

"mint": "SHDWyBxihqiCj6YekG2GUr7wqKLeLAMK1gHZck9pL6y",

"owner": "CckxW6C1CjsxYcXSiDbk7NYfPLhfqAm3kSB5LEZunnSE",

"amount": 100000000,

"delegated_amount": 0,

"frozen": false

},

We extracted the owner from these token accounts and added that to our list. If we wanted to, we could also store the amount of the token that each token account holds to figure out the largest holders. 

Now once this has been done, all we need to do is call the method:


findHolders();

The complete code in our getTokenHolders.js file should look like this:


const url = `https://mainnet.helius-rpc.com/?api-key=`;
const fs = require("fs");


const findHolders = async () => {
  let page = 1;
  let allOwners = new Set();

  while (true) {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        jsonrpc: "2.0",
        method: "getTokenAccounts",
        id: "helius-test",
        params: {
          page: page,
          limit: 1000,
          displayOptions: {},
          mint: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
        },
      }),
    });

		// Check if any error in the response
      if (!response.ok) {
        console.log(
          `Error: ${response.status}, ${response.statusText}`
        );
        break;
      }

    const data = await response.json();

    if (!data.result || data.result.token_accounts.length === 0) {
      console.log(`No more results. Total pages: ${page - 1}`);

      break;
    }
    console.log(`Processing results from page ${page}`);
    data.result.token_accounts.forEach((account) =>
      allOwners.add(account.owner)
    );
    page++;
  }

  fs.writeFileSync(
    "output.json",
    JSON.stringify(Array.from(allOwners), null, 2)
  );
};

findHolders();

Output

The output of our code will be a list of all the holders and will look similar to this:
[

  "111An9SVxuPpgjnuXW9Ub7hcVmZpYNrYZF4edsGwJEW",

  "11Mmng3DoMsq2Roq8LBcqdz6d4kw9oSD8oka9Pwfbj",

  "112uNfcC8iwX9P2TkRdJKyPatg6a4GNcr9NC5mTc2z3",

  "113uswn5HNgEfBUKfK4gVBmd2GpZYbxd1N6h1uUWReg",

  "11CyvpdYTqFmCVWbJJeKFNX8F8RSjNSYW5VVUi8eX4P",

  "11MANeaiHEy9S9pRQNu3nqKa2gpajzX2wrRJqWrf8dQ",


]

You can test this yourself through our replit example.

Conclusion

Concluding our guide, we've successfully covered the process of identifying Solana token holders via the Helius getTokenAccounts API. This walkthrough should give you the necessary skills to engage directly with your token community, whether for airdrops, insights, or further interactions. If you have any questions, please feel free to reach out via Twitter or Discord