> ## Documentation Index
> Fetch the complete documentation index at: https://seilabs-docs-evm-cookbook.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Read a balance

> Read the SEI balance of any address, and its balance of an ERC-20 token such as USDC, with viem, ethers, or web3.py.

export const RunSnippet = props => {
  const {method = 'eth_blockNumber', params = [], network = 'testnet', endpoint, label, title, description, decode = 'auto'} = props || ({});
  const ENDPOINTS = {
    testnet: 'https://evm-rpc-testnet.sei-apis.com',
    mainnet: 'https://evm-rpc.sei-apis.com'
  };
  const rpcUrl = endpoint || ENDPOINTS[network] || ENDPOINTS.testnet;
  const networkLabel = network === 'mainnet' ? 'Sei Mainnet' : network === 'testnet' ? 'Sei Testnet' : network;
  const requestBody = {
    jsonrpc: '2.0',
    id: 1,
    method,
    params
  };
  const requestJson = JSON.stringify(requestBody, null, 2);
  const [phase, setPhase] = useState('idle');
  const [result, setResult] = useState(null);
  const [errorMsg, setErrorMsg] = useState(null);
  const [elapsed, setElapsed] = useState(null);
  const [copied, setCopied] = useState(false);
  const [btnHover, setBtnHover] = useState(false);
  const groupThousands = s => s.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  const hexToDecimal = value => {
    if (typeof value !== 'string' || !(/^0x[0-9a-fA-F]+$/).test(value)) return null;
    if (value.length > 66) return null;
    try {
      return groupThousands(BigInt(value).toString(10));
    } catch (e) {
      return null;
    }
  };
  const run = async () => {
    setPhase('loading');
    setErrorMsg(null);
    setResult(null);
    setElapsed(null);
    const startedAt = typeof performance !== 'undefined' ? performance.now() : null;
    try {
      const response = await fetch(rpcUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(requestBody)
      });
      const data = await response.json();
      if (startedAt != null && typeof performance !== 'undefined') {
        setElapsed(Math.round(performance.now() - startedAt));
      }
      if (data && data.error) {
        setErrorMsg(data.error.message || 'RPC returned an error');
        setPhase('error');
        return;
      }
      setResult(data ? data.result : undefined);
      setPhase('success');
    } catch (err) {
      setErrorMsg(err && err.message ? err.message : 'Request failed');
      setPhase('error');
    }
  };
  const resultString = result === undefined ? 'undefined' : JSON.stringify(result, null, 2);
  const decoded = decode !== 'off' && typeof result === 'string' ? hexToDecimal(result) : null;
  const copyResult = () => {
    const flashCopied = () => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    };
    if (typeof navigator !== 'undefined' && navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(resultString).then(flashCopied, () => {});
      return;
    }
    if (typeof document !== 'undefined') {
      try {
        const ta = document.createElement('textarea');
        ta.value = resultString;
        ta.style.position = 'fixed';
        ta.style.opacity = '0';
        document.body.appendChild(ta);
        ta.select();
        document.execCommand('copy');
        document.body.removeChild(ta);
        flashCopied();
      } catch (e) {}
    }
  };
  const HAIRLINE = 'rgba(128, 128, 128, 0.25)';
  const surfaceStyle = {
    backgroundColor: 'rgba(128, 128, 128, 0.08)'
  };
  const monoStyle = {
    fontFamily: 'var(--sei-font-mono)'
  };
  const codeStyle = {
    backgroundColor: 'rgba(128, 128, 128, 0.05)',
    fontFamily: 'var(--sei-font-mono)'
  };
  const buttonStyle = {
    backgroundColor: btnHover ? 'var(--sei-maroon-200)' : 'var(--sei-maroon-100)',
    color: '#ffffff',
    fontFamily: 'var(--sei-font-mono)',
    textTransform: 'uppercase',
    letterSpacing: '0.04em',
    fontSize: '10px',
    opacity: phase === 'loading' ? 0.7 : 1,
    cursor: phase === 'loading' ? 'default' : 'pointer'
  };
  return <div className="not-prose w-full rounded-lg border overflow-hidden my-4" style={{
    borderColor: HAIRLINE
  }}>
			<div className="flex items-center justify-between gap-3 px-4 py-2.5 border-b" style={{
    ...surfaceStyle,
    borderBottomColor: HAIRLINE
  }}>
				<div className="flex flex-col min-w-0">
					<span className="text-sm font-medium text-neutral-900 dark:text-white truncate" style={monoStyle}>
						{title || method}
					</span>
					<span className="text-xs text-neutral-600 dark:text-neutral-400">{networkLabel}</span>
				</div>
				<button type="button" onClick={run} disabled={phase === 'loading'} onMouseEnter={() => setBtnHover(true)} onMouseLeave={() => setBtnHover(false)} className="inline-flex items-center gap-1.5 px-3 py-1.5 shrink-0 transition-colors" style={buttonStyle}>
					{phase === 'loading' ? <svg className="animate-spin h-4 w-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
								<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
								<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 0 1 8-8v4a4 4 0 0 0-4 4H4z" />
							</svg> : <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
								<path d="M8 5v14l11-7z" />
							</svg>}
					{phase === 'loading' ? 'Running…' : label || 'Run'}
				</button>
			</div>

			{description ? <div className="px-4 pt-3 text-sm text-neutral-600 dark:text-neutral-400">{description}</div> : null}

			<div className="px-4 pt-3 pb-1">
				<span className="text-xs uppercase tracking-wide text-neutral-600 dark:text-neutral-400">Request</span>
			</div>
			<pre className="m-0 px-4 py-3 text-sm overflow-x-auto text-neutral-700 dark:text-neutral-300" style={codeStyle}>
				{requestJson}
			</pre>

			{phase === 'success' ? <div className="border-t" style={{
    borderTopColor: HAIRLINE
  }}>
					<div className="flex items-center justify-between px-4 pt-3 pb-1">
						<span className="text-xs uppercase tracking-wide text-neutral-600 dark:text-neutral-400">Response{elapsed != null ? ` · ${elapsed} ms` : ''}</span>
						<button type="button" onClick={copyResult} className="text-xs text-neutral-500 hover:text-neutral-800 dark:text-neutral-400 dark:hover:text-neutral-200 transition-colors">
							{copied ? 'Copied' : 'Copy'}
						</button>
					</div>
					<pre className="m-0 px-4 py-3 text-sm overflow-x-auto text-neutral-700 dark:text-neutral-300" style={codeStyle}>
						{resultString}
					</pre>
					{decoded ? <div className="px-4 pb-3 text-xs text-neutral-600 dark:text-neutral-400" style={monoStyle}>
							= {decoded} (decimal)
						</div> : null}
				</div> : null}

			{phase === 'error' ? <div className="border-t" style={{
    borderTopColor: HAIRLINE
  }}>
					<div className="px-4 pt-3 pb-1">
						<span className="text-xs uppercase tracking-wide text-neutral-600 dark:text-neutral-400">Error</span>
					</div>
					<pre className="m-0 px-4 py-3 text-sm overflow-x-auto text-red-600 dark:text-red-400" style={codeStyle}>
						{errorMsg}
					</pre>
				</div> : null}
		</div>;
};

