Skip to content

Querying an Account Balance⚓︎

BEGINNER

Accounts on NEM can hold mosaics (fungible tokens), including the native currency XEM.

This tutorial shows how to query an account's mosaic balances and display NEM's whole-number atomic amounts in decimal form.

Prerequisites⚓︎

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

Full Code⚓︎

The following is the complete code listing for this tutorial. A detailed, step-by-step explanation follows in the next section.

import json
import os
import urllib.request

NODE_URL = os.getenv('NODE_URL', 'http://libertalia.nemtest.net:7890')
print(f'Using node {NODE_URL}')


def get_mosaic_balances(address):
    """
    Fetch all mosaic balances owned by an account.

    Args:
        address: The account address

    Returns:
        List of mosaics, each with a structured mosaicId and quantity
    """
    balances_path = f'/account/mosaic/owned?address={address}'
    with urllib.request.urlopen(f'{NODE_URL}{balances_path}') as response:
        balances_info = json.loads(response.read().decode())
        return balances_info['data']


def get_mosaic_definitions(address):
    """
    Fetch mosaic definitions for every mosaic owned by an account.

    Args:
        address: The account address

    Returns:
        Dictionary mapping "namespace:name" to the mosaic definition
    """
    definitions_path = '/account/mosaic/owned/definition'
    with urllib.request.urlopen(
        f'{NODE_URL}{definitions_path}?address={address}'
    ) as response:
        definitions_info = json.loads(response.read().decode())
        # Build a dictionary mapping "namespace:name" to its definition
        definitions_map = {}
        for entry in definitions_info['data']:
            entry_id = entry['id']
            entry_key = f'{entry_id["namespaceId"]}:{entry_id["name"]}'
            definitions_map[entry_key] = entry
        return definitions_map


def format_amount(amount, divisibility):
    """
    Format an atomic amount with decimal places.

    Args:
        amount: The atomic amount as an integer
        divisibility: Number of decimal places

    Returns:
        Formatted amount as a string
    """
    if divisibility == 0:
        return str(amount)
    whole_part = amount // (10 ** divisibility)
    fractional_part = amount % (10 ** divisibility)
    return f'{whole_part}.{fractional_part:0{divisibility}d}'


# The account address to query
ADDRESS = os.getenv('ADDRESS', 'TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP')
print(f'Fetching balances for {ADDRESS}')

try:
    # Fetch mosaic balances and definitions for the account
    account_mosaics = get_mosaic_balances(ADDRESS)
    mosaic_definitions = get_mosaic_definitions(ADDRESS)

    if not account_mosaics:
        print('Account holds no mosaics')
    else:
        print(f'Account holds {len(account_mosaics)} mosaic(s):')

        for mosaic_entry in account_mosaics:
            mosaic_id = mosaic_entry['mosaicId']
            key = f'{mosaic_id["namespaceId"]}:{mosaic_id["name"]}'
            balance = int(mosaic_entry['quantity'])

            # Get mosaic divisibility from the definition
            definition = mosaic_definitions[key]
            properties = {
                p['name']: p['value']
                for p in definition['properties']
            }
            mosaic_divisibility = int(
                properties.get('divisibility', '0'))

            # Format and display the balance
            formatted_balance = format_amount(
                balance, mosaic_divisibility)
            print(f'- Mosaic {key}')
            print(f'  Balance: {formatted_balance}')
            print(f'  Balance (atomic): {balance}')
            print(f'  Divisibility: {mosaic_divisibility}')
except Exception as error:
    print(error)

Download source

const NODE_URL = process.env.NODE_URL ||
    'http://libertalia.nemtest.net:7890';
console.log('Using node', NODE_URL);


/**
 * Fetch all mosaic balances owned by an account.
 * @param {string} address - Account address
 * @returns {Promise<object[]>} List of mosaics with id and quantity
 */
