Skip to content

Realized and open PnL per token

GET
/portfolio/{wallet}/pnl
curl --request GET \
--url 'https://api.getchord.xyz/v1/portfolio/example/pnl?chain=5042'

Average-cost PnL for one wallet from the tape: realized on sells, unrealized on what is still held at current prices, ROI, and the untracked balance that arrived by transfer (no cost basis, excluded from PnL). Cached with stale-while-revalidate (a day-old answer beats a $0 skeleton).

Portfolio routes answer an empty 200 shape rather than a 5xx when the tape read fails — treat an empty tokens list as “nothing yet” and retry later.

wallet
required

EVM address, hex, case-insensitive.

string
/^0x[a-fA-F0-9]{40}$/
chain
string
default: all
Allowed values: 5042 4663 143 all

One chain, or all to merge chains with a byChain breakdown.

Media typeapplication/json
object
tokens
Array<object>
object
token

EVM address, hex, case-insensitive.

string
/^0x[a-fA-F0-9]{40}$/
symbol
string | null
logo
string | null
realizedUsd
number
unrealizedUsd
number
totalUsd
number
costRemaining
number
curValue
number
curAmount
number
avgCost

Average cost per token, USD.

number
roiPct
number | null
untrackedAmount

Balance that arrived by transfer (no cost basis).

number
untrackedValue
number
realized
number
unrealized
number
total
number
exitedWithoutSale

Present when bought quantity left the wallet with no sale on the tape (a transfer out, or a sale credited to another address): {tokens, costUsd, top: [{symbol, costUsd}]}. Its result is unknown and NOT in the PnL above.

object
key
additional properties
any
byChain

Only with chain=all.

object
key
additional properties
any
updatedAt

Unix time, milliseconds — when the server built this answer.

integer
Example
{
"tokens": [
{
"token": "0xece5ca8bf9220718e5727754026757512212cb3c",
"symbol": "ARGUS",
"logo": "https://coin-images.coingecko.com/coins/images/102178392/large/argus-token.png?1789463380",
"realizedUsd": 30462.864779627198,
"unrealizedUsd": 11700.939232721703,
"totalUsd": 42163.804012348905,
"costRemaining": 1286.137220108177,
"curValue": 12987.07645282988,
"curAmount": 744055.9147763874,
"avgCost": 0.0017285491514366933,
"roiPct": 909.7737822825419,
"untrackedAmount": 0,
"untrackedValue": 0
},
{
"token": "0x66927743a8a841ca09575a4ccdcd090c761adcec",
"symbol": "HYCI",
"logo": "https://ipfs.filebase.io/ipfs/bafybeiezh2g4uhqy2bzdq4elwkzek55q5ccs4gpyyki672a4i3olf3u224",
"realizedUsd": 181.713005,
"unrealizedUsd": 0,
"totalUsd": 181.713005,
"costRemaining": 0,
"curValue": 0,
"curAmount": 0,
"avgCost": 0.000042576384935211256,
"roiPct": null,
"untrackedAmount": 0,
"untrackedValue": 0
}
],
"realized": 30488.101882774274,
"unrealized": 11700.939232721703,
"total": 42189.04111549597,
"updatedAt": 1789809277093
}

Invalid parameters — error names the check that failed (INVALID_PARAMS, BAD_ADDRESS, BAD_LIMIT, BAD_HOURS, …).

Media typeapplication/json

Every non-2xx body.

object
error
required

Stable machine code, e.g. INVALID_PARAMS, NOT_READY, RATE_LIMIT_EXCEEDED.

string
message

For humans.

string
statusCode

HTTP status, when included.

integer
Example
{
"error": "INVALID_PARAMS",
"message": "amount must be an integer string in the token's smallest unit"
}