A balance read is a free, read-only call. You do not need a private key, a wallet, or SEI for gas.

## Try it live

This call runs from your browser against Sei Mainnet. It returns the balance in wei, the smallest unit of SEI. One SEI is 10<sup>18</sup> wei.

<RunSnippet method="eth_getBalance" params={['0x0000000000000000000000000000000000000000', 'latest']} network="mainnet" title="eth_getBalance" description="Balance in wei of the example address at the latest block. The widget converts the hex value to decimal." />

## Install

<CodeGroup>
  ```bash viem theme={"dark"}
  npm install viem
  ```

  ```bash ethers theme={"dark"}
  npm install ethers
  ```

  ```bash web3.py theme={"dark"}
  pip install web3
  ```
</CodeGroup>

The TypeScript examples use top-level `await`. Save each one as a `.mts` file, such as `script.mts`. Run it with `npx tsx script.mts` on Node.js 18 or later. In a new npm project, `tsx` compiles a plain `.ts` file as CommonJS, where top-level `await` does not work.

## Read a native balance

The native balance is the amount of SEI that an address holds.

<CodeGroup>
  ```ts viem theme={"dark"}
  import { createPublicClient, http, formatEther } from 'viem';
  import { sei } from 'viem/chains';

  const client = createPublicClient({ chain: sei, transport: http() });

  // Replace with any Sei EVM address
  const address = '0x0000000000000000000000000000000000000000';

  const wei = await client.getBalance({ address });
  console.log(`${formatEther(wei)} SEI`);
  ```

  ```ts ethers theme={"dark"}
  import { ethers } from 'ethers';

  const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com');

  // Replace with any Sei EVM address
  const address = '0x0000000000000000000000000000000000000000';

  const wei = await provider.getBalance(address);
  console.log(`${ethers.formatEther(wei)} SEI`);
  ```

  ```python web3.py theme={"dark"}
  from web3 import Web3

  w3 = Web3(Web3.HTTPProvider("https://evm-rpc.sei-apis.com"))

  # Replace with any Sei EVM address
  address = Web3.to_checksum_address("0x0000000000000000000000000000000000000000")

  wei = w3.eth.get_balance(address)
  print(f"{w3.from_wei(wei, 'ether')} SEI")
  ```
