@finconvert/sdk
v0.1.0
Published
Official TypeScript SDK for the FinConvert API — convert between bank statement formats (MT940, CAMT.053, OFX, CSV, JSON)
Downloads
44
Maintainers
Readme
@finconvert/sdk
Official TypeScript SDK for the FinConvert API — convert between bank statement formats (MT940, CAMT.053, OFX, CSV, JSON) with a single API call.
- Zero dependencies
- Works in Node.js 18+, Bun, Deno, and browsers
- Fully typed
- Built-in retry with exponential backoff and
Retry-Afterrespect - Rate limit metadata on every response
Installation
npm install @finconvert/sdk
# or
bun add @finconvert/sdk
# or
pnpm add @finconvert/sdkQuick Start
import { FinConvert } from '@finconvert/sdk';
import { readFileSync } from 'node:fs';
const fc = new FinConvert('fc_live_...');
// Convert a MT940 file to CSV
const result = await fc.convert({
file: readFileSync('statement.mt940'),
to: 'csv',
});
console.log(result.filename); // "converted.csv"
console.log(result.mimeType); // "text/csv"
console.log(result.detectedFormat); // "mt940"
// result.data is an ArrayBuffer — decode it or write to disk
const text = new TextDecoder().decode(result.data);Configuration
const fc = new FinConvert('fc_live_...', {
baseUrl: 'https://api.finconvert.dev/v1', // default
maxRetries: 2, // default (0 disables retries)
timeout: 30_000, // default, ms
});
// API key can also come from FINCONVERT_API_KEY
const fc = new FinConvert(); // reads process.env.FINCONVERT_API_KEYMethods
convert(params) — Convert a file between formats
const result = await fc.convert({
file: buffer, // FileInput (see below)
to: 'csv', // required: 'csv' | 'json' | 'camt053' | 'ofx'
from: 'mt940', // optional, auto-detected if omitted
});
// result: {
// data: ArrayBuffer,
// filename: string,
// mimeType: string,
// detectedFormat: string,
// rateLimit: { limit, remaining, reset },
// }parse(params) — Parse to Universal Transaction Model
const result = await fc.parse({
file: buffer,
from: 'mt940', // optional
});
// result: {
// format: string,
// statements: BankStatement[],
// rateLimit: { limit, remaining, reset },
// }
for (const statement of result.statements) {
console.log(statement.accountId, statement.currency);
for (const tx of statement.transactions) {
console.log(tx.date, tx.amount, tx.description);
}
}detect(params) — Identify the format of a file
const result = await fc.detect({ file: buffer });
// result: { format: 'mt940', formatName: 'MT940 (SWIFT)', rateLimit: {...} }formats() — List supported formats
const result = await fc.formats();
// result: {
// input: ['mt940', 'camt053', 'ofx', 'csv'],
// output: ['csv', 'json', 'camt053', 'ofx'],
// }File Inputs
All methods that take a file parameter accept any of these types:
type FileInput =
| Blob // Browser, Bun, Node 20+
| Buffer // Node.js
| ArrayBuffer // Universal
| Uint8Array // Universal
| ReadableStream // Streaming
| string // File path (Node.js / Bun only)// From a file path (Node.js / Bun)
await fc.convert({ file: 'statement.mt940', to: 'csv' });
// From a Buffer (Node.js)
import { readFileSync } from 'node:fs';
await fc.convert({ file: readFileSync('statement.mt940'), to: 'csv' });
// From a Blob (browser / Bun)
const fileInput = document.querySelector('input[type=file]') as HTMLInputElement;
const file = fileInput.files![0];
await fc.convert({ file, to: 'csv' });
// From a stream
import { createReadStream } from 'node:fs';
const stream = createReadStream('statement.mt940');
// Node streams can be passed via Readable.toWeb() in Node 18+
await fc.convert({ file: Readable.toWeb(stream), to: 'csv' });Error Handling
All errors extend FinConvertError. Check specific subclasses with instanceof:
import {
FinConvert,
AuthenticationError,
ValidationError,
RateLimitError,
QuotaExceededError,
InternalError,
ConnectionError,
} from '@finconvert/sdk';
try {
const result = await fc.convert({ file, to: 'csv' });
} catch (err) {
if (err instanceof RateLimitError) {
console.log(`Rate limited. Retry in ${err.retryAfter}s`);
} else if (err instanceof QuotaExceededError) {
console.log(`Monthly quota exceeded. Resets ${err.quotaReset.toISOString()}`);
} else if (err instanceof AuthenticationError) {
console.log('Invalid API key');
} else if (err instanceof ValidationError) {
console.log(`Bad request: ${err.message} (code: ${err.code})`);
} else if (err instanceof InternalError) {
console.log('Server error — try again later');
} else if (err instanceof ConnectionError) {
console.log('Network error');
}
}Automatic retries
By default, the SDK retries up to 2 times on:
- Network errors (timeout, DNS failure)
- HTTP
5xxresponses - HTTP
429 RATE_LIMITED(respectsRetry-Afterheader)
It does not retry on:
4xxclient errors (invalid request, auth, etc.)429 QUOTA_EXCEEDED(monthly quota — retrying won't help)
Backoff is exponential with jitter: min(500ms × 2^attempt, 60s) × random(0.5, 1.5).
Disable retries with maxRetries: 0.
Rate Limit Info
Every successful response includes rate limit metadata extracted from response headers:
const result = await fc.convert({ file, to: 'csv' });
console.log(result.rateLimit.limit); // e.g., 100
console.log(result.rateLimit.remaining); // e.g., 87
console.log(result.rateLimit.reset); // Date when the window resetsUse this to implement proactive throttling before hitting limits.
TypeScript Types
The SDK reexports the Universal Transaction Model types for convenience:
import type {
BankStatement,
Transaction,
Balance,
Counterparty,
TransactionCategory,
StatementMetadata,
InputFormat,
OutputFormat,
RateLimitInfo,
} from '@finconvert/sdk';Runtime Support
| Runtime | Supported |
|---------|:---------:|
| Node.js 18+ | ✅ |
| Bun | ✅ |
| Deno (via npm compat) | ✅ |
| Browsers (with fetch + FormData) | ✅ |
String file paths require Node.js or Bun (they use node:fs/promises).
License
MIT © Zero Loop Labs Ltd
