bankstract

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/bankstract

Node 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)

Inspect

const report = await client.inspect(pdf, { bank: 'fbn' })
// report.clean, report.signals: evidence about edits, never a verdict.
// signal.detail quotes the statement: never log it. Log code, severity, page and row.

See Inspect for the codes and what they miss. The anonymous demo key works too, from a server that forwards a Turnstile token.

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` override

Options

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, redact and inspect 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)
  }
}
ThrowsWhen
AuthError401, missing or invalid key
SubscriptionInactiveError402, subscription inactive (also free_limit_reached on inspect: check errorClass)
ForbiddenError403, the key or token is not allowed on this route
PayloadTooLargeError413, file over 50MB
UnsupportedStatementError422, no parser, layout drift, reconciliation, or a processing limit (see errorClass, markerCoverage)
RateLimitError429, with retryAfter. The API no longer emits 429 (over-cap returns a 200 canned sample); kept as a defensive mapping.
ServerError5xx
TimeoutErrorrequest aborted (timeout or your signal)

On this page