async function getMosaicBalances(address) {
    const path = `/account/mosaic/owned?address=${address}`;
    const response = await fetch(`${NODE_URL}${path}`);
    const info = await response.json();
    return info.data;
}

/**
 * Fetch mosaic definitions for every mosaic owned by an account.
 * @param {string} address - Account address
 * @returns {Promise<Map>} Map of "namespace:name" to mosaic definition
 */
async function getMosaicDefinitions(address) {
    const path = `/account/mosaic/owned/definition?address=${address}`;
    const response = await fetch(`${NODE_URL}${path}`);
    const info = await response.json();
    // Build a map from "namespace:name" to mosaic definition
    const definitionsMap = new Map();
    for (const entry of info.data) {
        const key = `${entry.id.namespaceId}:${entry.id.name}`;
        definitionsMap.set(key, entry);
    }
    return definitionsMap;
}

/**
 * Format an atomic amount with decimal places.
 * @param {bigint} amount - The atomic amount
 * @param {number} divisibility - Number of decimal places
 * @returns {string} The formatted amount
 */
function formatAmount(amount, divisibility) {
    if (0 === divisibility)
        return amount.toString();

    const divisor = 10n ** BigInt(divisibility);
    const wholePart = amount / divisor;
    const fractionalPart = amount % divisor;
    const fractionalStr = fractionalPart.toString()
        .padStart(divisibility, '0');
    return `${wholePart}.${fractionalStr}`;
}

// The account address to query
const ADDRESS = process.env.ADDRESS ||
    'TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP';
console.log('Fetching balances for', ADDRESS);

try {
    // Fetch mosaic balances and definitions for the account
    const accountMosaics = await getMosaicBalances(ADDRESS);
    const mosaicDefinitions = await getMosaicDefinitions(ADDRESS);

    if (0 === accountMosaics.length) {
        console.log('Account holds no mosaics');
    } else {
        console.log(`Account holds ${accountMosaics.length} mosaic(s):`);

        for (const mosaicEntry of accountMosaics) {
            const { mosaicId } = mosaicEntry;
            const key = `${mosaicId.namespaceId}:${mosaicId.name}`;
            const balance = BigInt(mosaicEntry.quantity);

            // Get mosaic divisibility from the definition
            const definition = mosaicDefinitions.get(key);
            const properties = Object.fromEntries(
                definition.properties.map(p => [p.name, p.value])
            );
            const divisibility = parseInt(
                properties.divisibility || '0', 10);

            // Format and display the balance
            const formattedBalance = formatAmount(balance, divisibility);
            console.log(`- Mosaic ${key}`);
            console.log(`  Balance: ${formattedBalance}`);
            console.log(`  Balance (atomic): ${balance.toString()}`);
            console.log(`  Divisibility: ${divisibility}`);
        }
    }
} catch (e) {
    console.error(e.message, '| Cause:', e.cause?.code ?? 'unknown');
}

Download source

//JAVA 21+
//DEPS org.symbol:symbol-sdk:3.3.3

