Skip to content

Portfolios & Wallets

The Qeychain API gives you two complementary ways to organize on-chain addresses:

  • Linked wallets attach addresses directly to your Qeychain account. The number of wallets you can link is governed by your plan.
  • Portfolios are named groupings of addresses that produce aggregated views — consolidated holdings and consolidated tax figures — across every address in the group.

Use linked wallets to remember the addresses that belong to you, and portfolios when you need a single rolled-up answer across several of them (for example, one household or one fund reported together).

All endpoints on this page live under the supported /api/v1 surface. The base URL for every example is:

https://api.qeychain.xyz

These endpoints operate on your account, so they require a user access token (a JWT), not just a data API key. Send it as a Bearer token on every request:

Authorization: Bearer <ACCESS_TOKEN>

Every v1 response is wrapped in a standard envelope. Successful responses carry a data payload plus a meta block for request tracing:

{
"success": true,
"data": { },
"meta": {
"request_id": "3f2b7c1a-9d4e-4a51-8c2b-1e0f6a2d9b44",
"timestamp": "2026-07-29T14:32:10.123456Z",
"version": "1.0.0"
}
}

Errors use the same envelope with success: false and an error object:

{
"success": false,
"error": {
"code": "PORTFOLIO_NOT_FOUND",
"message": "Portfolio 8b1c… not found"
},
"meta": {
"request_id": "3f2b7c1a-9d4e-4a51-8c2b-1e0f6a2d9b44",
"timestamp": "2026-07-29T14:32:10.123456Z",
"version": "1.0.0"
}
}

List endpoints add a pagination block alongside data.


Linked wallets are addresses tied to your account. Supported address formats are EVM (0x…), Bitcoin, and Solana — fungible tokens and native coins are tracked, valued in USD.

Each plan permits a maximum number of linked wallets:

Plan Max linked wallets
Free 1
Starter 5
Professional 25
DeFi Power User Unlimited
POST /api/v1/user/wallets

Links an on-chain address to your account.

Request body

Parameter Type Required Description
address string Yes On-chain wallet address, 26–255 characters (EVM 0x…, Bitcoin, or Solana).
label string No Friendly name for the wallet (max 100 characters).

Example request

Terminal window
curl -X POST https://api.qeychain.xyz/api/v1/user/wallets \
-H "Authorization: Bearer $QEYCHAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"address": "0x742d35cc6634c0532925a3b844bc454e4438f44e",
"label": "Treasury cold wallet"
}'

Example response (200 OK)

{
"success": true,
"data": {
"id": "a1c2e3f4-5678-49ab-9cde-0123456789ab",
"address": "0x742d35cc6634c0532925a3b844bc454e4438f44e",
"label": "Treasury cold wallet",
"created_at": "2026-07-29T14:32:10.101010Z"
},
"meta": {
"request_id": "3f2b7c1a-9d4e-4a51-8c2b-1e0f6a2d9b44",
"timestamp": "2026-07-29T14:32:10.123456Z",
"version": "1.0.0"
}
}

If the same address is already linked to your account, the request returns 409 Conflict with error code INVALID_REQUEST.

GET /api/v1/user/wallets

Returns every wallet linked to your account (oldest first), plus your plan’s wallet allowance.

Example request

Terminal window
curl https://api.qeychain.xyz/api/v1/user/wallets \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"

Example response (200 OK)

{
"success": true,
"data": {
"wallets": [
{
"id": "a1c2e3f4-5678-49ab-9cde-0123456789ab",
"address": "0x742d35cc6634c0532925a3b844bc454e4438f44e",
"label": "Treasury cold wallet",
"created_at": "2026-07-29T14:32:10.101010Z"
}
],
"count": 1,
"wallet_limit": 5
},
"meta": {
"request_id": "7c9a1b2d-3e4f-4051-8617-2a3b4c5d6e7f",
"timestamp": "2026-07-29T14:33:02.554433Z",
"version": "1.0.0"
}
}

count is how many wallets you have linked; wallet_limit is how many your plan allows (null = unlimited).

DELETE /api/v1/user/wallets/{wallet_id}

Removes a linked wallet. You can only unlink wallets that belong to your own account.

Path parameters

Parameter Type Required Description
wallet_id string (UUID) Yes The wallet id returned when it was linked or listed.

Example request

Terminal window
curl -X DELETE \
https://api.qeychain.xyz/api/v1/user/wallets/a1c2e3f4-5678-49ab-9cde-0123456789ab \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"

Example response (200 OK)

