getBalance Solana Developer Guide

Learn how to read a single Solana account balance with getBalance, while handling API keys in a frontend — Shield keys that keep your account key out of the browser, a wallet connector in pure JavaScript, and the connected wallet's balance on screen.

Reference documentation: getBalance

Before We Begin

If you are new to Solana development, it would be a good idea to read through the developer guides for getAccountInfo, getMultipleAccounts, and getProgramAccounts before continuing.

This guide will require a FluxRPC API key (a free plan one is sufficient). There is no need to copy-paste it — just navigate back to this page while logged in, and we will automatically use the key you select.

In this guide, we will accomplish the following:

  1. Fetch the balance of a single account on the Solana blockchain
  2. Use shielded API keys to build a frontend application without revealing your API key to users
  3. Build a wallet connector in pure JavaScript
  4. Display the balance of the wallet that the user connects

We will provide examples in JavaScript in this guide.

Fetching the Balance of an Account on Solana

Just want to check a balance?

You do not need this guide. The getBalance Try It runs the same request in your browser — paste an address, press the button, done. This page is for putting that request inside a site of your own, which means writing a little code.

Fetching an account's balance on Solana is very straightforward. You only need to know the account's public key to perform a getBalance RPC call. While it is also possible to get an account's balance with getAccountInfo and (more efficiently) with getMultipleAccounts, often in a frontend application it's practical to just fetch the balance of a single account, e.g. a user's wallet.

As a pure RPC call, getBalance works as follows:

curl "https://eu.fluxrpc.com?key=<Your-API-Key>" -s -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBalance",
    "params": [
      "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"
    ]
  }'

Shielded API Keys

One thing that's impractical in the previous request is that it requires your API key. If your frontend code includes that API key, it's trivial for anyone to take it and use it for themselves. Shielded API keys mitigate this issue.

Shielded API keys have a low per-IP-address rate limit — 5 requests per second. This means they work fine for your app, which is used by many users on many different IP addresses, but someone can't access the full rate limit of your API key with what you give them. Shielded keys are used at slightly different URLs than normal API keys:

curl "{{SHIELD_RPC_URL}}?key={{SHIELD_KEY}}" -s -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBalance",
    "params": [
      "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"
    ]
  }'

Install a Wallet Extension

Before continuing, you will need a Solana wallet browser extension — Phantom, Solflare, or Backpack. You'll also need to create a wallet if you don't already have one. Each extension announces itself to the pages you visit, which is how the example finds it later.

Create the Two Files

Make a new folder anywhere, and create two empty files inside it with your editor.

wallet-balance/
├── index.html
└── app.js

Put this in index.html. The IDs match up with the JS code.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>FluxRPC Wallet Balance</title>
  </head>
  <body>
    <main>
      <h1>FluxRPC Wallet Balance</h1>

      <button id="connect-wallet-button" type="button">Connect Wallet</button>
      <button id="get-balance-button" type="button" disabled>Get Balance</button>

      <p id="wallet-address"></p>
      <p id="status-message" role="status"></p>
      <pre id="balance-output"></pre>
    </main>

    <script src="./app.js" defer></script>
  </body>
</html>

app.js contains all the JavaScript logic. The sections after this one build it up one part at a time and explain each. If you would rather copy the entire file once, it's the first code block below:

Add the Shield Key

Start app.js with your Shield key and the Shield host. Sign in and both values below are already yours, otherwise replace the placeholders by hand. Paste the key on its own: the ?key= part is added at request time, and a full URL in FLUXRPC_SHIELD_KEY is the easiest way to earn yourself a confusing invalid api key response.

The third constant is a unit. Solana counts balances in lamports, and one SOL is a billion of them. If you were displaying the balance of a specific token (using getTokenAccountBalance, or the more efficient trick we covered in getMultipleAccounts), you might use a different number here, determined by the mint decimals. For example if it was mint decimals=6, you might put 1_000_000.

The rest is the five element IDs from the HTML and the one piece of state this app keeps: the connected address.

const FLUXRPC_SHIELD_KEY = "{{SHIELD_KEY}}";
const FLUXRPC_SHIELD_RPC_URL = "{{SHIELD_RPC_URL}}";
const LAMPORTS_PER_SOL = 1_000_000_000;

const connectButton = document.getElementById("connect-wallet-button");
const balanceButton = document.getElementById("get-balance-button");
const addressOutput = document.getElementById("wallet-address");
const statusOutput = document.getElementById("status-message");
const balanceOutput = document.getElementById("balance-output");

let walletAddress = "";

Find the Installed Wallets

Wallets do not share a common variable to look under — Phantom, Solflare, and Backpack each use their own, and any wallet released tomorrow will use another. So instead of guessing names, use the Wallet Standard handshake that all major Solana wallets implement: your page dispatches one event announcing it is ready, and each installed wallet answers by registering itself.

