TABLE OF CONTENTS

How to Track Crypto Wallet Transactions, Balances, Profits & Loss

DeFi & Onchain Analytics
Portfolio Tracking
Onchain
Paid Subscribers

TL;DR

CoinGecko API provides a single, multi-chain solution for tracking a crypto wallet's balances, trades, token transfers, and PnL using a single provider.
 

A wallet balance shows the assets a wallet currently holds. To understand its activity, portfolio dashboards, tax tools, and wallet analytics also need trade and transfer data. The trades endpoint shows what was swapped and at what price, while the transfers endpoint shows which tokens moved in or out and in which direction. This provides the data needed to track both trades and asset movements.

This guide uses CoinGecko API's Onchain Wallet endpoints to retrieve both data sets for a wallet address and combine them into a single activity feed, sorted by time and labeled by type. It also covers how to add realized and unrealized PnL. Trade responses include USD pricing, so no separate price feed is needed to calculate trade values.

How to Track Crypto Wallet Transactions, Balances, Profits & Loss


Prerequisites

You’ll need a CoinGecko API key on the Analyst plan or above to use all four endpoints.

This guide uses the same Ethereum wallet address throughout, so the balance snapshot, trade history, transfer history, and PnL summary all describe the same wallet. A trade represents a swap with a buy or sell side and price, while a transfer represents a token moving into or out of a wallet through a swap, send, deposit, or airdrop. A single swap appears in both data sets as a trade and as the token transfers that moved the assets.

To get started, install the dependencies.

1
2
requests>=2.31.0
python-dotenv>=1.0.0
pip install -r requirements.txt
1
COINGECKO_PRO_API_KEY=CG-your_api_key_here
💡 Prefer less setup? You can call these endpoints with CoinGecko's official Python SDK or TypeScript SDK instead of raw HTTP requests. The SDK handles the base URL, headers, and authentication for you, so there is less boilerplate to write.

How to Fetch a Wallet's Balances Across Multiple Networks

CoinGecko API’s Token Balances by Wallet Address endpoint returns every token a wallet holds across one or more networks in a single call. Pass a comma-separated networks list, such as eth, base, or arbitrum, to see a wallet’s full multi-chain portfolio in one request instead of querying each chain’s RPC separately and merging the results yourself.

Each holding includes price_usd and value_usd, so you don’t need a separate price feed. Use token_type to filter for native or non_native (ERC-20 / SPL) tokens, and value_usd_min or reserve_in_usd_min to filter out dust and low-liquidity spam tokens.

Verify the endpoint with a curl request:

curl --request GET \
  --url "https://pro-api.coingecko.com/api/v3/onchain/wallets/0x9642b23ed1e01df1092b92641051881a322f5d4e/balances?networks=eth,base,arbitrum" \
  --header "x-cg-pro-api-key: CG-your_api_key_here"

This gives you a wallet’s current holdings and USD value to pair with the trade, transfer, and PnL history covered next. Every call in this guide uses the same API client, so set it up once before working through the endpoints.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import os
import requests
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv("COINGECKO_PRO_API_KEY")
BASE_URL = "https://pro-api.coingecko.com/api/v3"
HEADERS = {"x-cg-pro-api-key": API_KEY}

def get_balances(address, networks):
    """Fetch a wallet's token balances across networks."""
    url = f"{BASE_URL}/onchain/wallets/{address}/balances"
    response = requests.get(url, headers=HEADERS, params={"networks": networks}, timeout=20)
    response.raise_for_status()
    return response.json()

def get_trades(network, address, **params):
    """Fetch a wallet's trades on a single network."""
    url = f"{BASE_URL}/onchain/networks/{network}/wallets/{address}/trades"
    response = requests.get(url, headers=HEADERS, params=params, timeout=20)
    response.raise_for_status()
    return response.json()

def get_transfers(network, address, **params):
    """Fetch a wallet's token transfers on a single network."""
    # SDK equivalent (pip install coingecko_sdk):
    # from coingecko_sdk import Coingecko
    # client = Coingecko()  # reads COINGECKO_PRO_API_KEY automatically
    # response = client.onchain.networks.wallets.transfers.get(address, network=network, **params)
    url = f"{BASE_URL}/onchain/networks/{network}/wallets/{address}/transfers"
    response = requests.get(url, headers=HEADERS, params=params, timeout=20)
    response.raise_for_status()
    return response.json()

