Skip to content

Transaction History

The transaction endpoints return a wallet’s complete on-chain history, already normalized, spam-filtered, classified for tax purposes, and enriched with counterparty labels. You can page through it, filter it, inspect a single transaction with full cost-basis lineage, or export the whole filtered set as CSV or JSON.

All examples use the production base URL https://api.qeychain.xyz.

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

Authorization: Bearer <YOUR_ACCESS_TOKEN>

Browser sessions authenticate with the access_token cookie automatically. An unauthenticated request returns 401.

Every endpoint on this page accepts an optional owned_wallets parameter — the full set of addresses the same user controls. Pass it (repeat the parameter, or use a comma-separated list) whenever a user holds more than one wallet.

Supplying owned_wallets lets the engine recognize transfers between the user’s own wallets as non-taxable internal moves, carry cost basis across them, and stitch lineage together across chains. Omit it and a wallet-to-wallet move looks like a send from one address and an unrelated receive on the other.


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

Returns a paginated, filtered, sorted transaction history for a wallet, plus a filter_meta block describing every filter value present in the full history (so you can build filter UIs without a second call).

Name Type Required Description
address string yes Wallet address (EVM 0x…, Bitcoin, or Solana).
Name Type Required Description
page integer no Page number, 1-indexed. Default 1.
per_page integer no Items per page, 1100. Default 50.
type string no Filter by transaction type(s), comma-separated (e.g. trade,send). Matched against the provider operation type.
chain string no Filter by network (e.g. ethereum, polygon, base, bitcoin, solana).
from_date string no Start of date range, ISO 8601 (e.g. 2024-01-01).
to_date string no End of date range, ISO 8601. Inclusive to end-of-day.
hide_spam boolean no Hide spam/trash transactions. Default true.
asset string no Filter by asset symbol(s), comma-separated (e.g. ETH,USDC). Case-insensitive.
sort_by string no Sort field: timestamp (default), value_usd, or fee_usd.
sort_order string no desc (default) or asc.
owned_wallets string[] no All addresses owned by the user (see Multi-wallet ownership).
Terminal window
curl "https://api.qeychain.xyz/api/v1/wallets/0x742d35cc6634c0532925a3b844bc9e7595f0beb0/transactions?type=trade&chain=ethereum&per_page=2" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"
{
"success": true,
"data": [
{
"id": "ethereum:0x9f2c…a41b",
"tx_hash": "0x9f2c8b7d5e3a1c0f4b6d8e2a9c7f1b3d5e7a9c1f3b5d7e9a1c3f5b7d9e1a3c5b",
"type": "trade",
"timestamp": "2024-11-14T16:42:07+00:00",
"chain": "ethereum",
"status": "confirmed",
"from_asset": "USDC",
"from_quantity": "1000.000000",
"to_asset": "ETH",
"to_quantity": "0.284120",
"value_usd": "1000.00",
"fee_usd": "3.71",
"transfers": [
{
"direction": "out",
"symbol": "USDC",
"icon_url": "https://cdn.example/usdc.png",
"quantity": "1000.000000",
"value_usd": "1000.00",
"price_per_unit": "1.00",
"from_address": "0x742d35cc6634c0532925a3b844bc9e7595f0beb0",
"to_address": "0x111111125421ca6dc452d289314280a0f8842a65",
"contract_address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"from_entity": null,
"to_entity": {
"name": "1inch",
"type": "dex",
"label": "Aggregation Router",
"is_exchange": false
}
},
{
"direction": "in",
"symbol": "ETH",
"icon_url": "https://cdn.example/eth.png",
"quantity": "0.284120",
"value_usd": "1000.00",
"price_per_unit": "3519.64",
"from_address": "0x111111125421ca6dc452d289314280a0f8842a65",
"to_address": "0x742d35cc6634c0532925a3b844bc9e7595f0beb0",
"contract_address": null,
"from_entity": null,
"to_entity": null
}
],
"counterparty": {
"name": "1inch",
"type": "dex",
"label": "Aggregation Router",
"is_exchange": false
},
"lineage": null,
"tax_impact": null,
"is_spam": false
}
],
"meta": {
"request_id": "3f1c9a2e-8b47-4d6e-9a1c-2f5b7d9e1a3c",
"timestamp": "2026-01-20T09:15:42.318000+00:00",
"version": "1.0.0"
},
"pagination": {
"page": 1,
"per_page": 2,
"total": 138,
"total_pages": 69,
"has_more": true
},
"filter_meta": {
"spam_count": 24,
"available_years": [2024, 2023, 2022],
"available_types": [
{"type": "trade", "count": 61},
{"type": "send", "count": 40},
{"type": "receive", "count": 37}
],
"available_assets": [
{"symbol": "ETH", "count": 74},
{"symbol": "USDC", "count": 52}
],
"available_chains": [
{"chain": "ethereum", "count": 120},
{"chain": "base", "count": 18}
]
}
}

