Skip to content

Holdings & Balances

The Holdings suite tells you what a wallet holds right now and what it cost. Current quantities and USD values come straight from authoritative on-chain balances, while cost basis is computed by replaying the wallet’s full transaction history through a FIFO tax-lot engine and rebasing the result onto those balances.

All examples use the production base URL:

https://api.qeychain.xyz
Method Path Purpose
GET /api/v1/wallets/{address}/holdings Paginated list of current holdings
GET /api/v1/wallets/{address}/holdings/summary Portfolio totals + per-chain breakdown
GET /api/v1/wallets/{address}/holdings/{symbol} Single asset across all chains, lot by lot
GET /api/v1/wallets/{address}/cost-basis Full lot-level cost basis report

Every endpoint requires authentication. Send your access token as a Bearer token in the Authorization header:

Authorization: Bearer <YOUR_ACCESS_TOKEN>

A request with no valid credentials returns 401 UNAUTHORIZED.

Two independent sources are combined for every holding:

  • Current quantity and value are sourced from the wallet’s live on-chain positions. These are authoritative for “what is held now” and are what the dashboard’s portfolio_value is built from, so totals reconcile across endpoints.
  • Cost basis comes from the FIFO engine’s remaining tax lots. The engine’s raw remaining quantity can drift above the true balance whenever an outflow was not captured in transaction history (bridges, internal transactions, un-indexed transfers). To keep numbers meaningful, cost basis is rebased onto the actual on-chain quantity: the engine’s average cost per unit is multiplied by the balance you actually hold. When the FIFO remaining matches the on-chain balance, this equals the raw remaining cost exactly; when it has drifted, unrealized gain/loss stays consistent instead of showing a phantom total.

As a result, unrealized_gl = current_value - cost_basis, computed over the same quantity on both sides.

All Holdings endpoints return the standard v1 envelope. A successful response wraps its payload in data and always includes meta:

{
"success": true,
"data": { },
"meta": {
"request_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"timestamp": "2026-07-29T14:03:11.482Z",
"version": "1.0.0"
}
}

List endpoints add a pagination block:

"pagination": {
"page": 1,
"per_page": 50,
"total": 128,
"total_pages": 3,
"has_more": true
}

Errors use the same envelope with success: false:

{
"success": false,
"error": {
"code": "INVALID_ADDRESS",
"message": "Invalid wallet address. Supported: Ethereum (0x…), Bitcoin (bc1… / 1… / 3…), Solana.",
"details": { "provided": "not-an-address" },
"field": "address"
},
"meta": { "request_id": "", "timestamp": "", "version": "1.0.0" }
}

Monetary and quantity fields are serialized as JSON strings (for example "4500.00") to preserve decimal precision.

Heavy wallets (thousands of transactions) can take a long time to replay through the FIFO engine because the full history is fetched page by page from the data provider. Balances, by contrast, come from a single fast call. To stay within the gateway timeout, /holdings and /holdings/summary resolve balances first and treat cost-basis enrichment as best-effort under a wall-clock time budget:

  1. On-chain balances are fetched — this alone is enough to return a valid response.
  2. The cost-basis engine build runs under the remaining budget. If it finishes in time, you get full holdings with cost basis. If it overruns, the response is returned with balances only, and the engine build continues in the background to warm the cache.

When this happens, two top-level flags are set:

{
"success": true,
"data": [ ],
"partial": true,
"still_indexing": true,
"meta": { }
}

Both flags are always present on /holdings and /holdings/summary (false on a fully resolved response), so you can rely on the field. When partial is true:

  • quantity, current_price, and current_value are complete and accurate.
  • cost_basis and average_cost come back as "0.00" and unrealized_gl / unrealized_gl_pct are null, because the engine hasn’t finished.

GET /api/v1/wallets/{address}/holdings

Returns the wallet’s current holdings, one entry per (symbol, chain), with quantity, value, cost basis, and unrealized gain/loss. Supports filtering, sorting, and pagination.

Path parameters

Name Type Required Description
address string yes Wallet address (EVM, Bitcoin, or Solana).

Query parameters

