Privacy Pool data API
Read finalized public-flow amounts without inferring private ownership.
Privacy Pool data API
Starkscan exposes beta Privacy Pool routes for public activity, note evidence, prepared metrics, and finalized public-flow amounts. They do not expose note ownership, link a deposit to a withdrawal, calculate an anonymity set, claim a user's balance, or report a pool custody balance.
Use the external API host and a Starkscan read key. Do not use the explorer's
same-origin /v1/* lane from an external integration.
export STARKSCAN_BASE_URL=https://api.starkscan.co
export STARKSCAN_API_KEY=<your-read-key>
export STARKSCAN_CHAIN=SN_MAINThe supported external contract
| Need | Call | Use it for |
|---|---|---|
| Readiness and freshness | GET /v1/{chain}/privacy-pool/status | Decide whether indexed and decoded public facts are ready before publishing metrics. |
| Daily activity buckets | GET /v1/{chain}/privacy-pool/metric-buckets | Bounded daily deposits, withdrawals, and note activity for 1–90 days. |
| Prepared chart series | GET /v1/{chain}/privacy-pool/metrics/series | Viewing-key, tracked-supply, and fee series with explicit range and granularity. |
| Aggregate activity | GET /v1/{chain}/privacy-pool/analytics | Prepared public activity and observable-flow summaries, including explicit unavailable states. |
| Public event evidence | GET /v1/{chain}/privacy-pool/events | Cursor-paginated decoded events plus raw keys and data. |
| Note-state evidence | GET /v1/{chain}/privacy-pool/commitments, GET /v1/{chain}/privacy-pool/nullifiers | Public commitment and nullifier facts without inferring ownership or spend links. |
| Current public-flow snapshot | GET /v1/{chain}/privacy-pool/tvl | Exact per-token finalized public-flow amounts at one Starkscan indexed-finality snapshot. |
| Hourly prepared points | GET /v1/{chain}/privacy-pool/tvl/series | Existing prepared UTC-hour public-flow points; not a certified continuous history. |
These routes are bounded indexed or materialized reads: they do not scan full event history, call Starknet RPC, call another explorer, or fetch a price on the request path. For activity and evidence recipes, response gating, and cursor handling, see Privacy Pool metrics and note evidence.
Current public-flow snapshot
curl --fail-with-body \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"$STARKSCAN_BASE_URL/v1/$STARKSCAN_CHAIN/privacy-pool/tvl"This is an illustrative response with one asset shown. The live schema is in the public OpenAPI contract.
{
"schemaVersion": "1",
"chainId": "SN_MAIN",
"scope": "strk20_privacy_pool",
"status": "complete",
"accountingMethod": "finalized_public_flow_ledger_v1",
"asOf": {
"blockNumber": 12345678,
"blockHash": "0x...",
"blockTimestamp": "2026-07-27T00:00:00Z",
"materializedAt": "2026-07-27T00:01:00Z"
},
"coverage": {
"status": "complete",
"finalizedOnly": true,
"finalityBasis": "starkscan_indexed_finalized_tier",
"asOfL1Accepted": false,
"missingAmountEventCount": 0,
"decodedMaterializationFresh": true
},
"assets": [{
"status": "complete",
"reasonCode": "finalized_public_flow_ledger",
"token": { "address": "0x...", "symbol": "USDC", "decimals": 6 },
"protectedAmountRaw": "175093155774",
"protectedAmount": "175093.155774",
"missingAmountEventCount": 0
}]
}The accounting rule per token is:
protectedAmountRaw = depositAmountRaw - withdrawalAmountRawprotectedAmountRaw is the schema-v1 field name for the authoritative
base-10 finalized public-flow amount. Keep it as an arbitrary-precision
integer end to end; never parse it as a JavaScript number.
protectedAmount is only a convenience decimal rendering. Neither field is a
wallet balance, pool custody balance, or claim of TVL.
Schema v1 may include legacy valuation, price, valueUsd, and totalUsd
compatibility fields. They are not a supported pricing service or accounting
input for a new integration. Use token address, decimals, and raw public-flow
amounts as the integration inputs.
Publish an automated snapshot only when top-level status and
coverage.status are complete, coverage.missingAmountEventCount is 0,
coverage.decodedMaterializationFresh is true, and every consumed asset is
complete with no missing amount events.
finalizedOnly: true means Starkscan's depth-confirmed indexed-finalized tier.
It is not by itself L1 settlement. Require coverage.asOfL1Accepted === true
when an L1-accepted condition is necessary.
Hourly prepared points
FROM='<RFC3339 UTC-hour timestamp>'
TO='<RFC3339 UTC-hour timestamp>'
curl --fail-with-body --get \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
--data-urlencode "from=$FROM" \
--data-urlencode "to=$TO" \
--data-urlencode 'granularity=hour' \
--data-urlencode 'limit=24' \
"$STARKSCAN_BASE_URL/v1/$STARKSCAN_CHAIN/privacy-pool/tvl/series"from and to are required RFC3339 UTC-hour timestamps. The range is
inclusive, points are oldest first, and a response is capped at 24 hours. Pass
nextCursor unchanged with the same range for the next page.
Each items[] entry has a timestamp, an asOf final block identity, a
per-token assets[] array, and its own status and
missingAmountEventCount. Consume a point only when its status is complete
and its missing amount count is 0.
The hourly route is a prepared-point read, not a continuity certificate. It
currently has no page-level coverage or freshness watermark; current-snapshot
coverage does not certify an hourly page.
Availability rule: an empty items[] page or sparse UTC-hour sequence is unavailable history.
It is not a zero-value point or complete-range assertion; fail closed. Do not
synthesize missing hours from RPC, another explorer, or request-time pricing.
{
"items": [{
"timestamp": "2026-07-26T00:00:00.000Z",
"asOf": { "blockNumber": 12345678, "blockHash": "0x..." },
"status": "complete",
"missingAmountEventCount": 0,
"assets": [{ "token": { "address": "0x..." }, "protectedAmountRaw": "175093155774" }]
}],
"nextCursor": "opaque-next-page-token-or-null"
}Pricing is outside this contract
Pricing and valuation are outside the Privacy Pool API contract. Do not use schema-v1 compatibility fields to make a USD, wallet-balance, custody, or total-value claim. A consumer that needs a valuation must apply its own address-keyed price source to raw public-flow amounts at the relevant snapshot or point timestamp.
Efficient polling
Both routes return ETag and Last-Modified. Save the ETag and send it as
If-None-Match; a 304 Not Modified has no body. An ETag only validates a
representation; it is not proof that an hourly range is complete or fresh.
Respect Cache-Control, rate-limit headers, and Retry-After.
curl --fail-with-body --silent --show-error \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
-D /tmp/starkscan-privacy-pool-tvl.headers \
-o /dev/null \
"$STARKSCAN_BASE_URL/v1/$STARKSCAN_CHAIN/privacy-pool/tvl"
etag="$(awk 'BEGIN{IGNORECASE=1} /^etag:/ {sub(/^[^:]+:[[:space:]]*/, ""); sub(/\r$/, ""); print; exit}' /tmp/starkscan-privacy-pool-tvl.headers)"
curl --fail-with-body --output /dev/null --write-out '%{http_code}\n' \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
-H "If-None-Match: $etag" \
"$STARKSCAN_BASE_URL/v1/$STARKSCAN_CHAIN/privacy-pool/tvl"Public activity routes and intentional exclusions
The public OpenAPI now includes the dedicated status, analytics, metric-bucket, metric-series, event, commitment, and nullifier routes below. They remain beta: consume their declared status, coverage, visibility, and cursor fields rather than copying assumptions from the website.
| Call | Appropriate use | Boundary |
|---|---|---|
GET /v1/{chain}/privacy-pool/status | Health and decoded-event freshness. | Counters are not ownership, balances, or anonymity k. |
GET /v1/{chain}/privacy-pool/events | Cursor-paginated public event feed. Optional query: event, cursor, limit (1–100). | Fields can be partial or hidden_by_design; raw keys and data remain audit evidence. |
GET /v1/{chain}/privacy-pool/commitments | Public commitment facts. Optional query: pool, cursor, limit (1–100). | Never infer note ownership or a spend link. |
GET /v1/{chain}/privacy-pool/nullifiers | Public nullifier facts. Optional query: pool, cursor, limit (1–100). | Never infer which commitment was spent. |
GET /v1/{chain}/privacy-pool/analytics | Prepared aggregate counts and observable flows. | Analytics is not the canonical public-flow ledger. |
GET /v1/{chain}/privacy-pool/metric-buckets | Prepared daily chart buckets. Optional query: limit (1–90). | Product metrics, not accounting history. |
GET /v1/{chain}/privacy-pool/metrics/series | Prepared viewing-key, tracked supply, and fee series. Optional query: range, granularity, token. | Defaults are product-oriented; specify a token when needed. |
GET /v1/{chain}/privacy-pool/dashboard remains an explorer composition route;
use the dedicated public routes for durable integrations. Merkle-root facts are
not published as an external route while the materialized dataset has no facts.
For events, commitments, nullifiers, and roots, preserve nextCursor exactly;
do not construct a cursor from a block number. The events API supports only the
server-side event filter. Explorer UI filters for contract, transaction, and
block are not server-side API filters.
Integration rules
- Treat token contract address as identity; a symbol is display metadata.
- Keep raw amounts as strings and apply token decimals using arbitrary-precision arithmetic.
- Store response block/hash/timestamp, schema version, and accounting method with each consumed point.
- Fail closed on
degraded,unavailable, empty, or sparse history; do not fill a gap from request-time RPC, another explorer, or a request-time price lookup. - Do not label a finalized public-flow amount as a wallet balance, pool custody balance, or TVL.
- Use the API reference and
starkscan-openapi.yamlas the authoritative schema, stability, and error contract for every external endpoint.