import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public final class QueryBalance {
    private static final ObjectMapper JSON_MAPPER = new ObjectMapper();

    private static final HttpClient HTTP_CLIENT =
        HttpClient.newHttpClient();

    private static final String NODE_URL = System.getenv().getOrDefault(
        "NODE_URL", "http://libertalia.nemtest.net:7890");


    /**
     * Fetch all mosaic balances owned by an account.
     */
    private static JsonNode getMosaicBalances(
        final String address
    ) throws IOException, InterruptedException {
        final String path = String.format(
            "/account/mosaic/owned?address=%s", address);
        final HttpRequest request = HttpRequest.newBuilder(
            URI.create(NODE_URL + path)).GET().build();
        final HttpResponse<String> response = HTTP_CLIENT.send(
            request, BodyHandlers.ofString());
        return JSON_MAPPER.readTree(response.body()).get("data");
    }


    /**
     * Fetch mosaic definitions for every mosaic owned by an account.
     */
    private static Map<String, JsonNode> getMosaicDefinitions(
        final String address
    ) throws IOException, InterruptedException {
        final String path = String.format(
            "/account/mosaic/owned/definition?address=%s", address);
        final HttpRequest request = HttpRequest.newBuilder(
            URI.create(NODE_URL + path)).GET().build();
        final HttpResponse<String> response = HTTP_CLIENT.send(
            request, BodyHandlers.ofString());
        final JsonNode info = JSON_MAPPER.readTree(response.body());
        // Build a map from "namespace:name" to mosaic definition
        final Map<String, JsonNode> definitionsMap = new HashMap<>();
        for (final JsonNode entry : info.get("data")) {
            final JsonNode id = entry.get("id");
            final String key = String.format("%s:%s",
                id.get("namespaceId").asText(),
                id.get("name").asText());
            definitionsMap.put(key, entry);
        }
        return definitionsMap;
    }


    /**
     * Format an atomic amount with decimal places.
     */
    private static String formatAmount(
        final BigInteger amount,
        final int divisibility
    ) {
        if (0 == divisibility)
            return amount.toString();

        final BigInteger divisor = BigInteger.TEN.pow(divisibility);
        final BigInteger[] parts = amount.divideAndRemainder(divisor);
        return String.format(
            "%s.%0" + divisibility + "d", parts[0], parts[1]);
    }

    public static void main(final String[] args) {
        try {
            new QueryBalance().run();
        } catch (final Exception ex) {
            System.out.println(null == ex.getMessage()
                ? ex.toString()
                : ex.getMessage());
        }
    }

    private void run() throws IOException, InterruptedException {
        System.out.printf("Using node %s%n", NODE_URL);

        // The account address to query
        final String address = System.getenv().getOrDefault(
            "ADDRESS", "TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP");
        System.out.printf("Fetching balances for %s%n", address);

        // Fetch mosaic balances and definitions for the account
        final JsonNode accountMosaics = getMosaicBalances(address);
        final Map<String, JsonNode> mosaicDefinitions =
            getMosaicDefinitions(address);

        if (accountMosaics.isEmpty()) {
            System.out.println("Account holds no mosaics");
        } else {
            System.out.printf("Account holds %d mosaic(s):%n",
                accountMosaics.size());
            for (final JsonNode mosaicEntry : accountMosaics) {
                final JsonNode mosaicId = mosaicEntry.get("mosaicId");
                final String key = String.format("%s:%s",
                    mosaicId.get("namespaceId").asText(),
                    mosaicId.get("name").asText());
                final BigInteger balance = new BigInteger(
                    mosaicEntry.get("quantity").asText());

                // Get mosaic divisibility from the definition
                final JsonNode definition = mosaicDefinitions.get(key);
                int divisibility = 0;
                for (final JsonNode property
                    : definition.get("properties")) {
                    if ("divisibility".equals(
                        property.get("name").asText()))
                        divisibility = property.get("value").asInt();
                }

                // Format and display the balance
                final String formattedBalance = formatAmount(
                    balance, divisibility);
                System.out.printf("- Mosaic %s%n", key);
                System.out.printf("  Balance: %s%n", formattedBalance);
                System.out.printf("  Balance (atomic): %s%n", balance);
                System.out.printf("  Divisibility: %d%n", divisibility);
            }
        }
    }
}

Download source

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

The tutorial defines the following functions:

  • : Fetches all mosaics owned by an account.
  • : Fetches mosaic definitions, including divisibility.
  • : Formats amounts with the appropriate number of decimal places, according to their divisibility.

Code Explanation⚓︎

Fetching Mosaic Balances⚓︎