Name Type Required Description
chain string no Filter to a single chain, e.g. ethereum, polygon, arbitrum.
min_value number no Minimum current USD value. Must be >= 0.
sort string no Sort order (default value_desc). See below.
page integer no Page number, >= 1 (default 1).
per_page integer no Items per page, 1100 (default 50).
owned_wallets string[] no All addresses owned by the user, for internal-transfer detection.

Accepted sort values: value_desc (default), value_asc, cost_desc, cost_asc, gain_desc, gain_asc, quantity_desc, quantity_asc, symbol_asc, symbol_desc.

Example request

Terminal window
curl -sG "https://api.qeychain.xyz/api/v1/wallets/0x742d35cc6634c0532925a3b844bc454e4438f44e/holdings" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN" \
--data-urlencode "chain=ethereum" \
--data-urlencode "min_value=100" \
--data-urlencode "sort=value_desc" \
--data-urlencode "per_page=50"

Example response

{
"success": true,
"data": [
{
"symbol": "ETH",
"name": "Ethereum",
"chain": "ethereum",
"contract_address": null,
"quantity": "12.481003",
"current_price": "3612.44",
"current_value": "45090.87",
"cost_basis": "31204.10",
"average_cost": "2500.12",
"unrealized_gl": "13886.77",
"unrealized_gl_pct": "44.50",
"total_lots": 9,
"open_lots": 7,
"partial_lots": 2,
"earliest_acquisition": "2022-08-14T09:31:07Z",
"has_long_term_lots": true,
"long_term_quantity": "10.000000",
"short_term_quantity": "2.481003"
},
{
"symbol": "USDC",
"name": "USD Coin",
"chain": "ethereum",
"contract_address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"quantity": "8200.00",
"current_price": "1.00",
"current_value": "8200.00",
"cost_basis": "8200.00",
"average_cost": "1.00",
"unrealized_gl": "0.00",
"unrealized_gl_pct": "0.00",
"total_lots": 4,
"open_lots": 4,
"partial_lots": 0,
"earliest_acquisition": "2023-02-01T18:44:52Z",
"has_long_term_lots": true,
"long_term_quantity": "8200.00",
"short_term_quantity": null
}
],
"pagination": {
"page": 1,
"per_page": 50,
"total": 2,
"total_pages": 1,
"has_more": false
},
"partial": false,
"still_indexing": false,
"meta": {
"request_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"timestamp": "2026-07-29T14:03:11.482Z",
"version": "1.0.0"
}
}

Holding fields

Field Type Description
symbol string Token symbol.
name string | null Token name.
chain string Blockchain network.
contract_address string | null Token contract; null for native coins.
quantity string Current on-chain quantity held.
current_price string | null Current market price per unit (USD).
current_value string | null Current market value (USD).
cost_basis string Total cost basis, rebased to the held quantity (USD).
average_cost string Average cost per unit (USD).
unrealized_gl string | null Unrealized gain/loss (USD).
unrealized_gl_pct string | null Unrealized gain/loss as a percentage.
total_lots integer Number of active tax lots backing this holding.
open_lots integer Fully unconsumed lots.
partial_lots integer Partially consumed lots.
earliest_acquisition string | null Earliest lot acquisition date (ISO 8601).
has_long_term_lots boolean Whether any lots qualify for long-term treatment.
long_term_quantity string | null Quantity in lots held longer than one year.
short_term_quantity string | null Quantity in lots held one year or less.

GET /api/v1/wallets/{address}/holdings/summary

Returns aggregate portfolio totals, a per-chain breakdown, the top holdings by value, and a long-term/short-term value split. total_value is sourced from the same on-chain balances as the dashboard, so the two always agree.

Path parameters

Name Type Required Description
address string yes Wallet address.

Query parameters

Name Type Required Description
owned_wallets string[] no All addresses owned by the user, for internal-transfer detection.

Example request

Terminal window
curl -s "https://api.qeychain.xyz/api/v1/wallets/0x742d35cc6634c0532925a3b844bc454e4438f44e/holdings/summary" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"

Example response

