TypeScript SDK
Parse statements with @logickoder/bankstract, the typed Node.js client.
A typed Node.js wrapper over the /v1 API. Server-side only: the API key is a secret, so never ship it to a browser.
npm install @logickoder/bankstractNode 18+ (uses the global fetch). Ships ESM and CommonJS; types are bundled.
Quickstart
import { bankstract } from '@logickoder/bankstract'
import { readFile } from 'node:fs/promises'
const client = bankstract({ apiKey: process.env.BANKSTRACT_API_KEY! })
const pdf = await readFile('statement.pdf')
const result = await client.parse(pdf)
console.log(result.metadata?.bank, result.transactions.length)parse accepts Uint8Array | ArrayBuffer | Blob. See Authentication for keys and the parse endpoint for the response shape.
Redaction
const { data, format, redactions } = await client.redact(pdf, { bank: 'gtbank' })
// `data` is the redacted document bytes (PDF or XLSX), in memory.
await writeFile(`redacted.${format}`, data)Usage and banks
const usage = await client.usage() // tier, parses this cycle, projected overage
const banks = await client.banks() // supported bank ids for the `bank` overrideOptions
bankstract({
apiKey, // required
baseUrl, // default 'https://bankstract.logickoder.dev'
fetch, // inject a fetch implementation; default globalThis.fetch
timeoutMs, // per-request timeout, default 30000
})Per-call, parse and redact take { bank?, filename?, signal? }.
Errors
Every non-2xx response throws a typed BankstractError subclass. The API key never appears in the error. The thrown error mirrors the error envelope: status, errorClass, and markerCoverage where relevant.
import { RateLimitError, UnsupportedStatementError, BankstractError } from '@logickoder/bankstract'
try {
await client.parse(pdf)
} catch (err) {
if (err instanceof RateLimitError) {
console.log('retry after', err.retryAfter, 'seconds')
} else if (err instanceof UnsupportedStatementError) {
console.log('no parser matched:', err.errorClass, err.markerCoverage)
} else if (err instanceof BankstractError) {
console.log(err.status, err.errorClass, err.message)
}
}| Throws | When |
|---|---|
AuthError | 401, missing or invalid key |
SubscriptionInactiveError | 402, subscription inactive |
PayloadTooLargeError | 413, file over 50MB |
UnsupportedStatementError | 422, no parser, layout drift, or reconciliation (see errorClass, markerCoverage) |
RateLimitError | 429, with retryAfter. The API no longer emits 429 (over-cap returns a 200 canned sample); kept as a defensive mapping. |
ServerError | 5xx |
TimeoutError | request aborted (timeout or your signal) |