lineage and tax_impact are null in the list response. To retrieve them, call the detail endpoint for a specific transaction, or export with include_lineage=true.


There are two related classification layers. Knowing which is which avoids surprises.

1. Transaction type (the type field and type= filter)

Section titled “1. Transaction type (the type field and type= filter)”

The type field on each transaction is the provider operation type, a lowercase string such as trade, send, receive, deposit, or withdraw. This is exactly what the type= filter matches. Because the exact set depends on a wallet’s activity, treat filter_meta.available_types as the authoritative list of values you can filter on for that wallet.

2. Cost-basis event classification (drives lineage and tax)

Section titled “2. Cost-basis event classification (drives lineage and tax)”

Underneath, the cost-basis engine classifies each transaction into a canonical event type that determines whether it realizes a gain/loss and how lineage flows. These are the event families the engine recognizes:

Category Engine event types Tax treatment
Trade / disposal SELL, SWAP, SPEND, DEX_BUY, LIQUIDATION, FEE_DISPOSAL, GAS_FEE Realizes capital gain/loss
Send / transfer out SEND, TRANSFER_OUT, TRANSFER_TO_EXCHANGE, INTERNAL_TRANSFER Non-taxable; cost basis carried
Receive / inflow RECEIVE, DEPOSIT, TRANSFER_FROM_EXCHANGE Non-taxable acquisition / basis carry
Bridge BRIDGE_OUT, BRIDGE_IN Non-taxable; basis carries across chains
Wrap / migrate WRAP, UNWRAP, ASSET_MIGRATION, MIGRATION, REBASE Non-taxable; basis preserved
Stake STAKE, UNSTAKE, STAKE_CONVERSION, STAKING_TRANSFER, UNSTAKING_TRANSFER Non-taxable; basis preserved
Lending / DeFi DEFI_LEND_TRANSFER, DEFI_WITHDRAW_TRANSFER, DEFI_BORROW, DEFI_REPAY, LP_DEPOSIT, LP_WITHDRAW Lend/borrow transfers non-taxable; LP deposit/withdraw realize G/L by default (conservative treatment), and become non-taxable only if an operator disables the taxable-LP config
Income REWARD, INTEREST_INCOME (airdrop / staking / mining / interest inflows) Ordinary income at fair-market value; creates cost basis

So a transaction whose type is trade typically maps to a SWAP or SELL event, which is why its tax_impact.is_taxable is true, while a send between the user’s own wallets maps to INTERNAL_TRANSFER and is non-taxable.


GET /api/v1/wallets/{address}/transactions/{tx_hash}

Returns one transaction with its full cost-basis detail: the enhanced transaction (including lineage and tax_impact), every lot the transaction consumed and created, and the rolled-up proceeds / cost basis / realized gain or loss.