{
"success": true,
"data": { "deleted": true },
"meta": {
"request_id": "9d8c7b6a-5432-41fe-8dcb-6a5b4c3d2e1f",
"timestamp": "2026-07-29T14:35:44.778899Z",
"version": "1.0.0"
}
}

Error responses

Status Error code When
400 INVALID_PARAMETER wallet_id is not a valid UUID.
403 FORBIDDEN The wallet belongs to another account.
404 RESOURCE_NOT_FOUND No wallet exists with that ID.

A portfolio is a named group of addresses. Beyond CRUD, portfolios expose aggregation endpoints that consolidate holdings and tax figures across every address they contain.

A portfolio address record has these fields:

Field Type Description
address string The wallet address.
label string | null Optional label for the address.
chain string | null Optional default-chain hint.
added_at string (ISO 8601) When the address was added to the portfolio.
POST /api/v1/portfolios

Request body

Parameter Type Required Description
name string Yes Portfolio name (1–255 characters).
addresses string[] No Initial wallet addresses (max 1000). Each is validated as an EVM, Bitcoin, or Solana address.
description string No Free-form description.
metadata object No Arbitrary JSON metadata you want to store with the portfolio.

Example request

Terminal window
curl -X POST https://api.qeychain.xyz/api/v1/portfolios \
-H "Authorization: Bearer $QEYCHAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Fund I — Digital Assets",
"description": "All addresses reported for Fund I",
"addresses": [
"0x742d35cc6634c0532925a3b844bc454e4438f44e",
"0x53d284357ec70ce289d6d64134dfac8e511c8a3d"
]
}'

Example response (200 OK)

{
"success": true,
"data": {
"id": "8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405",
"name": "Fund I — Digital Assets",
"description": "All addresses reported for Fund I",
"address_count": 2,
"addresses": [
{
"address": "0x742d35cc6634c0532925a3b844bc454e4438f44e",
"label": null,
"chain": null,
"added_at": "2026-07-29T14:40:00.000000Z"
},
{
"address": "0x53d284357ec70ce289d6d64134dfac8e511c8a3d",
"label": null,
"chain": null,
"added_at": "2026-07-29T14:40:00.000000Z"
}
],
"metadata": null,
"created_at": "2026-07-29T14:40:00.000000Z",
"updated_at": "2026-07-29T14:40:00.000000Z"
},
"meta": {
"request_id": "1a2b3c4d-5e6f-4708-9a1b-2c3d4e5f6071",
"timestamp": "2026-07-29T14:40:00.221100Z",
"version": "1.0.0"
}
}
GET /api/v1/portfolios

Returns your portfolios, most recently created first, with pagination.

Query parameters

Parameter Type Required Description
page integer No Page number, 1-indexed. Default 1.
per_page integer No Items per page, 1–100. Default 50.

Example request

Terminal window
curl "https://api.qeychain.xyz/api/v1/portfolios?page=1&per_page=50" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"

Example response (200 OK)

{
"success": true,
"data": [
{
"id": "8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405",
"name": "Fund I — Digital Assets",
"description": "All addresses reported for Fund I",
"address_count": 2,
"addresses": [],
"metadata": null,
"created_at": "2026-07-29T14:40:00.000000Z",
"updated_at": "2026-07-29T14:40:00.000000Z"
}
],
"meta": {
"request_id": "2b3c4d5e-6f70-4819-8a2b-3c4d5e6f7081",
"timestamp": "2026-07-29T14:41:12.010203Z",
"version": "1.0.0"
},
"pagination": {
"page": 1,
"per_page": 50,
"total": 1,
"total_pages": 1,
"has_more": false
}
}
GET /api/v1/portfolios/{portfolio_id}

Path parameters

Parameter Type Required Description
portfolio_id string (UUID) Yes Portfolio identifier.

Example request

Terminal window
curl https://api.qeychain.xyz/api/v1/portfolios/8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405 \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"

Returns the same portfolio object shape shown under Create a portfolio, with addresses fully populated. A missing portfolio returns 404 with error code PORTFOLIO_NOT_FOUND.

PUT /api/v1/portfolios/{portfolio_id}

Updates the portfolio’s top-level fields. Only the fields you send are changed. To change the address set, use the address endpoints below.

Path parameters

Parameter Type Required Description
portfolio_id string (UUID) Yes Portfolio identifier.

Request body

Parameter Type Required Description
name string No New name (1–255 characters).
description string No New description.
metadata object No Replaces the stored metadata object.

Example request

Terminal window
curl -X PUT https://api.qeychain.xyz/api/v1/portfolios/8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405 \
-H "Authorization: Bearer $QEYCHAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "Fund I — Digital Assets (2026)" }'

