Content Hub
Canton NetworkApril 2026 · 8 min read

How to Query Canton Coin Balances on Canton Network

A practical guide to retrieving party balances — what makes it different from other chains, why the naive approach breaks, and how to get it right.

Martín Alejandro Mednik
Martín Alejandro MednikEngineering, Noves

Canton Balances Are Not What You Expect

If you’re coming from EVM-land, querying a balance is a one-liner. Call balanceOf, get a number, move on.

Canton Network doesn’t work that way.

Canton uses a UTXO-like model based on Daml contracts. There’s no single “balance” field stored anywhere. A party’s Canton Coin (CC) balance is the sum of all active Holding contracts where that party is the owner. Each Holding contract represents a discrete unit of value — similar to how Bitcoin UTXOs work, but expressed as Daml smart contracts on the ledger.

This design is fundamental to Canton’s privacy architecture. Contracts are only visible to their stakeholders, which means there’s no global state to scan and no public balanceOf function to call. It also means that querying balances requires a different mental model than what most blockchain developers are used to.


The Raw Approach: Querying the Ledger API Directly

Canton exposes balance-related data through its Ledger API, primarily via the Active Contract Set (ACS). If you want a party’s balance the “native” way, here’s what’s involved:

Using /v0/holdings/summary

The simplest built-in option. You call the summary endpoint for a party and get an aggregated view. The problem? It doesn’t support pagination.The ACS snapshot query has a hard limit of 1,000 results. If a party has more than 1,000 active contracts — which is common for active validators or high-throughput participants — you’ll hit this:

JSON
{
  "error": "Size of the result exceeded the limit in queryAcsSnapshot. Result size: 1001. Limit: 1000"
}
{
  "error": "Size of the result exceeded the limit in queryAcsSnapshot. Result size: 1001. Limit: 1000"
}

No partial result. No offset parameter. Just an error.

Using /v1/holdings/state with Pagination

The recommended workaround is to use the paginated holdings state endpoint. This works, but it shifts the complexity to you:

  1. Call /v1/holdings/state with pagination parameters.
  2. Iterate through all pages until exhausted.
  3. Filter results client-side by party (the endpoint returns all holdings, not just yours).
  4. Sum the amounts across all matching Holding contracts.
  5. If you care about locked vs. unlocked — separate them by contract type and sum each category independently.

For a single balance query, that’s a pagination loop, a filter, and an aggregation. For a dashboard that tracks multiple parties, multiply that by N.

What About UTXO Merging?

You might see advice in Canton forums to “merge your UTXOs as a matter of course.” This is good hygiene — consolidating many small Holding contracts into fewer large ones keeps things manageable and avoids the 1,000-result cap. But it’s not always under your control (you can’t merge other parties’ UTXOs), and it doesn’t help when you’re queryinga party that hasn’t merged.


Understanding the Balance Model

Before jumping into tools, it’s worth understanding what a “balance” actually means on Canton:

Unlocked vs. Locked

CC can exist in two states:

  • Unlocked — Freely transferable. These are standard Holding contracts that the party can send, merge, or split at will.
  • Locked — Held in a contract that restricts transfer. This happens during staking, vesting schedules, escrow arrangements, or any Daml workflow that places a lock on the holdings.

The total balanceis the sum of both. For most operational purposes, you care about the unlocked balance (what’s actually available to move), but for reporting and compliance you need both.

Contract Counts

Because Canton uses discrete contracts rather than a single balance register, the number of contracts matters too. A party might hold 50,000 CC across a single contract, or the same amount split across 500 contracts. The total is the same, but the operational characteristics differ — more contracts means more data to process, higher pagination risk, and a stronger case for periodic merging.

Decay (Advanced)

Some Canton economic mechanisms involve round-based decay, where the effective value of holdings decreases over time unless refreshed. If your use case involves validator rewards or specific DeFi protocols, decay-adjusted balances can differ from the nominal amounts.


A Simpler Path: Using the Noves Public Data API

Now that you understand the underlying model, let’s look at an approach that abstracts away the ACS complexity.

The Noves Canton Public Data API indexes publicly observable Canton Network data and exposes it through a standard REST interface. No Canton node required, no Ledger API pagination, no client-side aggregation.

To get started, register at app.noves.fi for a free API key.

Current Balance