def get_pnl(address, networks):
    """Fetch a wallet's realized and unrealized PnL across networks in one VM family."""
    url = f"{BASE_URL}/onchain/wallets/{address}/pnl"
    response = requests.get(url, headers=HEADERS, params={"networks": networks}, timeout=20)
    response.raise_for_status()
    return response.json()

Calling get_balances("0x9642b23ed1e01df1092b92641051881a322f5d4e", "eth,base,arbitrum") returns a data object. The top-level networks array shows the USD value and holding count per chain, so you can see how a wallet’s assets are distributed before drilling into individual balances:

{
  "data": {
    "id": "0x9642b23ed1e01df1092b92641051881a322f5d4e",
    "type": "wallet_balances",
    "attributes": {
      "wallet_address": "0x9642b23ed1e01df1092b92641051881a322f5d4e",
      "total_value_usd": "8452.10",
      "total_holdings": 14,
      "networks": [
        {
          "network": "eth",
          "value_usd": "6210.55",
          "holdings": 9,
          "last_updated_at": "2026-09-24T09:00:00Z"
        },
        {
          "network": "base",
          "value_usd": "2241.55",
          "holdings": 5,
          "last_updated_at": "2026-09-24T09:00:00Z"
        }
      ],
      "balances": [
        {
          "network": "eth",
          "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
          "token_type": "erc20",
          "name": "Wrapped Ether",
          "symbol": "WETH",
          "decimals": 18,
          "coingecko_coin_id": "weth",
          "balance_raw": "412000000000000000",
          "balance": "0.412",
          "price_usd": "2521.47",
          "value_usd": "1038.85",
          "h24_price_change_percentage": "1.32",
          "total_reserve_in_usd": "48210233.10"
        }
      ]
    }
  }
}

How to Fetch a Wallet's Trade History

CoinGecko API’s Trades by Wallet Address endpoint returns trade history for a specified network and wallet address. Each result is labeled as a buy or sell and includes the USD price for both sides of the swap. The endpoint supports 250+ networks, including Ethereum (network="eth") and Solana (network="solana")

Pass from and to together to query a fixed window of up to 30 days, or omit both parameters to default to the most recent 7 days. If the response contains more results, pass the next_cursor value from meta in your next request to fetch the next page.

Each entry's attributes object contains kind, which is either buy or sell, along with price_from_in_usd and price_to_in_usd for both sides of the swap and volume_in_usd for the total trade volume in USD. The response looks like this:

{
  "id": "eth_26043243_0x3a1c...142",
  "type": "trade",
  "attributes": {
    "pool_address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
    "pool_dex": "uniswap_v3",
    "block_number": 26043243,
    "block_timestamp": "2026-09-23T22:03:47Z",
    "tx_hash": "0x3a1c...142",
    "tx_from_address": "0x9642b23ed1e01df1092b92641051881a322f5d4e",
    "kind": "buy",
    "from_token_address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
    "to_token_address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
    "from_token_amount": "500.000000",
    "to_token_amount": "0.198342",
    "price_from_in_usd": "1.0002",
    "price_to_in_usd": "2521.47",
    "volume_in_usd": "500.10"
  }
}

How to Fetch a Wallet's Transfers

CoinGecko API’s Token Transfers by Wallet Address endpoint returns token transfer history for a specified network and wallet address. Each result includes a transfer direction and a resolved token amount, separate from its price. This captures activity that a trades-only view misses, such as deposits, sends, and airdrops that move tokens without a swap.

The from and to parameters define the date range, while the cursor fetches additional pages of results. The direction parameter filters results to incoming (in) or outgoing (out) transfers.

Each entry includes direction, which is out when the wallet is the from_address and in when it is the to_address. It also includes amount, the token amount transferred. Here is the response:

{
  "id": "eth_26046243_0x08550aecf7a762599ceaac52bddfdb42cec1ebfc14cb9bfd9c895e7278502134_413_out",
  "type": "transfer",
  "attributes": {
    "block_number": 26046243,
    "tx_hash": "0x08550aecf7a762599ceaac52bddfdb42cec1ebfc14cb9bfd9c895e7278502134",
    "direction": "out",
    "token_address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
    "amount_raw": "99598300",
    "amount": "99.5983",
    "block_timestamp": "2026-09-24T08:51:11Z",
    "from_address": "0x9642b23ed1e01df1092b92641051881a322f5d4e",
    "to_address": "0xb24c9abdeb1b06c19dece441fa259d69631e1e3b",
    "name": "Tether USD",
    "symbol": "USDT",
    "decimals": 6
  }
}

