Portfolio of an address
See a portfolio on an entity page →
Everything an address holds, valued in USD: its token balances and its native ETH, each with the balance, the hourly price, the USD value and the share of the total.
GET https://tracefast.xyz/api/v1/chains/1/entities/{address}/portfolio
| Parameter | In | Type | Notes |
|---|---|---|---|
address | path | string | 0x-prefixed, 40 hex characters, any case |
block | query | integer, optional | the state at the end of this block, priced at that block's hour. Absent: the current state |
limit | query | integer, optional | 1..100, default 100 |
Access — an API key on the Intelligence plan or higher (plans), or no key at all: pay per call with x402 at the same path on https://mpp.tracefast.xyz/api/v1, at the price its 402 states. As a tool for AI agents: get_entity_portfolio on the MCP server. Stable — see stability.
chains/1 is a literal: the method serves Ethereum mainnet data. Other chain
ids answer 404 chain_not_supported. There are no other parameters — an
unknown one is refused rather than ignored.
What it covers, and what it does not
- Token balances and native ETH only. No NFTs, no DeFi positions, no liquidity-pool shares.
- Token balances are served for the covered tokens —
coveredTokenCountsays how many that is, and the set grows. A balance in a token outside it is absent from the answer, not zero. - A position is listed only when it can be valued — a USD price for the
hour and known decimals. Everything else the address holds is counted in
hiddenCountand never listed. - At a past block, rebasing tokens count too. A token whose balance grows
without transfer events — stETH and the like — enters with its
balanceOfat the end ofblock: listed when it can be valued, otherwise inhiddenCount.
Response
200 application/json, here with ?limit=2:
{
"data": {
"address": "0xf977814e90da44bfa03b6295a0616a897441acec",
"window": { "kind": "current", "asOfBlock": 26132399, "nativeFreshness": "head" },
"priceBasis": "latest_hour",
"totalUsd": 20436624609.08933,
"pricedCount": 178,
"hiddenCount": 956,
"coveredTokenCount": 393435,
"positions": [
{
"kind": "token",
"token": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"symbol": "USDT",
"decimals": 6,
"balance": "16000000001124208",
"priceUsd": 1,
"priceHour": 1791280800,
"priceCarried": false,
"valueUsd": 16000000001.124208,
"share": 0.7829081517702341,
"asOfBlock": 26132399
},
{
"kind": "native",
"symbol": "ETH",
"decimals": 18,
"balance": "519397738757924349825671",
"priceUsd": 2709.57739502,
"priceHour": 1791280800,
"priceCarried": false,
"valueUsd": 1407348371.9629753,
"share": 0.06886403204456021,
"asOfBlock": 26130116
}
]
},
"meta": {
"chain": "ethereum",
"timestamp": 1791284628440,
"cached": true,
"asOfBlock": 26132399,
"docs": "https://docs.tracefast.xyz/api/address-portfolio"
}
}With ?block=24500000 the same address answers window.kind: "block",
priceBasis: "hour_of_block", a priceHour, and the balances as they stood
then.
data
| Field | Meaning |
|---|---|
window.kind | current, or block when you asked for one |
window.asOfBlock | the block the answer is true at — the state at the end of it. With block, the requested block. In the current view, the block the token balances are valid at; it moves forward every few hours |
window.nativeFreshness | head in the current view: the native ETH balance is read up to the chain head, so it can be newer than window.asOfBlock. Absent with block, where every position is at the requested block |
priceBasis | latest_hour for the current view, hour_of_block with block |
priceHour | with hour_of_block: the start of the UTC hour every position is priced at, in Unix seconds |
totalUsd | the sum over all valued positions, not only the listed ones. Absent — never 0 — when nothing could be valued |
pricedCount | how many positions carry a USD value: the size of the set positions is the top of |
hiddenCount | assets held with a positive balance that could not be valued. Counted, never listed, not part of totalUsd |
coveredTokenCount | how many token contracts balances are served for right now |
positions[] | at most limit rows in descending USD value |
pricedCount + hiddenCount is everything the address holds.
A position
| Field | Meaning |
|---|---|
kind | token or native |
token | the contract, lowercase; absent for the native asset |
symbol | the ticker as declared by the contract (ETH for the native asset), absent when it declares none. Any contract can declare any symbol — this is not proof of identity |
decimals | as declared by the contract |
balance | a decimal string in the asset's smallest unit — wei for ETH — not divided by decimals |
priceUsd | USD per whole unit at priceHour |
priceCarried | true when no price was observed in that hour and the latest earlier one was used |
valueUsd | balance / 10^decimals × priceUsd, a floating-point estimate; the exact figure follows from balance and priceUsd |
share | valueUsd / totalUsd, between 0 and 1 |
asOfBlock | the block this balance last changed at. For a token in the current view it can be earlier than window.asOfBlock — the balance has not changed since and is valid at window.asOfBlock. For native ETH in the current view it is the block of the address's last balance change, read up to the chain head, so it can be later than window.asOfBlock |
An address holding nothing valued answers 200 with an empty list, no
totalUsd, and hiddenCount for whatever it does hold. That is an answer, not
an error.
Two things not to confuse
hiddenCount is not dust. It is everything held that could not be priced —
a token with no hourly price, or with unknown decimals. A large hiddenCount
means totalUsd is a lower bound, not the address's worth.
Absent is not zero. A token outside the covered set is simply not in the answer. When you need a definitive statement about one token, ask address token balance — it refuses instead of guessing.
Errors
Every error is JSON: { "error": { "code": "...", "message": "...", "details": { … } } }
| Status | code | When | details |
|---|---|---|---|
| 400 | bad_request | malformed address; block not positive; limit outside 1..100; an unknown query parameter | reason: limit_out_of_range, maxLimit: 100 |
| 422 | block_not_available | block is past the covered history | reason: block_not_available, maxAvailableBlock |
| 500 | internal_error | our source returned two balances for one token at one block; neither is chosen | — |
| 422 | portfolio_too_large | the address holds more distinct tokens than the method can value | maxPositions |
| 404 | chain_not_supported | a chain id other than 1 | — |
| 402 | — | payment required, or payment rejected — see Paying with x402 | — |
Numbers a client has to act on are in details, never parsed out of message.
Related pages
- Address token balance — one token, exactly, or a refusal
- Token holders — the other direction: who holds a token
- Address attributes — what an address is
- Paying with x402 — how a call is paid for
- Accounts & Wallets — the same holdings, read in the app
Last updated 22 Oct 2026, 08:06 UTC · verified against TraceFast v1.0.261 · Markdown