def get_mosaic_balances(address):
    """
    Fetch all mosaic balances owned by an account.

    Args:
        address: The account address

    Returns:
        List of mosaics, each with a structured mosaicId and quantity
    """
    balances_path = f'/account/mosaic/owned?address={address}'
    with urllib.request.urlopen(f'{NODE_URL}{balances_path}') as response:
        balances_info = json.loads(response.read().decode())
        return balances_info['data']
/**
 * Fetch all mosaic balances owned by an account.
 * @param {string} address - Account address
 * @returns {Promise<object[]>} List of mosaics with id and quantity
 */
async function getMosaicBalances(address) {
    const path = `/account/mosaic/owned?address=${address}`;
    const response = await fetch(`${NODE_URL}${path}`);
    const info = await response.json();
    return info.data;
}
    /**
     * Fetch all mosaic balances owned by an account.
     */
    private static JsonNode getMosaicBalances(
        final String address
    ) throws IOException, InterruptedException {
        final String path = String.format(
            "/account/mosaic/owned?address=%s", address);
        final HttpRequest request = HttpRequest.newBuilder(
            URI.create(NODE_URL + path)).GET().build();
        final HttpResponse<String> response = HTTP_CLIENT.send(
            request, BodyHandlers.ofString());
        return JSON_MAPPER.readTree(response.body()).get("data");
    }

The /account/mosaic/owned GET endpoint returns every mosaic the account holds, together with its quantity in atomic units.

Fetching Mosaic Definitions⚓︎

def get_mosaic_definitions(address):
    """
    Fetch mosaic definitions for every mosaic owned by an account.

    Args:
        address: The account address

    Returns:
        Dictionary mapping "namespace:name" to the mosaic definition
    """
    definitions_path = '/account/mosaic/owned/definition'
    with urllib.request.urlopen(
        f'{NODE_URL}{definitions_path}?address={address}'
    ) as response:
        definitions_info = json.loads(response.read().decode())
        # Build a dictionary mapping "namespace:name" to its definition
        definitions_map = {}
        for entry in definitions_info['data']:
            entry_id = entry['id']
            entry_key = f'{entry_id["namespaceId"]}:{entry_id["name"]}'
            definitions_map[entry_key] = entry
        return definitions_map
/**
 * Fetch mosaic definitions for every mosaic owned by an account.
 * @param {string} address - Account address
 * @returns {Promise<Map>} Map of "namespace:name" to mosaic definition
 */
async function getMosaicDefinitions(address) {
    const path = `/account/mosaic/owned/definition?address=${address}`;
    const response = await fetch(`${NODE_URL}${path}`);
    const info = await response.json();
    // Build a map from "namespace:name" to mosaic definition
    const definitionsMap = new Map();
    for (const entry of info.data) {
        const key = `${entry.id.namespaceId}:${entry.id.name}`;
        definitionsMap.set(key, entry);
    }
    return definitionsMap;
}
    /**
     * Fetch mosaic definitions for every mosaic owned by an account.
     */
    private static Map<String, JsonNode> getMosaicDefinitions(
        final String address
    ) throws IOException, InterruptedException {
        final String path = String.format(
            "/account/mosaic/owned/definition?address=%s", address);
        final HttpRequest request = HttpRequest.newBuilder(
            URI.create(NODE_URL + path)).GET().build();
        final HttpResponse<String> response = HTTP_CLIENT.send(
            request, BodyHandlers.ofString());
        final JsonNode info = JSON_MAPPER.readTree(response.body());
        // Build a map from "namespace:name" to mosaic definition
        final Map<String, JsonNode> definitionsMap = new HashMap<>();
        for (final JsonNode entry : info.get("data")) {
            final JsonNode id = entry.get("id");
            final String key = String.format("%s:%s",
                id.get("namespaceId").asText(),
                id.get("name").asText());
            definitionsMap.put(key, entry);
        }
        return definitionsMap;
    }

To format mosaic balances correctly, the snippet fetches their definitions from the network. The key property required is divisibility, which defines how many decimal places a mosaic supports.