{
"success": true,
"data": {
"address": "0x742d35cc6634c0532925a3b844bc454e4438f44e",
"total_value": "53290.87",
"total_cost_basis": "39404.10",
"total_unrealized_gl": "13886.77",
"total_unrealized_gl_pct": "35.24",
"total_positions": 2,
"positions_with_gain": 1,
"positions_with_loss": 0,
"chains": [
{
"chain": "ethereum",
"total_value": "53290.87",
"cost_basis": "39404.10",
"unrealized_gl": "13886.77",
"position_count": 2,
"percentage_of_portfolio": "100.00"
}
],
"top_holdings": [
{
"symbol": "ETH",
"name": "Ethereum",
"chain": "ethereum",
"contract_address": null,
"quantity": "12.481003",
"current_price": "3612.44",
"current_value": "45090.87",
"cost_basis": "31204.10",
"average_cost": "2500.12",
"unrealized_gl": "13886.77",
"unrealized_gl_pct": "44.50",
"total_lots": 9,
"open_lots": 7,
"partial_lots": 2,
"earliest_acquisition": "2022-08-14T09:31:07Z",
"has_long_term_lots": true,
"long_term_quantity": "10.000000",
"short_term_quantity": "2.481003"
}
],
"long_term_value": "44290.87",
"short_term_value": "9000.00",
"risk_metrics": null
},
"partial": false,
"still_indexing": false,
"meta": {
"request_id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
"timestamp": "2026-07-29T14:05:44.109Z",
"version": "1.0.0"
}
}

Summary fields

Field Type Description
total_value string Total portfolio value (USD).
total_cost_basis string Total cost basis across holdings (USD).
total_unrealized_gl string Total unrealized gain/loss (USD).
total_unrealized_gl_pct string | null Total unrealized gain/loss percentage.
total_positions integer Number of unique holdings.
positions_with_gain integer Holdings currently in the green.
positions_with_loss integer Holdings currently in the red.
chains array Per-chain breakdown (below), sorted by value.
top_holdings array Top 5 holdings by value (same shape as /holdings entries).
long_term_value string | null Value attributable to long-term lots.
short_term_value string | null Value attributable to short-term lots.
risk_metrics object | null Reserved for future scoring; currently always null.

Each entry in chains has: chain, total_value, cost_basis, unrealized_gl, position_count, and percentage_of_portfolio.


GET /api/v1/wallets/{address}/holdings/{symbol}

Returns one asset aggregated across every chain it appears on, with a per-chain breakdown and the full list of tax lots. The headline quantity and value reflect the authoritative on-chain balance; lot-level detail comes from the FIFO engine.

Path parameters

Name Type Required Description
address string yes Wallet address.
symbol string yes Token symbol (case-insensitive, e.g. eth).

Query parameters

Name Type Required Description
owned_wallets string[] no All addresses owned by the user, for internal-transfer detection.

If the wallet has never held the symbol, the response is 404 RESOURCE_NOT_FOUND.

Example request

Terminal window
curl -s "https://api.qeychain.xyz/api/v1/wallets/0x742d35cc6634c0532925a3b844bc454e4438f44e/holdings/ETH" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"

Example response

