Skip to content

Querying Currency Supply
BEGINNER⚓︎

Exchanges and market data aggregators need accurate supply figures to display market capitalization and token metrics.

The Symbol network exposes the maximum, total, and circulating supply of XYM, the native currency, through dedicated REST endpoints.

This tutorial shows how to query each value and derive additional metrics from them.

Prerequisites⚓︎

This tutorial uses the Symbol REST API without requiring an SDK. You only need a way to make HTTP requests.

Full Code⚓︎

import os
import urllib.request

NODE_URL = os.getenv('NODE_URL', 'https://reference.symboltest.net:3001')
print(f'Using node {NODE_URL}')

SUPPLY_URL = f'{NODE_URL}/network/currency/supply'

try:
    with urllib.request.urlopen(f'{SUPPLY_URL}/max') as response:
        maximum_supply = float(response.read().decode().strip())
    print(f'Maximum supply: {maximum_supply:,.6f} XYM')

    with urllib.request.urlopen(f'{SUPPLY_URL}/total') as response:
        total_supply = float(response.read().decode().strip())
    print(f'Total supply: {total_supply:,.6f} XYM')

    with urllib.request.urlopen(f'{SUPPLY_URL}/circulating') as response:
        circulating_supply = float(response.read().decode().strip())
    print(f'Circulating supply: {circulating_supply:,.6f} XYM')

    non_circulating_supply = total_supply - circulating_supply
    print(f'Non-circulating supply: {non_circulating_supply:,.6f} XYM')

    unminted_supply = maximum_supply - total_supply
    print(f'Unminted supply: {unminted_supply:,.6f} XYM')

except Exception as error:
    print(error)

Download source

const NODE_URL = process.env.NODE_URL ||
    'https://reference.symboltest.net:3001';
console.log(`Using node ${NODE_URL}`);

const SUPPLY_PATH = '/network/currency/supply';

try {
    const fmt = v =>
        v.toLocaleString('en-US', { minimumFractionDigits: 6 });

    const maximumResponse = await fetch(`${NODE_URL}${SUPPLY_PATH}/max`);
    const maximumSupply = parseFloat((await maximumResponse.text()).trim());
    console.log(`Maximum supply: ${fmt(maximumSupply)} XYM`);

    const totalResponse = await fetch(`${NODE_URL}${SUPPLY_PATH}/total`);
    const totalSupply = parseFloat((await totalResponse.text()).trim());
    console.log(`Total supply: ${fmt(totalSupply)} XYM`);

    const circulatingResponse =
        await fetch(`${NODE_URL}${SUPPLY_PATH}/circulating`);
    const circulatingSupply =
        parseFloat((await circulatingResponse.text()).trim());
    console.log(`Circulating supply: ${fmt(circulatingSupply)} XYM`);

    const nonCirculatingSupply = totalSupply - circulatingSupply;
    console.log(`Non-circulating supply: ${fmt(nonCirculatingSupply)} XYM`);

    const unmintedSupply = maximumSupply - totalSupply;
    console.log(`Unminted supply: ${fmt(unmintedSupply)} XYM`);
} catch (error) {
    console.log(error);
}

Download source

The snippet uses the NODE_URL environment variable to set the Symbol API node. If no value is provided, a default testnet node is used.

Default node is testnet

The default node points to testnet. For production supply data, set NODE_URL to a mainnet node. For a list of available mainnet nodes, see symbol.fyi/nodes.

Code Explanation⚓︎

Fetching Supply Values⚓︎

    with urllib.request.urlopen(f'{SUPPLY_URL}/max') as response:
        maximum_supply = float(response.read().decode().strip())
    print(f'Maximum supply: {maximum_supply:,.6f} XYM')

    with urllib.request.urlopen(f'{SUPPLY_URL}/total') as response:
        total_supply = float(response.read().decode().strip())
    print(f'Total supply: {total_supply:,.6f} XYM')

    with urllib.request.urlopen(f'{SUPPLY_URL}/circulating') as response:
        circulating_supply = float(response.read().decode().strip())
    print(f'Circulating supply: {circulating_supply:,.6f} XYM')
    const fmt = v =>
        v.toLocaleString('en-US', { minimumFractionDigits: 6 });

    const maximumResponse = await fetch(`${NODE_URL}${SUPPLY_PATH}/max`);
    const maximumSupply = parseFloat((await maximumResponse.text()).trim());
    console.log(`Maximum supply: ${fmt(maximumSupply)} XYM`);

    const totalResponse = await fetch(`${NODE_URL}${SUPPLY_PATH}/total`);
    const totalSupply = parseFloat((await totalResponse.text()).trim());
    console.log(`Total supply: ${fmt(totalSupply)} XYM`);

    const circulatingResponse =
        await fetch(`${NODE_URL}${SUPPLY_PATH}/circulating`);
    const circulatingSupply =
        parseFloat((await circulatingResponse.text()).trim());
    console.log(`Circulating supply: ${fmt(circulatingSupply)} XYM`);

Each supply value is available through a dedicated endpoint:

All three endpoints return a plain-text number (not JSON), already expressed in whole units with decimal places (e.g. 8999999999.000000), not in atomic units.

Circulating supply is node-dependent

The list of non-circulating accounts is configured by each node operator (in the node's rest.json file), so different nodes could report different circulating supply values. If you are integrating supply data, ensure you query a trusted node with the default configuration.

Deriving Additional Metrics⚓︎

    non_circulating_supply = total_supply - circulating_supply
    print(f'Non-circulating supply: {non_circulating_supply:,.6f} XYM')

    unminted_supply = maximum_supply - total_supply
    print(f'Unminted supply: {unminted_supply:,.6f} XYM')
    const nonCirculatingSupply = totalSupply - circulatingSupply;
    console.log(`Non-circulating supply: ${fmt(nonCirculatingSupply)} XYM`);

    const unmintedSupply = maximumSupply - totalSupply;
    console.log(`Unminted supply: ${fmt(unmintedSupply)} XYM`);

After fetching all three values, the code derives two additional metrics:

  • Non-circulating: The difference between total and circulating supply.
  • Unminted: The difference between maximum and total supply, representing the XYM that remains to be minted.

Output⚓︎

The following output shows a typical run querying the currency supply:

1
2
3
4
5
6
Using node https://reference.symboltest.net:3001
Maximum supply: 8,999,999,999.000000 XYM
Total supply: 8,323,505,878.695894 XYM
Circulating supply: 8,323,495,854.693871 XYM
Non-circulating supply: 10,024.002024 XYM
Unminted supply: 676,494,120.304106 XYM

These values come from a testnet node and do not reflect mainnet supply figures.

The output shows the full breakdown of the XYM supply:

  • Maximum supply (line 2): The hard cap for XYM.
  • Total supply (line 3): Lower than the maximum, because not all XYM has been minted yet.
  • Circulating supply (line 4): Lower still, because some minted XYM is held by non-circulating accounts.
  • Non-circulating supply (line 5): The difference between total and circulating supply.
  • Unminted supply (line 6): The remaining XYM that will be gradually minted through inflation rewards.

Conclusion⚓︎

This tutorial showed how to:

Step Related documentation
Fetch maximum supply /network/currency/supply/max GET
Fetch total supply /network/currency/supply/total GET
Fetch circulating supply /network/currency/supply/circulating GET
Derive additional metrics -

Next steps⚓︎

To check a specific account's XYM balance, see the Query Account Balance tutorial.