How to Fetch a Wallet's PnL Across Multiple Networks

The PnL by Wallet Address endpoint returns a wallet’s realized and unrealized profit and loss across one or more networks in a single call. It uses the same multi-chain design as the balances endpoint, providing a wallet’s full trading performance without stitching together per-chain results.

Pass a comma-separated networks list, as long as every network belongs to the same VM family. For example, eth, base, and arbitrum can be queried together because they’re all EVM, while Solana requires a separate request. The response includes total_realized_pnl_usd and total_unrealized_pnl_usd for the requested networks combined, a networks array with the PnL breakdown by chain, and a token_stats array with the breakdown by token.

The endpoint returns a combined PnL summary across the requested networks, along with a per-network breakdown, giving you a consolidated view of wallet performance without requiring separate PnL requests and manual aggregation for each chain. The API returns the following:

{
  "data": {
    "id": "0x9642b23ed1e01df1092b92641051881a322f5d4e",
    "type": "wallet_pnl",
    "attributes": {
      "wallet_address": "0x9642b23ed1e01df1092b92641051881a322f5d4e",
      "total_realized_pnl_usd": "1284.52",
      "total_unrealized_pnl_usd": "312.90",
      "total_tokens": 14,
      "networks": [
        {
          "network": "eth",
          "realized_pnl_usd": "980.15",
          "unrealized_pnl_usd": "150.40",
          "tokens": 9
        },
        {
          "network": "base",
          "realized_pnl_usd": "304.37",
          "unrealized_pnl_usd": "162.50",
          "tokens": 5
        }
      ],
      "token_stats": [
        {
          "network": "eth",
          "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
          "name": "Wrapped Ether",
          "symbol": "WETH",
          "decimals": 18,
          "realized_pnl_usd": "540.22",
          "unrealized_pnl_usd": "88.10",
          "token_balance": "0.412",
          "average_buy_price_usd": "2410.00",
          "average_sell_price_usd": "2601.55",
          "total_buy_count": 6,
          "total_sell_count": 4,
          "total_buy_usd": "2901.84",
          "total_sell_usd": "2061.63"
        }
      ]
    }
  }
}

How to Build a Multi-Chain Crypto Wallet Portfolio Dashboard and Feed

Balances, trades, transfers, and PnL each provide a different view of a wallet. Pulling all four for the same wallet address gives you a complete multi-chain view of holdings, activity, and performance from one API. Trades and transfers combine into one chronological activity feed, while balances and PnL provide separate portfolio and performance summaries.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
from wallet_client import get_balances, get_trades, get_transfers, get_pnl

WALLET = "0x9642b23ed1e01df1092b92641051881a322f5d4e"
NETWORK = "eth"
NETWORKS = "eth,base,arbitrum"

def fetch_all_trades(network, address):
    """Page through every trade using the cursor CoinGecko returns."""
    trades = []
    cursor = None
    while True:
        params = {"per_page": 100}
        if cursor:
            params["cursor"] = cursor
        payload = get_trades(network, address, **params)
        trades.extend(payload["data"])
        cursor = payload["meta"]["next_cursor"]
        if not cursor:
            break
    return trades

def fetch_all_transfers(network, address):
    """Page through every transfer using the cursor CoinGecko returns."""
    transfers = []
    cursor = None
    while True:
        params = {"per_page": 100}
        if cursor:
            params["cursor"] = cursor
        payload = get_transfers(network, address, **params)
        transfers.extend(payload["data"])
        cursor = payload["meta"]["next_cursor"]
        if not cursor:
            break
    return transfers

def build_activity_feed(trades, transfers):
    """Merge trades and transfers into one feed, labeled and sorted by time."""
    feed = []

    for trade in trades:
        attrs = trade["attributes"]
        feed.append({
            "type": "trade",
            "timestamp": attrs["block_timestamp"],
            "tx_hash": attrs["tx_hash"],
            "kind": attrs["kind"],
            "volume_usd": attrs["volume_in_usd"],
        })

    for transfer in transfers:
        attrs = transfer["attributes"]
        feed.append({
            "type": "transfer",
            "timestamp": attrs["block_timestamp"],
            "tx_hash": attrs["tx_hash"],
            "direction": attrs["direction"],
            "token": attrs["symbol"],
            "amount": attrs["amount"],
        })

    feed.sort(key=lambda entry: entry["timestamp"], reverse=True)
    return feed

