Docs/Wallet/Wallet PnL

Wallet PnL

GET/v1/wallet/{address}/pnl🔑 API key required

Realized + unrealized profit & loss for a wallet — per token and aggregate — from its swaps on the tokens we index. Cost basis is average-cost by default (?basis=fifo for lot-matched), priced at each swap's block time; open positions are valued at the current reconciled price (Jupiter fallback) and cross-checked against the wallet's actual on-chain balances.

Path parameters

FieldTypeDescription
addressrequiredstringWallet address.

Query parameters

FieldTypeDescription
basisoptionalavgcost | fifoCost-basis method: average-cost (default) or FIFO lot-matching. default avgcost
gasoptionalon | offon subtracts an ESTIMATED network fee per indexed swap from realized PnL, valued at block-time SOL/USD (the swaps table carries no per-tx fee, so gas is an estimate). default off
reconcileoptionaltrue | falseCross-check replay positions against the wallet's actual on-chain token balances. false skips the RPC read; summary.reconciled reports whether it ran. default true
formatoptionalcsvformat=csv downloads the per-token reconciliation as a CSV artifact instead of JSON.
networkoptionalmainnet | devnetNetwork to read. Devnet returns an empty payload (with a note) until its ingestor lands. default mainnet

Response fields

FieldTypeDescription
basisavgcost | fifoCost-basis method used for this response.
gasIncludedbooleantrue when realized PnL is gas-inclusive (gas=on with a configured per-swap estimate).
summary.realizedPnlUsd / unrealizedPnlUsd / totalPnlUsdnumberAggregate PnL across indexed tokens. When summaryComplete is false, totalPnlUsd is a FLOOR estimate (unpriced open positions contribute $0).
summary.roiPctnumber | nulltotalPnl / total invested, %.
summary.winRatenumber | null% of closed positions (those with a sell) that were profitable.
summary.totalInvestedUsd / totalProceedsUsd / totalVolumeUsdnumberUSD bought / USD sold / total traded.
summary.tokensTraded / tradeCount / openPositions / pricedOpenPositionsnumberCounts (openPositions = tokens still held; pricedOpenPositions = those with a current price).
summary.basisstringCost-basis method used (avgcost or fifo).
summary.totalGasUsdnumberTotal estimated gas subtracted from realized PnL (0 unless gas=on).
summary.nonTradeInflowTokensnumberTokens whose sold quantity exceeded their bought quantity (likely transfer/airdrop inflows).
summary.reconciledbooleantrue when positions were verified against on-chain balances; false when reconcile=false or the RPC read failed/timed out.
summary.balanceMismatchTokensnumberTokens whose replay position disagreed with the on-chain balance (dust-tolerant).
summary.summaryCompletebooleanfalse when any open position lacks a current price — summary totals are then a floor estimate.
summary.unpricedOpenPositions / unpricedCostBasisUsdnumberOpen positions with no current price, and the cost basis sitting in them (excluded from unrealized PnL).
summary.priceCapExceededbooleantrue when the wallet traded more distinct mints than the price-lookup cap — mints beyond it have no current price.
summary.stalePriceMintsnumberOpen priced positions whose price was last observed more than 10 minutes ago.
truncatedbooleantrue when swap history hit the replay cap (oldest swaps kept for cost-basis correctness) — later activity is NOT reflected.
truncatedAtstring | nullTimestamp (ISO) of the last swap included when truncated; null otherwise.
unindexedHoldings[].mintstringA chain holding with zero swaps in our index — listed with no PnL claims. The array itself is null when reconciliation was skipped or failed.
unindexedHoldings[].uiAmountnumberOn-chain balance of that holding (ui units; native SOL is folded into wSOL).
tokens[].realizedPnlUsdnumberPer-token realized PnL (gas-inclusive when gas=on).
tokens[].unrealizedPnlUsd / totalPnlUsdnumber | nullPer-token unrealized / total PnL — null when an open position can't be priced.
tokens[].avgBuyPriceUsd / avgSellPriceUsdnumber | nullWeighted average entry / exit price.
tokens[].heldQty / costBasisUsdnumberOpen position size (replay) and its remaining cost basis.
tokens[].heldValueUsdnumber | nullValue of the open position — null when unpriced; clamped to the chain balance when the replay over-states it.
tokens[].partialbooleantrue if the wallet sold more than we saw it buy (incomplete pre-index history).
tokens[].coveragenumber | nullFraction of sold quantity with a swap-derived cost basis, in [0,1]; null when nothing was sold.
tokens[].nonTradeInflowbooleantrue when part of the position arrived off-book (transfer/airdrop) — sold more than bought, or the chain holds more than the replay accounts for.
tokens[].gasUsdnumberEstimated gas (USD) already subtracted from realizedPnlUsd (0 unless gas=on).
tokens[].chainBalanceobject | nullThe wallet's actual on-chain balance ({ amountRaw, uiAmount }; native SOL folded into wSOL); null when reconciliation skipped/failed.
tokens[].balanceMismatch / clampedUnrealizedbooleanbalanceMismatch: replay vs chain disagree beyond dust. clampedUnrealized: the replay held MORE than the chain (a sell happened off our index) — the valued quantity was clamped and the un-held portion's outcome left out rather than guessed.
tokens[].priceAsOfstring | nullWhen the current price was last observed (ISO) — staleness disclosure for unrealized PnL.
tokens[].indexedSincestring | nullEarliest swap we hold for this mint — "all-time" PnL starts here, not at the token's launch.
tokens[].notestring | nullPer-token caveat — e.g. the native-SOL read failed so wSOL was excluded from balance reconciliation this response. null otherwise.
Covers only the tokens this instance indexes; attribution is by transaction fee-payer (a bot fee-payer is not necessarily the trader). Cost basis is average-cost by default; ?basis=fifo gives lot-matched FIFO accounting.
Positions are reconciled against the wallet's ACTUAL on-chain balances by default. If the replay holds more than the chain (a sell happened off our index), the valued quantity is clamped and flagged (clampedUnrealized) instead of reporting phantom unrealized PnL; if the chain holds more, nonTradeInflow is set. Chain holdings we never saw trade are listed under unindexedHoldings with no PnL claims.
summary totals are a FLOOR estimate when summaryComplete is false: open positions without a current price contribute $0 (see unpricedOpenPositions / unpricedCostBasisUsd / priceCapExceeded). "All-time" means since we indexed each mint (tokens[].indexedSince), and very large histories are capped oldest-first — truncated/truncatedAt disclose when later activity is not reflected.