Name Type Required Description
address string yes Wallet address.
tx_hash string yes The transaction hash to look up (case-insensitive).
Name Type Required Description
owned_wallets string[] no All addresses owned by the user, for accurate cross-wallet lineage.
Terminal window
curl "https://api.qeychain.xyz/api/v1/wallets/0x742d35cc6634c0532925a3b844bc9e7595f0beb0/transactions/0x9f2c8b7d5e3a1c0f4b6d8e2a9c7f1b3d5e7a9c1f3b5d7e9a1c3f5b7d9e1a3c5b" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"
{
"success": true,
"data": {
"transaction": {
"id": "ethereum:0x9f2c…a41b",
"tx_hash": "0x9f2c8b7d5e3a1c0f4b6d8e2a9c7f1b3d5e7a9c1f3b5d7e9a1c3f5b7d9e1a3c5b",
"type": "trade",
"timestamp": "2024-11-14T16:42:07+00:00",
"chain": "ethereum",
"status": "confirmed",
"from_asset": "USDC",
"from_quantity": "1000.000000",
"to_asset": "ETH",
"to_quantity": "0.284120",
"value_usd": "1000.00",
"fee_usd": "3.71",
"transfers": [ "" ],
"counterparty": {
"name": "1inch",
"type": "dex",
"label": "Aggregation Router",
"is_exchange": false
},
"lineage": {
"source_lots": [
{
"lot_id": "USDC-ethereum-0007",
"quantity": "1000.000000",
"cost_basis": "1000.00",
"acquisition_date": "2024-09-02T11:05:00+00:00"
}
],
"destination_lots": [
{
"lot_id": "ETH-ethereum-0031",
"quantity": "0.284120",
"cost_basis": "1000.00",
"acquisition_date": "2024-11-14T16:42:07+00:00"
}
]
},
"tax_impact": {
"is_taxable": true,
"realized_gain_loss": "0.00",
"is_long_term": false,
"holding_period_days": 73,
"wash_sale_disallowed": null
},
"is_spam": false
},
"lots_consumed": [
{
"lot_id": "USDC-ethereum-0007",
"quantity": "1000.000000",
"cost_basis": "1000.00",
"acquisition_date": "2024-09-02T11:05:00+00:00"
}
],
"lots_created": [
{
"lot_id": "ETH-ethereum-0031",
"quantity": "0.284120",
"cost_basis": "1000.00",
"acquisition_date": "2024-11-14T16:42:07+00:00"
}
],
"total_proceeds": "1000.00",
"total_cost_basis": "1000.00",
"total_realized_gl": "0.00"
},
"meta": {
"request_id": "a12f7c04-6d3b-4e21-9c8a-7b5e1f2d3a4c",
"timestamp": "2026-01-20T09:16:10.442000+00:00",
"version": "1.0.0"
}
}

If no transaction with that hash is found for the wallet, the endpoint returns 404 with error code RESOURCE_NOT_FOUND.


Lineage is the money trail that connects lots to the transactions that created and consumed them — the backbone of accurate cost basis.

  • source_lots / lots_consumed — the existing lots a disposal draws down (FIFO). For the swap above, the USDC lot is consumed to fund the trade.
  • destination_lots / lots_created — the new lots a transaction produces. The swap creates an ETH lot whose cost basis is inherited from what was given up.

For non-taxable movements the engine deliberately carries cost basis forward instead of realizing a gain, so the trail stays intact:

  • Bridges — a BRIDGE_OUT on the source chain is matched to a BRIDGE_IN on the destination chain (within a 24-hour window, allowing up to a 2% bridge fee, and treating equivalent symbols such as ETH/WETH as the same asset). The destination lot inherits the source lot’s basis and acquisition date.
  • Internal transfers — a move between two of the user’s own wallets is recognized as an INTERNAL_TRANSFER and the receiving lot inherits basis from the sending lot. This only works when you pass owned_wallets.
  • Wraps, migrations, and staking — preserve basis across the wrapped, migrated, or staked representation.

GET /api/v1/wallets/{address}/transactions/export

Applies the same filters as the list endpoint (plus a quick range preset) and streams every matching row — no pagination — as a downloadable CSV or JSON file. Row count is subject to the plan transaction limit.

Name Type Required Description
address string yes Wallet address.
Name Type Required Description
format string no csv (default) or json.
range string no Quick range preset: all (default), 1m, 3m, 6m, 9m, 1y, fy, or custom.
from_date string no Start date, ISO 8601. Used when range=custom.
to_date string no End date, ISO 8601. Used when range=custom. Inclusive to end-of-day.
type string no Filter by type(s), comma-separated.
chain string no Filter by network.
asset string no Filter by asset symbol(s), comma-separated.
hide_spam boolean no Hide spam/trash transactions. Default true.
sort_by string no timestamp (default), value_usd, or fee_usd.
sort_order string no desc (default) or asc.
include_lineage boolean no Include the lineage and tax-impact columns (source/destination lots, realized G/L, holding period). Default false.
owned_wallets string[] no All addresses owned by the user.