if __name__ == "__main__":
    balances = get_balances(WALLET, NETWORKS)
    portfolio = balances["data"]["attributes"]
    print(f"Portfolio value: ${portfolio['total_value_usd']} across {len(portfolio['networks'])} networks\n")

    trades = fetch_all_trades(NETWORK, WALLET)
    transfers = fetch_all_transfers(NETWORK, WALLET)
    feed = build_activity_feed(trades, transfers)

    for entry in feed[:10]:
        if entry["type"] == "trade":
            print(f"{entry['timestamp']}  TRADE     {entry['kind']:<4}  ${entry['volume_usd']}")
        else:
            print(f"{entry['timestamp']}  TRANSFER  {entry['direction']:<3}   {entry['amount']} {entry['token']}")

    pnl = get_pnl(WALLET, NETWORKS)
    summary = pnl["data"]["attributes"]
    print(f"\nRealized PnL: ${summary['total_realized_pnl_usd']}")
    print(f"Unrealized PnL: ${summary['total_unrealized_pnl_usd']}")
    for network in summary["networks"]:
        print(f"  {network['network']:<10} realized ${network['realized_pnl_usd']}  unrealized ${network['unrealized_pnl_usd']}")

fetch_all_trades and fetch_all_transfers fetch data for one network at a time. To build a feed across multiple networks, run both for each network and merge the results by timestamp.

Once trades and transfers are in the same list, the type field identifies each event as a trade or transfer. This matters because one swap can produce one trade entry and two transfer entries for the tokens that moved.

PnL stays separate from the activity feed. Trades and transfers are individual events with timestamps, while realized and unrealized PnL are totals. get_pnl therefore returns a separate PnL summary instead of adding PnL to the event list.

Note: If you are building the feed for a Solana wallet, use fetch_all_trades with network="solana" and add PnL separately with get_pnl.

Running activity_feed.py prints the portfolio value, merged activity feed, and PnL summary, including the per-network breakdown:

Portfolio value: $8452.10 across 2 networks

2026-09-24T09:14:02Z  TRADE     sell  $312.40
2026-09-24T08:51:11Z  TRANSFER  out   99.5983 USDT
2026-09-24T08:51:11Z  TRANSFER  out   1803.28 LINK
2026-09-23T22:03:47Z  TRADE     buy   $500.10
2026-09-23T19:40:15Z  TRANSFER  in    250.0 USDC

Realized PnL: $1284.52
Unrealized PnL: $312.90
  eth        realized $980.15  unrealized $150.40
  base       realized $304.37  unrealized $162.50

The per-network breakdown enables a multi-chain dashboard to be built from a single wallet address. The API provides balances, trades, transfers, and aggregated PnL across the requested networks, all through a single provider.

The dashboard below is built directly from the data above:

Multi-Chain Wallet Tracking Use Case

Subscribe now - Build multi-chain apps with CoinGecko API market and wallet data across 250+ networks.


Conclusion

CoinGecko API provides the data to build a complete, multi-chain view of any wallet, including balances, trade history, token transfers, and realized and unrealized PnL, across 250+ networks through one API. Query balances and PnL across multiple chains in a single call, then combine trades and transfers into one onchain activity feed with USD pricing.

Once you have wallet activity covered, combine it with CoinGecko’s market data endpoints to build a multi-chain crypto platform with real-time prices and market data alongside wallet balances, trades, transfers, and PnL. Create a Crypto Portfolio Dashboard shows how to bring these data points together in a portfolio dashboard. You can also track newly created pools across networks or stream real-time crypto prices with REST and WebSocket

Ready to start building? The CoinGecko API Analyst plan unlocks the onchain wallet endpoints used in this guide, along with 500K monthly API call credits and a 500 RPM rate limit. If you are not ready to subscribe yet, start with a free Demo API key to explore 60+ endpoints with 10K free monthly API call credits.