The page never names a wallet, so this works with extensions that did not exist when you wrote it. Wallets that register a moment later push into the same array, so a slow extension still turns up before anyone clicks.

// The Wallet Standard handshake: the page announces it is ready, and every
// installed wallet answers by registering itself.
function discoverWallets() {
  const found = [];

  const api = {
    register(...wallets) {
      wallets.forEach((wallet) => {
        if (wallet.chains?.some((chain) => chain.startsWith("solana:"))) {
          found.push(wallet);
        }
      });
      return () => {};
    },
  };

  window.addEventListener("wallet-standard:register-wallet", (event) => {
    event.detail(api);
  });

  window.dispatchEvent(
    new CustomEvent("wallet-standard:app-ready", { detail: api }),
  );

  return found;
}

const solanaWallets = discoverWallets();

Connect the Wallet

connectWallet asks the first wallet it found for permission through its standard:connect feature and reads account.address. This is already a base58 public key, so no conversion is needed. As we mentioned earlier, that public key is the only thing the balance request needs.

Connecting the wallet to get the user's public key is a permission prompt, not a signature. Connecting a wallet in this way should never ask the user to sign a transaction.

async function connectWallet() {
  const wallet = solanaWallets[0];

  if (!wallet) {
    throw new Error(
      "No Solana wallet found. Install Phantom, Solflare, or Backpack, then reload this page.",
    );
  }

  const { accounts } = await wallet.features["standard:connect"].connect();

  walletAddress = accounts[0].address;
  addressOutput.textContent = "Connected public key: " + walletAddress;
}

Request the Balance

Now, we fetch the balance using the user's public key and our shielded FluxRPC API key. It's the getBalance RPC call we mentioned earlier.

async function loadBalance() {
  const response = await fetch(
    FLUXRPC_SHIELD_RPC_URL + "?key=" + encodeURIComponent(FLUXRPC_SHIELD_KEY),
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        jsonrpc: "2.0",
        id: 1,
        method: "getBalance",
        params: [walletAddress],
      }),
    },
  );

  const payload = await response.json();

  // JSON-RPC reports failures in the body, not in the HTTP status code.
  if (payload.error) {
    throw new Error(payload.error.message || payload.error);
  }

  const lamports = payload.result.value;

  balanceOutput.textContent = JSON.stringify(
    {
      publicKey: walletAddress,
      lamports,
      sol: lamports / LAMPORTS_PER_SOL,
    },
    null,
    2,
  );
}

Wire the Buttons

Finally we'll add some simple click handlers. Each one announces what it is doing, awaits the step, and prints the error message if it throws. Get Balance starts disabled in the HTML and becomes available once a wallet is connected.

connectButton.addEventListener("click", async () => {
  statusOutput.textContent = "Opening the wallet...";

  try {
    await connectWallet();
    statusOutput.textContent = "Wallet connected.";
    balanceButton.disabled = false;
  } catch (error) {
    statusOutput.textContent = error.message;
  }
});

balanceButton.addEventListener("click", async () => {
  statusOutput.textContent = "Loading balance through FluxRPC...";

  try {
    await loadBalance();
    statusOutput.textContent = "Balance loaded.";
  } catch (error) {
    statusOutput.textContent = error.message;
  }
});

In this example, we've specifically avoided complex dependencies. This is for two reasons — first, the extra abstraction would get in the way of us showing you how things work. Second, using unnecessary dependencies is an attack vector for supply chain attacks.

There have been a few bad ones on NPM this year (I probably won't have to update this text next year...). So it's a good idea to avoid unnecessary dependencies where practical.

Run It on localhost

Run one of these inside the folder holding your two files to launch a temporary web server:

# if you have Python
python3 -m http.server 8000

# the same command, on Windows
py -m http.server 8000

# if you have Node.js instead
npx serve . --listen 8000

Then open http://localhost:8000 in your browser. Type that address yourself rather than using any links from the terminal. This is because you want to specifically be on localhost and not some IP address, it's likely that your wallet will refuse to connect to a non-HTTPS website unless it's localhost.

If you prefer not to use a terminal

In VS Code, install the Live Server extension, then right-click index.html and choose Open with Live Server. It serves the folder on localhost and opens the browser for you.

Click Connect Wallet and approve the wallet prompt. The page prints your public key and the status line reads Wallet connected. Then click Get Balance. The output block fills in with the wallet's mainnet balance:

{
  "publicKey": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
  "lamports": 1435000000,
  "sol": 1.435
}

lamports is what the RPC returns; sol is that number divided by 1,000,000,000.

Troubleshooting

Tips & Tricks

Next Steps

If you have not already, getAccountInfo covers what else lives in an account besides its balance, and getMultipleAccounts is what you reach for the moment you need more than one balance at a time.

← All developer guides