</CodeGroup>

**You are done when you see:**

```text theme={"dark"}
1384.115892177302953913 SEI
```

The number is illustrative. Each library returns the balance in wei: a `bigint` in viem and ethers, and an `int` in web3.py. The `formatEther` and `from_wei(…, 'ether')` helpers divide by 10<sup>18</sup>. They carry Ethereum names, but they apply to SEI because SEI also uses 18 decimals on the EVM.

<Note>EVM RPC methods take `0x` addresses. If you have a `sei1…` address, see [Accounts](/learn/accounts) to find the EVM address of the same account. web3.py also requires the checksummed form of an address, which `Web3.to_checksum_address` produces from a lowercase one.</Note>

## Read an ERC-20 balance

The token contract stores the token balances, so you call its `balanceOf` function. Then you scale the result by the token's `decimals`. This example reads USDC on Sei Mainnet at `0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392`, which uses 6 decimals.

<CodeGroup>
  ```ts viem theme={"dark"}
  import { createPublicClient, http, parseAbi, formatUnits } from 'viem';
  import { sei } from 'viem/chains';

  const client = createPublicClient({ chain: sei, transport: http() });

  const USDC = '0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392';
  const address = '0x0000000000000000000000000000000000000000';
  const abi = parseAbi([
    'function balanceOf(address owner) view returns (uint256)',
    'function decimals() view returns (uint8)',
  ]);

  const [balance, decimals] = await Promise.all([
    client.readContract({ address: USDC, abi, functionName: 'balanceOf', args: [address] }),
    client.readContract({ address: USDC, abi, functionName: 'decimals' }),
  ]);
  console.log(`${formatUnits(balance, decimals)} USDC`);
  ```

  ```ts ethers theme={"dark"}
  import { ethers } from 'ethers';

  const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com');

  const USDC = '0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392';
  const address = '0x0000000000000000000000000000000000000000';
  const usdc = new ethers.Contract(
    USDC,
    ['function balanceOf(address owner) view returns (uint256)', 'function decimals() view returns (uint8)'],
    provider
  );

  const [balance, decimals] = await Promise.all([usdc.balanceOf(address), usdc.decimals()]);
  console.log(`${ethers.formatUnits(balance, decimals)} USDC`);
  ```

  ```python web3.py theme={"dark"}
  from decimal import Decimal
  from web3 import Web3

  w3 = Web3(Web3.HTTPProvider("https://evm-rpc.sei-apis.com"))

  USDC = "0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392"
  address = Web3.to_checksum_address("0x0000000000000000000000000000000000000000")
  abi = [
      {"type": "function", "name": "balanceOf", "stateMutability": "view",
       "inputs": [{"name": "owner", "type": "address"}], "outputs": [{"name": "", "type": "uint256"}]},
      {"type": "function", "name": "decimals", "stateMutability": "view",
       "inputs": [], "outputs": [{"name": "", "type": "uint8"}]},
  ]
  usdc = w3.eth.contract(address=USDC, abi=abi)

  balance = usdc.functions.balanceOf(address).call()
  decimals = usdc.functions.decimals().call()
  print(f"{Decimal(balance) / 10**decimals} USDC")
  ```
</CodeGroup>

The zero address holds no USDC, so the example prints `0 USDC`. ethers prints `0.0 USDC`, because `formatUnits` in ethers always includes a decimal place. To see a real balance, replace `address` with your own address.

## Read many balances at once

Each read above is a separate RPC request. To read dozens of balances in one request, batch them with [Multicall3](/evm/evm-parity/examples/multicall). Multicall3 is deployed at `0xcA11bde05977b3631167028862bE2a173976CA11` on Sei Mainnet and Sei Testnet.

## Related recipes

* [Send SEI](/evm/cookbook/send-sei): move SEI to another address
* [ERC-20 interaction](/evm/evm-parity/examples/erc20): token metadata, transfers, and approvals
* [Multicall](/evm/evm-parity/examples/multicall): batch many reads into one request


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.