{
"success": true,
"data": {
"address": "0x742d35cc6634c0532925a3b844bc454e4438f44e",
"symbol": "ETH",
"total_quantity": "12.481003",
"total_current_value": "45090.87",
"total_cost_basis": "31204.10",
"total_unrealized_gl": "13886.77",
"holdings_by_chain": [
{
"symbol": "ETH",
"name": "Ethereum",
"chain": "ethereum",
"contract_address": null,
"quantity": "12.481003",
"current_price": "3612.44",
"current_value": "45090.87",
"cost_basis": "31204.10",
"average_cost": "2500.12",
"unrealized_gl": "13886.77",
"unrealized_gl_pct": "44.50",
"total_lots": 9,
"open_lots": 7,
"partial_lots": 2,
"earliest_acquisition": "2022-08-14T09:31:07Z",
"has_long_term_lots": true,
"long_term_quantity": "10.000000",
"short_term_quantity": "2.481003"
}
],
"lots": [
{
"lot_id": "ETH-ethereum-0001",
"original_quantity": "5.000000",
"remaining_quantity": "5.000000",
"cost_per_unit": "1850.00",
"total_cost": "9250.00",
"remaining_cost": "9250.00",
"acquisition_date": "2022-08-14T09:31:07Z",
"holding_period_days": 1445,
"is_long_term": true,
"lot_type": "purchase",
"price_source": "zerion",
"status": "OPEN",
"is_untracked": false,
"needs_manual_cost_basis": false,
"source_lots": [],
"destination_lots": [],
"origin_type": "purchase",
"origin_chain": null,
"origin_entity": null
},
{
"lot_id": "ETH-ethereum-0007",
"original_quantity": "3.000000",
"remaining_quantity": "2.481003",
"cost_per_unit": "3100.00",
"total_cost": "9300.00",
"remaining_cost": "7691.11",
"acquisition_date": "2025-11-02T21:12:40Z",
"holding_period_days": 269,
"is_long_term": false,
"lot_type": "swap_in",
"price_source": "zerion",
"status": "PARTIAL",
"is_untracked": false,
"needs_manual_cost_basis": false,
"source_lots": ["USDC-ethereum-0031"],
"destination_lots": [],
"origin_type": "swap",
"origin_chain": null,
"origin_entity": null
}
],
"total_lots": 2,
"long_term_lots": 1,
"short_term_lots": 1,
"earliest_acquisition": "2022-08-14T09:31:07Z",
"latest_acquisition": "2025-11-02T21:12:40Z"
},
"meta": {
"request_id": "c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f",
"timestamp": "2026-07-29T14:07:02.551Z",
"version": "1.0.0"
}
}

Top-level detail fields

Field Type Description
total_quantity string On-chain quantity summed across chains.
total_current_value string | null Total current value (USD).
total_cost_basis string Total cost basis, rebased to held balances (USD).
total_unrealized_gl string | null Total unrealized gain/loss (USD).
holdings_by_chain array One holding entry per chain (same shape as /holdings).
lots array All tax lots for the asset, oldest first (fields below).
total_lots integer Number of lots returned.
long_term_lots integer Lots qualifying for long-term treatment.
short_term_lots integer Lots held one year or less.
earliest_acquisition string | null Oldest lot date.
latest_acquisition string | null Most recent lot date.

Lot fields (lots[])

Field Type Description
lot_id string Unique lot identifier.
original_quantity string Quantity acquired.
remaining_quantity string Quantity remaining after disposals.
cost_per_unit string Cost per unit at acquisition (USD).
total_cost string Original total cost basis (USD).
remaining_cost string Remaining cost basis (USD).
acquisition_date string | null When the lot was acquired.
holding_period_days integer | null Days held since acquisition.
is_long_term boolean Held longer than one year.
lot_type string Acquisition type, e.g. purchase, swap_in, reward.
price_source string | null Source of the acquisition price.
status string OPEN, PARTIAL, or CLOSED.
is_untracked boolean Cost basis unknown / needs manual entry.
needs_manual_cost_basis boolean Flagged for manual cost-basis entry.
source_lots string[] Lot IDs this lot was derived from (money trail).
destination_lots string[] Lot IDs this lot became.
origin_type string | null Origin, e.g. purchase, swap, bridge.
origin_chain string | null Origin chain for bridged assets.

GET /api/v1/wallets/{address}/cost-basis

Returns a full, paginated, lot-level cost-basis report across all assets, with summary counts and holding-period totals. The method parameter controls the ordering of lots (and which order field is populated); it does not change the lot data itself.

Path parameters

Name Type Required Description
address string yes Wallet address.

Query parameters

Name Type Required Description
method string no Ordering method: fifo (default), lifo, hifo, specific_id.
symbol string no Filter to a single token symbol.
status string no Filter by lot status: OPEN, PARTIAL, CLOSED, or ALL.
page integer no Page number, >= 1 (default 1).
per_page integer no Items per page, 1100 (default 50).
owned_wallets string[] no All addresses owned by the user, for internal-transfer detection.

Example request