Range presets. 1m/3m/6m/9m cover the last N months; 1y the last 12 months; fy the current financial year (January 1 to now — US crypto is taxed as property, so the tax year is the calendar year); custom uses your explicit from_date/to_date; all returns the full history. A range preset takes precedence over from_date/to_date.

With range=custom, a malformed from_date or to_date returns 400 with error code VALIDATION_ERROR.

The export sets two response headers:

Header Description
X-Export-Count Number of rows in the file.
X-Export-Truncated true if the upstream data provider hit its page cap and the history returned may be incomplete; otherwise false.

The file downloads with a Content-Disposition attachment named qeychain-transactions-<address8>-<range>-<YYYYMMDD>.csv (or .json).

Terminal window
curl -L "https://api.qeychain.xyz/api/v1/wallets/0x742d35cc6634c0532925a3b844bc9e7595f0beb0/transactions/export?format=csv&range=fy&include_lineage=true" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN" \
-D - -o transactions.csv

The -D - flag prints the response headers so you can inspect X-Export-Count and X-Export-Truncated.

Columns whose value is blank for every row are dropped, so a file always fits its data. The identity columns (Transaction Hash, Timestamp, Type, Chain) are always present; the lineage and tax columns (Is Taxable, Realized G/L, Holding Period, Source Lots, Destination Lots) appear only when include_lineage=true; Counterparty appears only when an entity was resolved.

Transaction Hash,Timestamp,Type,Chain,Status,From Asset,From Quantity,To Asset,To Quantity,Value USD,Fee USD,Counterparty,Is Taxable,Realized G/L,Holding Period,Source Lots,Destination Lots
0x9f2c…3c5b,2024-11-14T16:42:07+00:00,trade,ethereum,confirmed,USDC,1000.000000,ETH,0.284120,1000.00,3.71,1inch (dex),True,0.00,Short-term,USDC-ethereum-0007,ETH-ethereum-0031
Terminal window
curl -L "https://api.qeychain.xyz/api/v1/wallets/0x742d35cc6634c0532925a3b844bc9e7595f0beb0/transactions/export?format=json&range=1y" \
-H "Authorization: Bearer $QEYCHAIN_TOKEN"
{
"transactions": [
{
"id": "ethereum:0x9f2c…a41b",
"tx_hash": "0x9f2c8b7d5e3a1c0f4b6d8e2a9c7f1b3d5e7a9c1f3b5d7e9a1c3f5b7d9e1a3c5b",
"type": "trade",
"timestamp": "2024-11-14T16:42:07+00:00",
"chain": "ethereum",
"status": "confirmed",
"from_asset": "USDC",
"from_quantity": "1000.000000",
"to_asset": "ETH",
"to_quantity": "0.284120",
"value_usd": "1000.00",
"fee_usd": "3.71",
"transfers": [ "" ],
"counterparty": { "name": "1inch", "type": "dex", "label": "Aggregation Router", "is_exchange": false },
"lineage": null,
"tax_impact": null,
"is_spam": false
}
],
"count": 96,
"truncated": false
}

Every JSON response on this page uses the standard v1 envelope. Successful responses carry a success flag, the data payload, and a meta block; list responses add pagination (and, on the transaction list, filter_meta).

{
"success": true,
"data": { "…": "" },
"meta": {
"request_id": "uuid",
"timestamp": "2026-01-20T09:15:42.318000+00:00",
"version": "1.0.0"
}
}

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

{
"success": false,
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Transaction not found: 0x…",
"details": { "tx_hash": "0x…", "address": "0x…" }
},
"meta": {
"request_id": "uuid",
"timestamp": "2026-01-20T09:16:10.442000+00:00",
"version": "1.0.0"
}
}

Common error codes for these endpoints:

Code HTTP When
INVALID_ADDRESS 400 The wallet address is not a valid EVM, Bitcoin, or Solana address.
VALIDATION_ERROR 400 A parameter is malformed (e.g. a bad from_date on a custom export).
UNAUTHORIZED 401 Missing or invalid credentials.
RESOURCE_NOT_FOUND 404 The requested transaction hash was not found for the wallet.
DATA_PROVIDER_ERROR 502 The upstream data provider failed to return history.