CoinGecko's Content Editorial Guidelines
CoinGecko’s content aims to demystify the crypto industry. While certain posts you see may be sponsored, we strive to uphold the highest standards of editorial quality and integrity, and do not publish any content that has not been vetted by our editors.
Learn more
Want to be the first to know about upcoming airdrops?
Subscribe to the CoinGecko Daily Newsletter!
Join 600,000+ crypto enthusiasts, traders, and degens in getting the latest crypto news, articles, videos, and reports by subscribing to our FREE newsletter.
Tell us how much you like this article!
Ru Jun Ang
Ru Jun Ang
Ru Jun is a growth marketer who’s curious about crypto, APIs, and the technology behind digital assets, with a focus on market data, on-chain trends, and products shaping the Web3 space.

Related Articles

Select Currency
Suggested Currencies
USD
US Dollar
IDR
Indonesian Rupiah
TWD
New Taiwan Dollar
EUR
Euro
KRW
South Korean Won
JPY
Japanese Yen
RUB
Russian Ruble
CNY
Chinese Yuan
Fiat Currencies
AED
United Arab Emirates Dirham
ARS
Argentine Peso
AUD
Australian Dollar
BDT
Bangladeshi Taka
BHD
Bahraini Dinar
BMD
Bermudian Dollar
BRL
Brazil Real
CAD
Canadian Dollar
CHF
Swiss Franc
CLP
Chilean Peso
CZK
Czech Koruna
DKK
Danish Krone
GBP
British Pound Sterling
GEL
Georgian Lari
HKD
Hong Kong Dollar
HUF
Hungarian Forint
ILS
Israeli New Shekel
INR
Indian Rupee
KWD
Kuwaiti Dinar
LKR
Sri Lankan Rupee
MMK
Burmese Kyat
MXN
Mexican Peso
MYR
Malaysian Ringgit
NGN
Nigerian Naira
NOK
Norwegian Krone
NZD
New Zealand Dollar
PHP
Philippine Peso
PKR
Pakistani Rupee
PLN
Polish Zloty
SAR
Saudi Riyal
SEK
Swedish Krona
SGD
Singapore Dollar
THB
Thai Baht
TRY
Turkish Lira
UAH
Ukrainian hryvnia
VND
Vietnamese đồng
ZAR
South African Rand
XDR
IMF Special Drawing Rights
Cryptocurrencies
BTC
Bitcoin
ETH
Ether
LTC
Litecoin
BCH
Bitcoin Cash
BNB
Binance Coin
EOS
EOS
XRP
XRP
XLM
Lumens
LINK
Chainlink
DOT
Polkadot
YFI
Yearn.finance
SOL
Solana
Bitcoin Units
BITS
Bits
SATS
Satoshi
Commodities
XAG
Silver - Troy Ounce
XAU
Gold - Troy Ounce
Select Language
Popular Languages
EN
English
RU
Русский
DE
Deutsch
PL
język polski
ES
Español
VI
Tiếng việt
FR
Français
PT-BR
Português
All Languages
AR
العربية
BG
български
CS
čeština
DA
dansk
EL
Ελληνικά
FI
suomen kieli
HE
עִבְרִית
HI
हिंदी
HR
hrvatski
HU
Magyar nyelv
ID
Bahasa Indonesia
IT
Italiano
JA
日本語
KO
한국어
LT
lietuvių kalba
NL
Nederlands
NO
norsk
RO
Limba română
SK
slovenský jazyk
SL
slovenski jezik
SV
Svenska
TH
ภาษาไทย
TR
Türkçe
UK
украї́нська мо́ва
ZH
简体中文
ZH-TW
繁體中文
Welcome to CoinGecko
Welcome back!
Login or Sign up in seconds
or
Sign in with . Not you?
Forgot your password?
Didn't receive confirmation instructions?
Resend confirmation instructions
Password must contain at least 8 characters including 1 uppercase letter, 1 lowercase letter, 1 number, and 1 special character
By continuing, you acknowledge that you've read and agree fully to our Terms of Service and Privacy Policy.
Get Price Alerts with CoinGecko App
Forgot your password?
You will receive an email with instructions on how to reset your password in a few minutes.
Resend confirmation instructions
You will receive an email with instructions for how to confirm your email address in a few minutes.
Get the CoinGecko app.
Scan this QR code to download the app now App QR Code Or check it out in the app stores
Add NFT
CoinGecko
Better on the app
Real-time price alerts and a faster, smoother experience.