Returns the updated portfolio object. A missing portfolio returns 404 PORTFOLIO_NOT_FOUND.

DELETE /api/v1/portfolios/{portfolio_id}

Deletes the portfolio and its address memberships. This does not unlink any account wallets — it only removes the grouping.

Example response (200 OK)

{
"success": true,
"data": {
"deleted": true,
"portfolio_id": "8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405"
},
"meta": {
"request_id": "3c4d5e6f-7081-4920-8b3c-4d5e6f708192",
"timestamp": "2026-07-29T14:45:00.334455Z",
"version": "1.0.0"
}
}

A missing portfolio returns 404 PORTFOLIO_NOT_FOUND.

POST /api/v1/portfolios/{portfolio_id}/addresses

Path parameters

Parameter Type Required Description
portfolio_id string (UUID) Yes Portfolio identifier.

Request body

Parameter Type Required Description
address string Yes Wallet address to add (EVM, Bitcoin, or Solana).
label string No Optional label (max 255 characters).
chain string No Optional default-chain hint (max 50 characters).

Example request

Terminal window
curl -X POST \
https://api.qeychain.xyz/api/v1/portfolios/8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405/addresses \
-H "Authorization: Bearer $QEYCHAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"address": "0xab5801a7d398351b8be11c439e05c5b3259aec9b",
"label": "Operating wallet"
}'

Example response (200 OK)

{
"success": true,
"data": {
"address": "0xab5801a7d398351b8be11c439e05c5b3259aec9b",
"label": "Operating wallet",
"chain": null,
"added_at": "2026-07-29T14:47:31.000000Z"
},
"meta": {
"request_id": "4d5e6f70-8192-4a31-8c4d-5e6f70819243",
"timestamp": "2026-07-29T14:47:31.556677Z",
"version": "1.0.0"
}
}

Error responses

Status Error code When
400 PORTFOLIO_ADDRESS_EXISTS The address is already in this portfolio.
404 PORTFOLIO_NOT_FOUND No portfolio exists with that ID.
DELETE /api/v1/portfolios/{portfolio_id}/addresses/{address}

Path parameters

Parameter Type Required Description
portfolio_id string (UUID) Yes Portfolio identifier.
address string Yes The address to remove from the portfolio.

Example request

Terminal window
curl -X DELETE \
"https://api.qeychain.xyz/api/v1/portfolios/8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405/addresses/0xab5801a7d398351b8be11c439e05c5b3259aec9b" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"

Example response (200 OK)

{
"success": true,
"data": {
"removed": true,
"address": "0xab5801a7d398351b8be11c439e05c5b3259aec9b"
},
"meta": {
"request_id": "5e6f7081-9243-4b41-8d5e-6f70819243a5",
"timestamp": "2026-07-29T14:49:05.998877Z",
"version": "1.0.0"
}
}

If the address is not part of the portfolio, the request returns 404 RESOURCE_NOT_FOUND.


These endpoints roll up on-chain data across every address in a portfolio. Qeychain fetches and processes each address’s transaction history when you call them.

GET /api/v1/portfolios/{portfolio_id}/holdings

Returns holdings consolidated by symbol + chain across all addresses. Fungible tokens and native coins are included; values are in USD.

Path parameters

Parameter Type Required Description
portfolio_id string (UUID) Yes Portfolio identifier.

Example request

Terminal window
curl https://api.qeychain.xyz/api/v1/portfolios/8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405/holdings \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"

Example response (200 OK)

{
"success": true,
"data": {
"portfolio_id": "8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405",
"portfolio_name": "Fund I — Digital Assets",
"address_count": 2,
"addresses": [
"0x742d35cc6634c0532925a3b844bc454e4438f44e",
"0x53d284357ec70ce289d6d64134dfac8e511c8a3d"
],
"holdings": [
{
"symbol": "ETH",
"chain": "ethereum",
"quantity": 12.5,
"cost_basis": 24000.0,
"current_value": 40625.0,
"unrealized_gl": 16625.0,
"unrealized_gl_pct": 69.27,
"price": 3250.0,
"wallet_count": 2
},
{
"symbol": "USDC",
"chain": "ethereum",
"quantity": 50000.0,
"cost_basis": 50000.0,
"current_value": 50000.0,
"unrealized_gl": 0.0,
"unrealized_gl_pct": 0.0,
"price": 1.0,
"wallet_count": 1
}
],
"total_value": 90625.0,
"total_cost_basis": 74000.0,
"total_unrealized_gl": 16625.0,
"errors": null
},
"meta": {
"request_id": "6f708192-43a5-4c51-8e6f-70819243a5b6",
"timestamp": "2026-07-29T14:52:18.112233Z",
"version": "1.0.0"
}
}