BASH
curl "https://api.canton.noves.fi/canton/balances/PARTY_ID" \
  --header "apiKey: YOUR_API_KEY"
curl "https://api.canton.noves.fi/canton/balances/PARTY_ID" \
  --header "apiKey: YOUR_API_KEY"

That’s it. Here’s a real response:

JSON
{
  "timestamp": "2026-04-15T12:32:45.920325+00:00",
  "balances": [
    {
      "balance": "56797.9929778898",
      "usdValue": "8487.9488686088296018",
      "token": {
        "symbol": "CC",
        "name": "Canton Coin",
        "decimals": 10,
        "address": "CC",
        "price": "0.149441"
      }
    }
  ]
}
{
  "timestamp": "2026-04-15T12:32:45.920325+00:00",
  "balances": [
    {
      "balance": "56797.9929778898",
      "usdValue": "8487.9488686088296018",
      "token": {
        "symbol": "CC",
        "name": "Canton Coin",
        "decimals": 10,
        "address": "CC",
        "price": "0.149441"
      }
    }
  ]
}

You get the CC balance, its USD valuation at the current market price, and token metadata — all in a single request. The API handles the ACS aggregation, contract summing, and pricing behind the scenes.

Daily Balance History

BASH
curl "https://api.canton.noves.fi/canton/balances/PARTY_ID/history" \
  --header "apiKey: YOUR_API_KEY"
curl "https://api.canton.noves.fi/canton/balances/PARTY_ID/history" \
  --header "apiKey: YOUR_API_KEY"

Returns end-of-day snapshots going back 30 days:

JSON
{
  "party": "noves-validator-1::1220dd5cc6e969cd7d7c11e339fbd07fab5f6fa1f999dc09339228d17299d9d68941",
  "snapshots": [
    { "date": "2026-04-14", "balance": "59603.0546152449" },
    { "date": "2026-04-13", "balance": "64692.4616717781" },
    { "date": "2026-04-12", "balance": "69859.8306650733" },
    { "date": "2026-04-11", "balance": "75756.9635662133" },
    { "date": "2026-04-10", "balance": "80622.7154832230" }
  ]
}
{
  "party": "noves-validator-1::1220dd5cc6e969cd7d7c11e339fbd07fab5f6fa1f999dc09339228d17299d9d68941",
  "snapshots": [
    { "date": "2026-04-14", "balance": "59603.0546152449" },
    { "date": "2026-04-13", "balance": "64692.4616717781" },
    { "date": "2026-04-12", "balance": "69859.8306650733" },
    { "date": "2026-04-11", "balance": "75756.9635662133" },
    { "date": "2026-04-10", "balance": "80622.7154832230" }
  ]
}

This is useful for trend analysis, reporting, and monitoring. You can see at a glance how a party’s holdings are evolving — whether rewards are accumulating, decay is taking effect, or large transfers have occurred.


Going Deeper: Private Data with the Noves Data App

The public API covers publicly observable activity. But Canton’s core value proposition is privacy — the most important data is visible only to the parties involved in a contract.

If you’re a Canton participant and you need visibility into your own private activity, the Noves Data App is a self-hosted application that runs inside your environment, connected to your participant node. It indexes everything you’re entitled to see — both public and private — and exposes it through a local API (with full OpenAPI docs at /docs) and a web-based explorer UI.

The Data App is open source and available at github.com/Noves-Inc/canton-data-app.

Here’s what the local API adds on top of what the public API offers:

Detailed CC Balances

BASH
curl "https://YOUR_HOST/api/v2/parties/PARTY_ID/balances" \
  --header "jwt: YOUR_JWT_TOKEN"
curl "https://YOUR_HOST/api/v2/parties/PARTY_ID/balances" \
  --header "jwt: YOUR_JWT_TOKEN"
JSON
{
  "partyId": "noves-validator-1::1220dd5cc6e969cd7d7c11e339fbd07fab5f6fa1f999dc09339228d17299d9d68941",
  "unlockedBalance": "56697.6189172238",
  "lockedBalance": "0",
  "totalBalance": "56697.6189172238",
  "source": "cache+delta",
  "unlockedContractCount": 1,
  "lockedContractCount": 0,
  "totalContractCount": 1
}
{
  "partyId": "noves-validator-1::1220dd5cc6e969cd7d7c11e339fbd07fab5f6fa1f999dc09339228d17299d9d68941",
  "unlockedBalance": "56697.6189172238",
  "lockedBalance": "0",
  "totalBalance": "56697.6189172238",
  "source": "cache+delta",
  "unlockedContractCount": 1,
  "lockedContractCount": 0,
  "totalContractCount": 1
}