The /account/mosaic/owned/definition GET endpoint returns the definition for every mosaic owned by the account in a single request, including divisibility and other properties.

Formatting Amounts⚓︎

def format_amount(amount, divisibility):
    """
    Format an atomic amount with decimal places.

    Args:
        amount: The atomic amount as an integer
        divisibility: Number of decimal places

    Returns:
        Formatted amount as a string
    """
    if divisibility == 0:
        return str(amount)
    whole_part = amount // (10 ** divisibility)
    fractional_part = amount % (10 ** divisibility)
    return f'{whole_part}.{fractional_part:0{divisibility}d}'
/**
 * Format an atomic amount with decimal places.
 * @param {bigint} amount - The atomic amount
 * @param {number} divisibility - Number of decimal places
 * @returns {string} The formatted amount
 */
function formatAmount(amount, divisibility) {
    if (0 === divisibility)
        return amount.toString();

    const divisor = 10n ** BigInt(divisibility);
    const wholePart = amount / divisor;
    const fractionalPart = amount % divisor;
    const fractionalStr = fractionalPart.toString()
        .padStart(divisibility, '0');
    return `${wholePart}.${fractionalStr}`;
}
    /**
     * Format an atomic amount with decimal places.
     */
    private static String formatAmount(
        final BigInteger amount,
        final int divisibility
    ) {
        if (0 == divisibility)
            return amount.toString();

        final BigInteger divisor = BigInteger.TEN.pow(divisibility);
        final BigInteger[] parts = amount.divideAndRemainder(divisor);
        return String.format(
            "%s.%0" + divisibility + "d", parts[0], parts[1]);
    }

This utility function converts atomic amounts into human-friendly representations:

  • Atomic amount: The raw value stored on the blockchain, expressed as an integer.
  • Formatted amount: The display format with decimal places determined by the mosaic's divisibility.

The formatting splits the atomic amount into whole and fractional parts by dividing and taking the remainder with respect to \(10^{\text{divisibility}}\). The fractional part is then zero-padded to ensure it always displays the correct number of decimal places.

Putting It All Together⚓︎

# The account address to query
ADDRESS = os.getenv('ADDRESS', 'TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP')
print(f'Fetching balances for {ADDRESS}')

try:
    # Fetch mosaic balances and definitions for the account
    account_mosaics = get_mosaic_balances(ADDRESS)
    mosaic_definitions = get_mosaic_definitions(ADDRESS)

    if not account_mosaics:
        print('Account holds no mosaics')
    else:
        print(f'Account holds {len(account_mosaics)} mosaic(s):')

        for mosaic_entry in account_mosaics:
            mosaic_id = mosaic_entry['mosaicId']
            key = f'{mosaic_id["namespaceId"]}:{mosaic_id["name"]}'
            balance = int(mosaic_entry['quantity'])

            # Get mosaic divisibility from the definition
            definition = mosaic_definitions[key]
            properties = {
                p['name']: p['value']
                for p in definition['properties']
            }
            mosaic_divisibility = int(
                properties.get('divisibility', '0'))

            # Format and display the balance
            formatted_balance = format_amount(
                balance, mosaic_divisibility)
            print(f'- Mosaic {key}')
            print(f'  Balance: {formatted_balance}')
            print(f'  Balance (atomic): {balance}')
            print(f'  Divisibility: {mosaic_divisibility}')
except Exception as error:
    print(error)
// The account address to query
const ADDRESS = process.env.ADDRESS ||
    'TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP';
console.log('Fetching balances for', ADDRESS);

