Short answer: A crypto portfolio tracker API integration reads balances, fills and transfers from each exchange with read-only keys, stores them in an append-only ledger, computes positions and average cost, and reconciles the result against the balances each exchange reports. It needs no trading or withdrawal permission and places no orders. This is reporting software for your own holdings, which is a different thing from managing a portfolio for someone else.
Reading account data is the lowest-risk way to use an exchange API, because a read-only key cannot place an order or move funds. The difficulty lies elsewhere: histories arrive in pages with awkward time windows, fees come in other assets, transfers look like losses until they are paired, and a total means little until it has been reconciled.
This guide covers the build for product and engineering leads. Endpoints come from official documentation read on 4 November 2026, in alphabetical order, as examples and not recommendations. Tracker apps are neither named nor ranked. Key handling in general is covered in API key security and request signing in crypto exchange API integration.
How does a crypto portfolio tracker work?
A tracker is a one-way pipeline from exchange APIs to reports, and each stage can be tested on its own.
- Connectors call each exchange with a read-only key and keep a checkpoint per account, endpoint and symbol.
- Raw store keeps every response unchanged, so a parsing bug can be fixed by replaying history.
- Normalizer maps venue formats to one event type.
- Ledger holds those events as immutable rows.
- Calculator derives quantity, average cost and realized result.
- Reconciliation and reports compare the result with reported balances and publish totals with an as-of time.
A spreadsheet or a project from GitHub can be enough for a few accounts. Before adopting one, read its license and check where it stores keys. Custom work starts to make sense when several venues, sub-accounts or auditors are involved.
Read-only API keys: which permissions to grant
Grant only read permissions, and make the software verify this itself at start-up where the exchange exposes it.
| Exchange | Permission to grant | Can software check it? |
|---|---|---|
| Binance | Enable Reading only | Yes: GET /sapi/v1/account/apiRestrictions |
| Coinbase | view only | Yes: GET /api/v3/brokerage/key_permissions |
| Kraken | Query Funds, Query Open Orders & Trades, Query Closed Orders & Trades, Query Ledger Entries | Yes: POST /0/private/GetApiKeyInfo returns a permissions list |
This guard refuses any key that could do more than read. It was tested on the synthetic responses at the bottom:
def binance_key_is_read_only(r):
"""r is the response of GET /sapi/v1/account/apiRestrictions."""
writes = ["enableWithdrawals", "enableInternalTransfer", "permitsUniversalTransfer",
"enableSpotAndMarginTrading", "enableMargin", "enableFutures",
"enableVanillaOptions", "enableFixApiTrade", "enablePortfolioMarginTrading"]
return r.get("enableReading") is True and not any(r.get(f) for f in writes)
def coinbase_key_is_read_only(r):
"""r is the response of GET /api/v3/brokerage/key_permissions."""
return r.get("can_view") is True and not r.get("can_trade") and not r.get("can_transfer")
print(binance_key_is_read_only({"enableReading": True, "enableSpotAndMarginTrading": True}))
print(coinbase_key_is_read_only({"can_view": True, "can_trade": False, "can_transfer": False}))It prints False and then True. On Kraken, apply the same idea to the documented permissions list. Add an IP restriction as well, because a read-only key still exposes balances and trade history. Readers using platforms on the Turkish regulator's list can find their documented APIs in the Turkish crypto exchange APIs guide.
Balances, fills and transfers: which endpoints to call
A complete record needs three kinds of data, and each exchange splits them differently.
| Data | Binance Spot | Coinbase | Kraken Spot |
|---|---|---|---|
| Balances | GET /api/v3/account, weight 20 | GET /api/v3/brokerage/accounts (opens in a new tab), up to 250 per page, cursor | Balance, BalanceEx |
| Fills | GET /api/v3/myTrades, per symbol, up to 1,000 per call, fromId; weight 20 | GET /api/v3/brokerage/orders/historical/fills, cursor | TradesHistory, 1 to 100 per page, with_cursor; ofs is deprecated |
| Transfers | GET /sapi/v1/capital/deposit/hisrec and GET /sapi/v1/capital/withdraw/history, up to 1,000 per call, windows under 90 days | GET /v2/accounts/:account_id/transactions, view scope | Ledgers, 50 per page, with_cursor |
Three details cause most gaps:
- Windows. On Binance,
myTradeswith a start and end time cannot span more than 24 hours, so page byfromId. Transfer history needs a walk through 90-day windows. - Symbols. Binance trades are requested per symbol, so the connector must know every symbol the account has traded.
- Fees. A Binance fill reports
commissionandcommissionAsset, which may differ from either side of the trade. Store the fee as its own movement.
Also list what the tracker does not cover, such as futures, earn products or other sub-accounts, so a total is never mistaken for the whole account.
Pagination and rate limits: planning a backfill
The first import is the expensive part, so run it as a resumable job with checkpoints and a request budget.
Limits differ in kind. Binance counts request weight, with 6,000 per minute in live exchangeInfo on 4 November 2026, and returns HTTP 429 with Retry-After when exceeded. Kraken's documentation describes a call counter per key where ledger and trade history calls add 2, and the counter decays by 0.33 to 1 per second depending on tier; going over returns EAPI:Rate limit exceeded.
Hypothetical example. A Kraken account has 12,000 ledger entries. At 50 per page that is 240 calls, adding 480 to the counter. At the Starter decay of 0.33 per second, the import takes about 24 minutes; at the Pro decay of 1 per second, about 8 minutes. On Binance, a sweep of 50 symbols at weight 20 uses 1,000 of the 6,000 weight available per minute, shared with anything else on that IP.
After the backfill, poll on a schedule and overlap each window slightly. The overlap is harmless because the ledger ignores IDs it has already stored. Binance's limits are explained in more depth in Binance API rate limits.
An append-only ledger: the core data model
Store every movement as an immutable row with a venue-scoped ID, and correct mistakes by adding rows instead of editing old ones.
| Field | Notes |
|---|---|
event_id | Venue plus native ID, under a unique constraint. On Coinbase, entry_id identifies a fill, while trade_id is documented as not unique for adjusted fills |
account, time | Exchange account or sub-account, and time in UTC |
kind | Buy, sell, fee, deposit, withdrawal, reward, adjustment. Kraken's ledger types include trade, deposit, withdrawal, staking and adjustment |
asset, quantity | Exact decimals, stored as text or a decimal type, never floats |
value, value_source | Value in reporting currency and where the price came from |
raw_ref | Pointer to the stored raw response |
Pair transfers between your own accounts by asset, amount and time, and keep the network fee as a separate row. Valuing history needs a price source, and redistribution terms apply if the numbers are shown to others; see crypto price APIs.
Average-cost calculation in Python
Average cost adds each purchase and its fee to a pool, then removes cost in proportion when units leave.
This runs on a synthetic ledger with a made-up asset, and shows a replayed row being ignored, an own transfer leaving cost unchanged and one reconciliation failure:
from decimal import Decimal as D
# Synthetic normalized ledger, oldest first. "XYZ" is a made-up asset.
# (event id, venue, kind, quantity, gross value, fee in quote currency)
ledger = [
("A:t1", "A", "buy", "1.0", "100", "1.0"),
("A:t2", "A", "buy", "1.0", "200", "2.0"),
("A:t2", "A", "buy", "1.0", "200", "2.0"), # re-fetched page, same ID
("A:t3", "A", "sell", "0.5", "150", "1.5"),
("A:w1", "A", "transfer_out", "0.5", "0", "0"),
("B:d1", "B", "transfer_in", "0.5", "0", "0"),
("A:w1f", "A", "network_fee", "0.01", "0", "0"),
]
seen, qty, cost, realized, balances = set(), D(0), D(0), D(0), {}
for eid, venue, kind, q, value, fee in ledger:
if eid in seen:
continue
seen.add(eid)
q, value, fee = D(q), D(value), D(fee)
sign = 1 if kind in ("buy", "transfer_in") else -1
balances[venue] = balances.get(venue, D(0)) + sign * q
if kind == "buy":
qty, cost = qty + q, cost + value + fee
elif kind in ("sell", "network_fee"):
removed = cost / qty * q
qty, cost = qty - q, cost - removed
if kind == "sell":
realized += value - fee - removed
# own transfers move units between venues, not cost
print("quantity", qty, "cost", round(cost, 3), "average", round(cost / qty, 2))
print("realized", round(realized, 2))
reported = {"A": D("0.99"), "B": D("0.48")} # from balance endpoints
for venue, computed in sorted(balances.items()):
gap = reported[venue] - computed
print(venue, "computed", computed, "reported", reported[venue],
"OK" if gap == 0 else f"EXCEPTION {gap}")Output:
quantity 1.49 cost 225.735 average 151.50
realized 72.75
A computed 0.99 reported 0.99 OK
B computed 0.5 reported 0.48 EXCEPTION -0.02The arithmetic checks by hand: (100 + 1 + 200 + 2) / 2 = 151.50, and the sale realizes 150 − 1.5 − 75.75 = 72.75. Treating a network fee as a disposal is one modeling choice, and here its 1.515 of cost leaves the pool without reaching the realized figure, so report it on its own line. A sale before any buy stops the sketch with a division error on the empty pool, so load units from outside as opening rows with their cost. Tax rules decide the method for filing, and they differ: HMRC's manual applies same-day and 30-day matching first and pools the rest of a token type in a section 104 pool, while the IRS FAQ, as read on 4 November 2026, applies first in, first out unless specific units are identified, and Revenue Procedure 2024-28 (opens in a new tab) explains that from 1 January 2025 Treasury Regulation 1.1012-1(j) applies those rules within each wallet or account. Keep the method configurable, label it on every report and have foreign readers check their home-country treatment. Turkish rules are covered in Turkey crypto tax.
Crypto reconciliation software: proving the numbers
Reconciliation compares the balance computed from the ledger with the balance the exchange reports, per account and asset, and treats any gap as an open item.
Gaps usually have ordinary causes: a transfer still in flight, a reward or small-balance conversion the connector does not fetch, a delisted asset, or a product outside the tracker's scope. In the sample above venue B shows 0.02 less than computed, which should open an exception rather than a silent correction. Once the cause is known, add a labeled adjustment row. Report the as-of time and the last successful sync per venue, and mark a total as provisional while exceptions are open. Finance teams will ask for this before they trust any other number in crypto reporting software.
Tracking and reporting are not portfolio management
Reading data and reporting it is software; deciding or trading for someone else is a regulated or prohibited activity in many places.
MiCA, Article 3(1)(25), defines portfolio management of crypto-assets as managing portfolios under mandates given by clients on a discretionary, client-by-client basis, and Article 3(1)(24) defines advice as personalized recommendations. In Turkey, Communiqué III-35/B.2 (opens in a new tab), Article 15(3), prohibits managing customer portfolios on each customer's behalf for direct or indirect benefit, as a proxy or on oral authority with the same effect. That text sits in the platform rules, and whether it reaches a software company is a question for a lawyer; the practical answer is to stay well clear of it.
So the wording and features matter. Call the product a tracker or reporting tool. Avoid buy or sell prompts, rebalancing instructions and any promise of results; alerts should state facts such as a failed sync or a balance gap.
Rules depend on your jurisdiction and each platform's terms. For readers in Turkey, Article 99/A of Capital Markets Law No. 6362 treats activity by foreign platforms directed at Turkish residents as unauthorized, and the Capital Markets Board says appearing on its operating list (opens in a new tab) does not mean a firm is authorized.
Fincode is a software company and does not provide investment advice, portfolio management or crypto asset services. Read-only reporting is the kind of work described on the exchange API integration page and in the crypto portfolio risk dashboard scenario.
Frequently asked questions
Is there a free API for a crypto portfolio tracker?
Exchanges generally do not charge for reading your own account through their APIs, within rate limits. Historical prices for valuation may need a paid data plan, depending on the provider and your use.
Can a read-only API key move my funds?
A key with read permission only cannot place orders or withdraw. It still reveals balances and trades, so keep it secret, restrict its IP and verify its permissions where the exchange allows.
Is a crypto portfolio tracker app better than Excel?
A spreadsheet works for a few accounts and manual imports. Once you need automatic sync, reconciliation or an audit trail, a small service with a ledger is easier to trust.
What does it take to develop crypto asset management software?
For tracking and reporting: connectors, a ledger, cost calculation and reconciliation, as described here. Software that decides or places trades for others is a different product with licensing questions.
Sources
- Binance Spot API documentation: REST API (account, myTrades, limits) (opens in a new tab)
- Binance Wallet API: Capital (deposit and withdraw history) (opens in a new tab)
- Binance Wallet API: Get API key permission (opens in a new tab)
- Coinbase Advanced Trade API: List Fills (opens in a new tab)
- Coinbase Advanced Trade API: Get API Key Permissions (opens in a new tab)
- Coinbase App API: Transactions (opens in a new tab)
- Kraken: API key permissions (opens in a new tab)
- Kraken: Get API Key Info (opens in a new tab)
- Kraken: Spot REST rate limits (opens in a new tab)
- Kraken: Get Trades History (opens in a new tab)
- Kraken: Get Ledgers Info (opens in a new tab)
- HMRC Cryptoassets Manual: CRYPTO22200, pooling (opens in a new tab)
- IRS: Frequently asked questions on virtual currency transactions (Q39 to Q41) (opens in a new tab)
- ESMA interactive single rulebook: MiCA Article 3, definitions (opens in a new tab)
- Capital Markets Law No. 6362, consolidated text, Article 99/A (Turkish) (opens in a new tab)
This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the editorial policy.