The key differences from the public API response: you get the locked/unlocked breakdown, contract counts, and a source field that tells you how the balance was computed (cache, cache+delta, or recalculated).

Optional query parameters useCache and useDelta let you control the speed-vs-freshness tradeoff — useful in operational contexts where you need guaranteed-fresh data and are willing to wait for a full ACS scan.

Point-in-Time Historical Balances

BASH
curl "https://YOUR_HOST/api/v2/parties/PARTY_ID/balances/as-of?asOf=2026-01-01T00:00:00Z" \
  --header "jwt: YOUR_JWT_TOKEN"
curl "https://YOUR_HOST/api/v2/parties/PARTY_ID/balances/as-of?asOf=2026-01-01T00:00:00Z" \
  --header "jwt: YOUR_JWT_TOKEN"

Returns the balance as of a specific datetime. The response follows the same structure as the current balance endpoint. This is essential for audit and compliance workflows where you need to prove holdings at a specific moment — not just end-of-day approximations.

Token Balances (Non-CC Instruments)

Canton isn’t just Canton Coin. As more instruments are deployed on the network (tokenized assets, stablecoins, synthetic instruments), parties will hold multiple asset types. The Data App tracks them all:

BASH
curl "https://YOUR_HOST/api/v2/parties/PARTY_ID/token-balances" \
  --header "jwt: YOUR_JWT_TOKEN"
curl "https://YOUR_HOST/api/v2/parties/PARTY_ID/token-balances" \
  --header "jwt: YOUR_JWT_TOKEN"
JSON
{
  "party": "noves-validator-1::1220dd5cc6e969cd7d7c11e339fbd07fab5f6fa1f999dc09339228d17299d9d68941",
  "balances": [
    {
      "tokenId": "CBTC",
      "registrar": "cbtc-network::12205af3b949a04776fc48cdcc05a060f6bda2e470632935f375d1049a8546a3b262",
      "balance": "0.0001404904",
      "holdingCount": 1
    }
  ]
}
{
  "party": "noves-validator-1::1220dd5cc6e969cd7d7c11e339fbd07fab5f6fa1f999dc09339228d17299d9d68941",
  "balances": [
    {
      "tokenId": "CBTC",
      "registrar": "cbtc-network::12205af3b949a04776fc48cdcc05a060f6bda2e470632935f375d1049a8546a3b262",
      "balance": "0.0001404904",
      "holdingCount": 1
    }
  ]
}

You can also drill into a specific token to see the individual Holding contracts that compose the balance:

BASH
curl "https://YOUR_HOST/api/v2/parties/PARTY_ID/token-balances/CBTC" \
  --header "jwt: YOUR_JWT_TOKEN"
curl "https://YOUR_HOST/api/v2/parties/PARTY_ID/token-balances/CBTC" \
  --header "jwt: YOUR_JWT_TOKEN"

This gives you the full contract-level detail — useful when debugging transfers, planning UTXO merges, or auditing specific holdings.

Cache Management

If you suspect your cached balance is stale (e.g., after a reclassification or manual DB fix), you can force a full recalculation:

BASH
curl -X POST "https://YOUR_HOST/api/v2/admin/balance-cache/PARTY_ID/recalculate" \
  --header "jwt: YOUR_JWT_TOKEN"
curl -X POST "https://YOUR_HOST/api/v2/admin/balance-cache/PARTY_ID/recalculate" \
  --header "jwt: YOUR_JWT_TOKEN"

Choosing the Right Approach

The best tool depends on your context:

You’re building a quick integration or dashboard and just need current balances and trends for publicly visible parties → the Public Data API gets you there in minutes with zero infrastructure.

You’re a Canton participant running a node and need full visibility into your own activity — locked/unlocked breakdowns, private token holdings, point-in-time queries for audit → the Data App gives you the complete picture, running entirely within your infrastructure.

You need both→ they’re complementary. Use the public API for external lookups and USD pricing. Use the Data App for internal operational data. They share the same underlying data philosophy — clean, pre-aggregated, developer-friendly JSON instead of raw ACS parsing.


Resources