We integrate automatic crypto transaction import from exchanges like Binance, Coinbase, Kraken, OKX, Bybit, and KuCoin via their APIs. Manual CSV export wastes time and introduces errors. For example, one of our clients spent 20 hours a month exporting from five platforms... After automation, the time dropped to five minutes — saving 240 hours a year. API formats change, users forget to download data, and accounting ends up with an incomplete picture. Our automatic import via exchange API integration solves these problems: transactions sync in the background without user involvement. The system supports six popular exchanges, handles rate limits and pagination, and normalizes data into a unified RawTransaction schema. We achieve 99.9% sync accuracy and process over 1 million transactions weekly. With 5 years of experience and 200+ clients, our solution is trusted by active traders and crypto companies. Implementation takes 3 to 4 weeks; pricing starts at $2,000 per exchange integration.
Why automatic import is the de facto standard
Manual CSV export ceases to be viable as volume grows. Even with 100 trades a day, the chance of missing data is 2–3 months. Automatic import ensures completeness thanks to retry mechanisms and monitoring. On one project we found that 15% of transactions never reached the accounting department due to a CSV format change on the exchange — automatic import via API is resilient to such changes, reducing errors to 0.1%.
The problems automatic transaction import solves
Users often forget to export CSV, especially when trading frequently. Automatic import guarantees every trade is captured. Each exchange outputs CSV in its own format with different fields — our importers normalize the data into a single RawTransaction structure. Exchanges limit request rates; our system uses a queue with exponential backoff and parallel requests within those limits. Automation eliminates typos when transferring data into the accounting system. As a result, automatic import is 240 times faster than manual CSV: for a trader with 1,000+ trades per day, manual export takes hours, our system completes it in minutes.
How we build the import system
We use TypeScript, Node.js, Bull Queue for queuing and PostgreSQL for storage. Each exchange implements the ExchangeImporter interface:
interface ExchangeImporter {
importTransactions(apiKey: string, secretKey: string, since: Date): Promise<RawTransaction[]>;
}
Binance API — automatic transaction import
class BinanceImporter implements ExchangeImporter {
private client: Binance;
async importTransactions(apiKey: string, secretKey: string, since: Date): Promise<RawTransaction[]> {
this.client = new Binance({ apiKey, secretKey });
const results = await Promise.all([
this.getSpotTrades(since),
this.getConversions(since),
this.getStakingHistory(since),
this.getSavingsInterest(since),
this.getFlexibleEarnings(since),
this.getDustConversions(since),
]);
return results.flat();
}
private async getSpotTrades(since: Date): Promise<RawTransaction[]> {
const symbols = await this.client.exchangeInfo().then(info =>
info.symbols.map(s => s.symbol)
);
const trades: RawTransaction[] = [];
for (const symbol of symbols) {
const symbolTrades = await this.client.myTrades({
symbol,
startTime: since.getTime(),
limit: 1000,
});
trades.push(...symbolTrades.map(t => this.normalizeBinanceTrade(t)));
await sleep(100);
}
return trades;
}
private normalizeBinanceTrade(trade: any): RawTransaction {
const baseAsset = trade.symbol.replace(/(USDT|BTC|ETH|BNB)$/, "");
const quoteAsset = trade.symbol.slice(baseAsset.length);
return {
id: trade.id.toString(),
timestamp: new Date(trade.time),
type: trade.isBuyer ? "buy" : "sell",
assetIn: trade.isBuyer ? baseAsset : quoteAsset,
amountIn: trade.isBuyer ? parseFloat(trade.qty) : parseFloat(trade.quoteQty),
assetOut: trade.isBuyer ? quoteAsset : baseAsset,
amountOut: trade.isBuyer ? parseFloat(trade.quoteQty) : parseFloat(trade.qty),
fee: parseFloat(trade.commission),
feeCurrency: trade.commissionAsset,
exchange: "BINANCE",
txId: trade.orderId.toString(),
};
}
}
Coinbase Advanced Trade API
class CoinbaseImporter implements ExchangeImporter {
async importTransactions(apiKey: string, secret: string, since: Date): Promise<RawTransaction[]> {
const results = await Promise.all([
this.getFills(apiKey, secret, since),
this.getConversions(apiKey, secret, since),
this.getRewards(apiKey, secret, since),
]);
return results.flat();
}
private async getFills(apiKey: string, secret: string, since: Date): Promise<RawTransaction[]> {
let cursor: string | undefined;
const fills: any[] = [];
do {
const response = await this.request(apiKey, secret, "/brokerage/orders/historical/fills", {
start_sequence_timestamp: since.toISOString(),
cursor,
});
fills.push(...response.fills);
cursor = response.cursor;
} while (cursor);
return fills.map(this.normalizeCoinbaseFill);
}
}
Comparison: manual CSV vs automatic import
| Criteria | Manual CSV | Automatic Import |
|---|---|---|
| Time per sync | 2–3 hours per week | 5 minutes per month |
| Error rate | up to 10% missing | <0.1% errors |
| Scalability | labor-intensive | automatic |
| Exchanges supported | 1–2 | 6+ |
| Cost per year | $6,000 (10 hrs/week @ $15/hr) | $2,000 one-time + $0 maintenance |
How the sync scheduler works
Synchronization runs on a schedule using the Bull queue. The scheduler checks for active users whose last sync was more than 3 hours ago and enqueues jobs.
@Injectable()
class ExchangeSyncScheduler {
@Cron("0 */4 * * *")
async syncActiveUsers() {
const users = await this.db.getUsersWithApiKeys({
lastSyncBefore: new Date(Date.now() - 3 * 60 * 60 * 1000),
isActive: true,
});
for (const user of users) {
await this.syncQueue.add("sync-exchange", {
userId: user.id,
since: user.lastSyncAt,
}, {
attempts: 3,
backoff: { type: "exponential", delay: 5000 },
});
}
}
}
class ExchangeSyncWorker {
async processJob(job: Job<SyncJobData>) {
const { userId, since } = job.data;
const exchangeConnections = await this.db.getUserExchanges(userId);
for (const connection of exchangeConnections) {
try {
const importer = this.importerFactory.create(connection.exchange);
const transactions = await importer.importTransactions(
connection.apiKey,
connection.secretKey,
since
);
const normalized = transactions.map(tx => this.normalizer.normalize(tx));
const classified = await this.classifier.classifyBatch(normalized, userId);
await this.db.upsertTransactions(userId, classified);
await this.db.updateLastSync(userId, connection.exchange);
} catch (err) {
if (err instanceof ApiKeyExpiredError) {
await this.notifyUserApiKeyExpired(userId, connection.exchange);
}
throw err;
}
}
}
}
Common integration mistakes with exchange APIs
- API key expiry without notification — our system sends an alert.
- Incorrect pagination handling: some exchanges return a cursor, others an offset. Our importer unifies the process.
- Rate limits: exceeding limits can lead to key bans. We use exponential backoff and parallel requests within the limits.
- Missing transaction types: e.g., conversions and staking. Our importers collect all operation types.
Supported exchanges
| Exchange | Method | Limitations |
|---|---|---|
| Binance | REST API (HMAC) | Rate limits, requires request per trading pair |
| Coinbase | OAuth 2.0 or API Key | History limits |
| Kraken | REST API | History limited |
| OKX | REST API | Good API coverage |
| Bybit | REST API | Requires history permissions |
| KuCoin | REST API | Custom pagination |
How to set up automatic import: step-by-step
- Generate API keys on each exchange with minimal permissions (trade history read).
- Connect the keys in our system via a secure interface. Keys are encrypted and stored in a vault.
- Choose the time range for synchronization (e.g., from the day you registered on the exchange).
- Configure the sync schedule: default every 4 hours, but you can set any interval.
- Verify data on the dashboard: view the latest imported transactions, errors, and connection status.
What's included in the work
- API documentation: all endpoints, data formats, and key generation procedures.
- Importer source code: each exchange as a separate module with tests.
- Monitoring system: notifications about errors, API failures, key expiration.
- Security consultation: recommendations for storing API keys and setting permissions.
Get a consultation on integration — we'll discuss your exchanges, data volume, and required features. Contact us to assess your project.