Each holding reports wallet_count, the number of portfolio addresses that hold it. Holdings are sorted by current_value descending. If a position has no available price, its current_value is 0 and price is null. A missing portfolio returns 404 PORTFOLIO_NOT_FOUND.

GET /api/v1/portfolios/{portfolio_id}/tax-reports/summary

Returns realized short- and long-term gains and losses consolidated across every address in the portfolio for a tax year.

Path parameters

Parameter Type Required Description
portfolio_id string (UUID) Yes Portfolio identifier.

Query parameters

Parameter Type Required Description
tax_year integer No Tax year (2009–2100). Defaults to the current year.

Example request

Terminal window
curl "https://api.qeychain.xyz/api/v1/portfolios/8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405/tax-reports/summary?tax_year=2025" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"

Example response (200 OK)

{
"success": true,
"data": {
"portfolio_id": "8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405",
"portfolio_name": "Fund I — Digital Assets",
"tax_year": 2025,
"address_count": 2,
"addresses": [
"0x742d35cc6634c0532925a3b844bc454e4438f44e",
"0x53d284357ec70ce289d6d64134dfac8e511c8a3d"
],
"short_term_gains": 4200.00,
"short_term_losses": 1100.00,
"net_short_term": 3100.00,
"long_term_gains": 18500.00,
"long_term_losses": 900.00,
"net_long_term": 17600.00,
"total_realized_gl": 20700.00,
"total_disposals": 47,
"errors": null
},
"meta": {
"request_id": "70819243-a5b6-4d61-8f70-819243a5b6c7",
"timestamp": "2026-07-29T14:55:41.443322Z",
"version": "1.0.0"
}
}

A missing portfolio returns 404 PORTFOLIO_NOT_FOUND.

GET /api/v1/portfolios/{portfolio_id}/tax-reports/8949

Returns every disposal in the tax year, formatted for IRS Form 8949 and consolidated across all portfolio addresses. Entries are split into short-term and long-term lists.

Path parameters

Parameter Type Required Description
portfolio_id string (UUID) Yes Portfolio identifier.

Query parameters

Parameter Type Required Description
tax_year integer Yes Tax year (2009–2100).

Example request

Terminal window
curl "https://api.qeychain.xyz/api/v1/portfolios/8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405/tax-reports/8949?tax_year=2025" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"

Example response (200 OK, abridged)

{
"success": true,
"data": {
"portfolio_id": "8b1c2d3e-4f50-4617-92a8-b0c1d2e3f405",
"portfolio_name": "Fund I — Digital Assets",
"tax_year": 2025,
"address_count": 2,
"short_term_entries": [
{
"description": "ETH (ethereum)",
"date_acquired": "03/14/2025",
"date_sold": "09/02/2025",
"proceeds": 3250.00,
"cost_basis": 2400.00,
"adjustment": 0,
"gain_or_loss": 850.00,
"quantity": 1.0,
"lot_id": "lot_7f3a",
"source_wallet": "0x742d35cc6634c0532925a3b844bc454e4438f44e"
}
],
"short_term_count": 1,
"short_term_total_proceeds": 3250.00,
"short_term_total_cost": 2400.00,
"short_term_total_gl": 850.00,
"long_term_entries": [],
"long_term_count": 0,
"long_term_total_proceeds": 0,
"long_term_total_cost": 0,
"long_term_total_gl": 0,
"errors": null
},
"meta": {
"request_id": "819243a5-b6c7-4e71-8081-9243a5b6c7d8",
"timestamp": "2026-07-29T14:58:03.221100Z",
"version": "1.0.0"
}
}

Each entry carries a source_wallet so you can trace consolidated disposals back to the originating address. A missing portfolio returns 404 PORTFOLIO_NOT_FOUND.


The endpoints on this page can return the following error codes in the standard error envelope:

Code Typical status Meaning
UNAUTHORIZED 401 Missing or invalid access token.
FORBIDDEN 403 You do not own the target resource.
INVALID_REQUEST 409 Wallet address already linked to your account.
INVALID_PARAMETER 400 A path parameter (such as wallet_id) is malformed.
RESOURCE_NOT_FOUND 404 Wallet, or portfolio address, not found.
PORTFOLIO_NOT_FOUND 404 No portfolio exists with that ID.
PORTFOLIO_ADDRESS_EXISTS 400 The address is already in the portfolio.
INTERNAL_ERROR 500 Unexpected server error while processing the request.