try {
    // Fetch mosaic balances and definitions for the account
    const accountMosaics = await getMosaicBalances(ADDRESS);
    const mosaicDefinitions = await getMosaicDefinitions(ADDRESS);

    if (0 === accountMosaics.length) {
        console.log('Account holds no mosaics');
    } else {
        console.log(`Account holds ${accountMosaics.length} mosaic(s):`);

        for (const mosaicEntry of accountMosaics) {
            const { mosaicId } = mosaicEntry;
            const key = `${mosaicId.namespaceId}:${mosaicId.name}`;
            const balance = BigInt(mosaicEntry.quantity);

            // Get mosaic divisibility from the definition
            const definition = mosaicDefinitions.get(key);
            const properties = Object.fromEntries(
                definition.properties.map(p => [p.name, p.value])
            );
            const divisibility = parseInt(
                properties.divisibility || '0', 10);

            // Format and display the balance
            const formattedBalance = formatAmount(balance, divisibility);
            console.log(`- Mosaic ${key}`);
            console.log(`  Balance: ${formattedBalance}`);
            console.log(`  Balance (atomic): ${balance.toString()}`);
            console.log(`  Divisibility: ${divisibility}`);
        }
    }
} catch (e) {
    console.error(e.message, '| Cause:', e.cause?.code ?? 'unknown');
}
        // The account address to query
        final String address = System.getenv().getOrDefault(
            "ADDRESS", "TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP");
        System.out.printf("Fetching balances for %s%n", address);

        // Fetch mosaic balances and definitions for the account
        final JsonNode accountMosaics = getMosaicBalances(address);
        final Map<String, JsonNode> mosaicDefinitions =
            getMosaicDefinitions(address);

        if (accountMosaics.isEmpty()) {
            System.out.println("Account holds no mosaics");
        } else {
            System.out.printf("Account holds %d mosaic(s):%n",
                accountMosaics.size());
            for (final JsonNode mosaicEntry : accountMosaics) {
                final JsonNode mosaicId = mosaicEntry.get("mosaicId");
                final String key = String.format("%s:%s",
                    mosaicId.get("namespaceId").asText(),
                    mosaicId.get("name").asText());
                final BigInteger balance = new BigInteger(
                    mosaicEntry.get("quantity").asText());

                // Get mosaic divisibility from the definition
                final JsonNode definition = mosaicDefinitions.get(key);
                int divisibility = 0;
                for (final JsonNode property
                    : definition.get("properties")) {
                    if ("divisibility".equals(
                        property.get("name").asText()))
                        divisibility = property.get("value").asInt();
                }

                // Format and display the balance
                final String formattedBalance = formatAmount(
                    balance, divisibility);
                System.out.printf("- Mosaic %s%n", key);
                System.out.printf("  Balance: %s%n", formattedBalance);
                System.out.printf("  Balance (atomic): %s%n", balance);
                System.out.printf("  Divisibility: %d%n", divisibility);
            }
        }

The main code reads the ADDRESS environment variable to determine which account to query. If no value is provided, it uses a default sample address.

It orchestrates the helper functions to:

  1. Fetch the mosaic balances for the account.
  2. Retrieve the mosaic definitions to determine each mosaic's divisibility.
  3. Iterate through each mosaic and format its balance with the appropriate number of decimal places.

Output⚓︎

The output shown below corresponds to a typical run of the program.

Using node http://libertalia.nemtest.net:7890
Fetching balances for TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP
Account holds 2 mosaic(s):
- Mosaic nem:xem
  Balance: 9883.200000
  Balance (atomic): 9883200000
  Divisibility: 6
- Mosaic company:token
  Balance: 1000000
  Balance (atomic): 1000000
  Divisibility: 0

The output displays all mosaics the account holds. Notice how different mosaics have different divisibility values:

  • The first mosaic is nem:xem, the network's native currency, which has divisibility 6 and is therefore displayed with six decimal places (9883.200000).
  • The second mosaic is company:token, a user-defined mosaic with divisibility 0, displayed as an integer (1000000).

Conclusion⚓︎

This tutorial showed how to:

Step Related documentation
Fetch mosaic balances /account/mosaic/owned GET
Fetch mosaic definitions /account/mosaic/owned/definition GET