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.
Authentication
Section titled “Authentication”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.
Multi-wallet ownership
Section titled “Multi-wallet ownership”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.
List transactions
Section titled “List transactions”GET /api/v1/wallets/{address}/transactionsReturns 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).
Path parameters
Section titled “Path parameters”| Name | Type | Required | Description |
|---|---|---|---|
address |
string | yes | Wallet address (EVM 0x…, Bitcoin, or Solana). |
Query parameters
Section titled “Query parameters”| Name | Type | Required | Description |
|---|---|---|---|
page |
integer | no | Page number, 1-indexed. Default 1. |
per_page |
integer | no | Items per page, 1–100. 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). |
Example request
Section titled “Example request”curl "https://api.qeychain.xyz/api/v1/wallets/0x742d35cc6634c0532925a3b844bc9e7595f0beb0/transactions?type=trade&chain=ethereum&per_page=2" \ -H "Authorization: Bearer $QEYCHAIN_TOKEN"Example response
Section titled “Example response”{ "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.
Classification vocabulary
Section titled “Classification vocabulary”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 a single transaction
Section titled “Get a single transaction”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.
Path parameters
Section titled “Path parameters”| Name | Type | Required | Description |
|---|---|---|---|
address |
string | yes | Wallet address. |
tx_hash |
string | yes | The transaction hash to look up (case-insensitive). |
Query parameters
Section titled “Query parameters”| Name | Type | Required | Description |
|---|---|---|---|
owned_wallets |
string[] | no | All addresses owned by the user, for accurate cross-wallet lineage. |
Example request
Section titled “Example request”curl "https://api.qeychain.xyz/api/v1/wallets/0x742d35cc6634c0532925a3b844bc9e7595f0beb0/transactions/0x9f2c8b7d5e3a1c0f4b6d8e2a9c7f1b3d5e7a9c1f3b5d7e9a1c3f5b7d9e1a3c5b" \ -H "Authorization: Bearer $QEYCHAIN_TOKEN"Example response
Section titled “Example response”{ "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
Section titled “Lineage”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_OUTon the source chain is matched to aBRIDGE_INon 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_TRANSFERand the receiving lot inherits basis from the sending lot. This only works when you passowned_wallets. - Wraps, migrations, and staking — preserve basis across the wrapped, migrated, or staked representation.
Export transactions
Section titled “Export transactions”GET /api/v1/wallets/{address}/transactions/exportApplies 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.
Path parameters
Section titled “Path parameters”| Name | Type | Required | Description |
|---|---|---|---|
address |
string | yes | Wallet address. |
Query parameters
Section titled “Query parameters”| 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.
Response headers and the truncation flag
Section titled “Response headers and the truncation flag”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).
Example: CSV export
Section titled “Example: CSV export”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.csvThe -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 Lots0x9f2c…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-0031Example: JSON export
Section titled “Example: JSON export”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}Response envelope
Section titled “Response envelope”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. |