Terminal window
curl -sG "https://api.qeychain.xyz/api/v1/wallets/0x742d35cc6634c0532925a3b844bc454e4438f44e/cost-basis" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN" \
--data-urlencode "method=fifo" \
--data-urlencode "status=OPEN" \
--data-urlencode "per_page=50"

Example response

{
"success": true,
"data": {
"address": "0x742d35cc6634c0532925a3b844bc454e4438f44e",
"method": "fifo",
"total_cost_basis": "39404.10",
"total_current_value": "53290.87",
"total_unrealized_gl": "13886.77",
"total_lots": 13,
"open_lots": 11,
"partial_lots": 2,
"closed_lots": 0,
"long_term_lots": 8,
"short_term_lots": 5,
"long_term_cost_basis": "25204.10",
"short_term_cost_basis": "14200.00",
"lots": [
{
"lot_id": "ETH-ethereum-0001",
"symbol": "ETH",
"chain": "ethereum",
"quantity": "5.000000",
"original_quantity": "5.000000",
"cost_per_unit": "1850.00",
"total_cost_basis": "9250.00",
"original_cost_basis": "9250.00",
"acquisition_date": "2022-08-14T09:31:07Z",
"holding_period_days": 1445,
"is_long_term": true,
"current_price": "3612.44",
"current_value": "18062.20",
"unrealized_gl": "8812.20",
"lot_type": "purchase",
"status": "OPEN",
"is_untracked": false,
"fifo_order": 1,
"lifo_order": null,
"hifo_order": null
}
]
},
"pagination": {
"page": 1,
"per_page": 50,
"total": 13,
"total_pages": 1,
"has_more": false
},
"meta": {
"request_id": "d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80",
"timestamp": "2026-07-29T14:09:18.220Z",
"version": "1.0.0"
}
}

Report fields

Field Type Description
method string Ordering method applied.
total_cost_basis string Remaining cost basis across all lots (USD).
total_current_value string | null Current value, reconciled to on-chain balances (USD).
total_unrealized_gl string | null Reconciled unrealized gain/loss (USD).
total_lots integer Total lots matching the filters.
open_lots integer Fully unconsumed lots.
partial_lots integer Partially consumed lots.
closed_lots integer Fully consumed lots.
long_term_lots integer Lots held longer than one year.
short_term_lots integer Lots held one year or less.
long_term_cost_basis string Cost basis in long-term lots (USD).
short_term_cost_basis string Cost basis in short-term lots (USD).
lots array Lot detail for the current page (fields below).

Report lot fields (lots[])

Field Type Description
lot_id string Unique lot identifier.
symbol string Token symbol.
chain string Blockchain network.
quantity string Remaining quantity.
original_quantity string Quantity at acquisition.
cost_per_unit string Cost per unit at acquisition (USD).
total_cost_basis string Remaining cost basis (USD).
original_cost_basis string Original total cost basis (USD).
acquisition_date string | null Acquisition date.
holding_period_days integer | null Days held.
is_long_term boolean Held longer than one year.
current_price string | null Current price (USD), if available.
current_value string | null Per-lot current value for display (USD).
unrealized_gl string | null Per-lot unrealized gain/loss for display (USD).
lot_type string Acquisition type.
status string OPEN, PARTIAL, or CLOSED.
is_untracked boolean Cost basis unknown.
fifo_order integer | null Consumption order under FIFO (1 = oldest).
lifo_order integer | null Consumption order under LIFO (1 = newest).
hifo_order integer | null Consumption order under HIFO (1 = highest cost).
Code HTTP When
INVALID_ADDRESS 400 {address} is not a valid EVM, Bitcoin, or Solana address.
VALIDATION_ERROR 400 A query parameter is out of range or malformed.
UNAUTHORIZED 401 Missing or invalid credentials.
RESOURCE_NOT_FOUND 404 /holdings/{symbol} — the wallet has never held that symbol.
DATA_PROVIDER_ERROR 502 The upstream data provider failed while fetching balances or history.
PROVIDER_ENTITLEMENT_ERROR 502 The upstream provider rejected the request at the plan level (expired trial or exhausted quota) — an account issue, not an outage.