bankstatementparser-loader-bai2
A BAI2 (Bank Administration Institute, version 2) cash-management loader that parses BAI2 files into bankstatementparser Transaction objects.
Contents
- What is bankstatementparser-loader-bai2? — the problem it solves
- Install — PyPI, virtualenv
- Quick start — parse a file in three lines
- Public API —
load_bai2,load_bai2_file,summarize_bai2 - Supported BAI2 subset — exactly which records are handled
- Amount and sign convention — how cents and debit/credit map
- When not to use this loader — honest boundaries
- Development — gates, make targets
- Security — input-handling posture
- Contributing — how to get changes in
- License — Apache-2.0
What is bankstatementparser-loader-bai2?
BAI2 (Bank Administration Institute, version 2) is the de-facto US
cash-management file format that banks ship for intraday and prior-day
balance and transaction reporting. The published
bankstatementparser
library parses PDF and other statement formats but does not support
BAI2.
bankstatementparser-loader-bai2 is a small, dependency-light companion
that fills that gap: give it a BAI2 payload and it returns a flat list of
bankstatementparser.transaction_models.Transaction
objects (source="bai2") that the rest of your deterministic pipeline
can consume unchanged.
| Concern | How this loader handles it |
|---|---|
| Record model | A documented, pragmatic subset of BAI2 (01/02/03/16/88 plus ignored trailers) |
| Amounts | BAI2 minor-unit integers (cents) converted to Decimal (never float) |
| Debit / credit | Derived from the 16 type-code range, with the raw code preserved |
| Multiple accounts | All 16 records across every group / account are flattened into one list |
| Real-world text | The 16 free-text field (and 88 continuations) is kept verbatim — commas and slashes inside it are preserved, not split on |
| Robustness | Tolerates CRLF, blank lines, trailing spaces, an optional trailing / per record, and V/S funds-type subfields |
| Errors | A clear ValueError if the file does not start with an 01 File Header |
Install
| Channel | Command | Notes |
|---|---|---|
| PyPI | pip install bankstatementparser-loader-bai2 |
Pulls in bankstatementparser >= 0.0.11 |
| Source | git clone https://github.com/sebastienrousseau/bankstatementparser-loader-bai2 && cd bankstatementparser-loader-bai2 && poetry install |
For development |
Requires Python 3.10 or later. Works on macOS, Linux, and Windows.
Using an isolated virtual environment (recommended)
python -m venv venv
source venv/bin/activate # macOS/Linux
venv\Scripts\activate # Windows
python -m pip install -U bankstatementparser-loader-bai2
Quick start
from bankstatementparser_loader_bai2 import load_bai2_file
transactions = load_bai2_file("statement.bai")
for txn in transactions:
print(txn.account_id, txn.currency, txn.amount, txn.description)
Or parse an in-memory payload:
from bankstatementparser_loader_bai2 import load_bai2
payload = (
"01,SENDER,RECEIVER,260601,1200,FILE001,,,/\n"
"02,RCVR,ORIG,1,260601,1200,USD,/\n"
"03,0123456789,USD,010,150000,1,,/\n"
"16,165,150000,Z,BANKREF1,CUSTREF1,Incoming wire payment/\n"
"88,from ACME Corp invoice 42/\n"
"16,475,2500,Z,BANKREF2,,ATM withdrawal/\n"
"49,152500,2/\n"
"98,152500,1,4/\n"
"99,152500,1,6/\n"
)
for txn in load_bai2(payload):
print(txn.amount, txn.category, txn.description)
# 1500 bai2:165 Incoming wire payment from ACME Corp invoice 42
# -25 bai2:475 ATM withdrawal
Runnable versions live in examples/.
Public API
from bankstatementparser_loader_bai2 import (
Bai2StatementParser,
load_bai2,
load_bai2_file,
summarize_bai2,
Bai2Summary,
)
| Class / Function | Signature | Returns |
|---|---|---|
Bai2StatementParser |
Bai2StatementParser(path) |
BankStatementParser parser instance |
load_bai2 |
load_bai2(text: str) |
list[Transaction] |
load_bai2_file |
load_bai2_file(path) |
list[Transaction] |
summarize_bai2 |
summarize_bai2(text: str) |
Bai2Summary |
Bai2Summary is a dataclass with the fields file_id, group_count,
account_count, transaction_count, and currency.
Each produced Transaction is populated as follows:
Transaction field |
Source |
|---|---|
account_id |
03 Account Identifier — accountNumber |
currency |
03 currencyCode, falling back to the 02 group currency |
amount |
16 amount (cents / 100), signed per the convention below |
booking_date |
02 Group Header as-of date, when present |
description |
16 text plus any 88 continuations |
transaction_id |
16 bankRefNum, falling back to customerRefNum |
reference / category |
The raw 16 type code (category as bai2:<code>) |
source |
Always "bai2" |
Supported BAI2 subset
BAI2 records are comma-delimited fields ending with a / delimiter,
each beginning with a numeric type code. This loader implements a
documented, pragmatic subset:
| Record | Meaning | Handling |
|---|---|---|
01 |
File Header | Required first record. fileId captured for the summary. |
02 |
Group Header | Group currency and as-of date captured. |
03 |
Account Identifier | accountNumber + optional currencyCode captured; account currency overrides group currency. |
16 |
Transaction Detail | One transaction. The free-text field runs to end-of-record (commas included) and is kept verbatim; V/S funds-type subfields are accounted for when locating the references and text. |
88 |
Continuation | Appended verbatim (commas included) to the preceding 16 record's text. A 88 continuing an 03 summary, or one with no preceding 16, is dropped rather than mis-attached. The rare 88: colon form is tolerated. |
49 / 98 / 99 |
Account / Group / File trailers | Ignored — control totals are not validated. |
Any other (or unknown) leading type code is ignored so that vendor extensions do not abort the parse. Ignoring control-total trailers is a deliberate, documented choice: the goal is faithful transaction extraction, not file-level reconciliation.
Amount and sign convention
BAI2 amounts are unsigned integers in the account currency's minor
units (cents), with no decimal point. They are converted to
decimal.Decimal by dividing by 100. An empty amount field is treated
as 0.
Debit / credit direction is derived from the documented numeric ranges
of the 16 record's type code (this is the loader's chosen, documented
convention):
| Type-code range | Meaning | Behaviour |
|---|---|---|
100–399 |
Credit | amount kept positive |
400–699 |
Debit | amount made negative |
700–799 |
Loan detail | treated as a debit-side disbursement: amount made negative |
900–999 |
Custom / summary / status | no Transaction emitted — these non-detail status/summary codes are skipped (and any continuation attached to one is dropped with it) |
| anything else | Unknown (incl. non-numeric) | amount kept positive |
The raw BAI2 type code is always preserved on every emitted
Transaction in both category (as bai2:<code>) and reference, so
no information is lost.
A small, optional lookup of well-known type codes (for example 142
"ACH credit", 301 "Commercial deposit", 475 "Check paid", 501
"Wire transfer debit") enriches the description of a 16 record that
carries no free-text of its own; a record that already has text keeps
its own text unchanged.
When not to use this loader
- You have ISO 20022 camt.053 or SWIFT MT940, not BAI2. Those are different formats with their own dedicated loaders.
- You need control-total reconciliation. This loader extracts
transactions and deliberately ignores the
49/98/99trailers; if you must validate file sums, do so before or after loading. - You need the full BAI2 specification. This is a documented subset focused on transaction extraction, not an exhaustive BAI2 parser.
Development
This project uses Poetry and mise.
git clone https://github.com/sebastienrousseau/bankstatementparser-loader-bai2.git
cd bankstatementparser-loader-bai2
poetry env use python3.12
poetry install
A Makefile orchestrates the quality gates (kept in lockstep with CI):
| Target | What it runs |
|---|---|
make check |
All gates (REQUIRED before commit) |
make test |
pytest --cov=bankstatementparser_loader_bai2 --cov-branch --cov-fail-under=100 |
make lint |
ruff check + black --check |
make type-check |
mypy --strict |
make doc-coverage |
interrogate --fail-under=100 (docstring coverage) |
make mutation |
mutmut run + mutmut results (mutation testing) |
Current state (v0.0.19): all tests passing, 100% line + branch
coverage against a 100% enforced floor, mypy --strict clean,
interrogate 100%, and a mutation-tested loader (317/336 mutants killed;
the 19 survivors are documented equivalent mutants — see
tests/MUTATION.md).
Security
- Read-only. The loader only reads text / files you pass it; it writes nothing.
- No XML, no network, no code execution. Parsing is a pure string-to-dataclass transformation.
- Decimal arithmetic is used throughout, avoiding
floatrounding surprises in financial amounts. - Dependencies are pinned via
poetry.lockand audited in CI.
To report a vulnerability, please use GitHub private vulnerability reporting rather than a public issue.
Contributing
Contributions are welcome — see the
contributing instructions.
Thanks to all the
contributors
who have helped build bankstatementparser-loader-bai2.
Ecosystem
bankstatementparser is part of a modular financial ecosystem. Optional companion packages provide specialized loaders, writers, AI agents, language servers, and transport protocol adapters:
| Package | GitHub Repository | PyPI | Role | Description |
|---|---|---|---|---|
bankstatementparser |
sebastienrousseau/bankstatementparser |
Core Engine | Unified parser for CAMT (052/053), PAIN.001, CSV, OFX, QFX, MT940, and PDF statements | |
bankstatementparser-mcp |
sebastienrousseau/bankstatementparser-mcp |
AI Protocol | Model Context Protocol (MCP) server exposing statement tools to LLMs & AI agents | |
bankstatementparser-lsp |
sebastienrousseau/bankstatementparser-lsp |
Developer Tooling | Language Server Protocol (LSP) with live SWIFT MT940 statement validation & diagnostics | |
bankstatementparser-transport-ebics |
sebastienrousseau/bankstatementparser-transport-ebics |
Transport | Automated bank statement retrieval over EBICS 3.0 (H005) and 2.5 (H004) protocols |
|
bankstatementparser-writer-xlsx |
sebastienrousseau/bankstatementparser-writer-xlsx |
Output Writer | Formats and exports parsed banking transactions into styled Microsoft Excel (.xlsx) workbooks |
|
bankstatementparser-writer-qif |
sebastienrousseau/bankstatementparser-writer-qif |
Output Writer | Serializes transactions into standard Quicken Interchange Format (.qif) exchange files |
|
bankstatementparser-writer-ofx |
sebastienrousseau/bankstatementparser-writer-ofx |
Output Writer | Serializes transactions into standard Open Financial Exchange (.ofx) XML/SGML files |
|
bankstatementparser-writer-swift |
sebastienrousseau/bankstatementparser-writer-swift |
Output Writer | Exports transactions to SWIFT MT940 customer statements and MT942 interim reports | |
bankstatementparser-loader-bai2 |
sebastienrousseau/bankstatementparser-loader-bai2 |
Input Loader | Parses BAI2 cash-management and account balance statements | |
bankstatementparser-loader-mt942 |
sebastienrousseau/bankstatementparser-loader-mt942 |
Input Loader | Parses SWIFT MT942 interim transaction reports with credit/debit summary reconciliation | |
bankstatementparser-loader-cfonb |
sebastienrousseau/bankstatementparser-loader-cfonb |
Input Loader | Parses French CFONB 120 / AFB120 120-byte fixed-width banking statement files | |
bankstatementparser-loader-camt054 |
sebastienrousseau/bankstatementparser-loader-camt054 |
Input Loader | Ingests ISO 20022 CAMT.054 real-time debit/credit notification stream XML | |
bankstatementparser-loader-sepa |
sebastienrousseau/bankstatementparser-loader-sepa |
Input Loader | Ingests ISO 20022 SEPA PAIN.002 payment status reports and PAIN.008 direct debit mandates | |
bankstatementparser-loader-bacs |
sebastienrousseau/bankstatementparser-loader-bacs |
Input Loader | Parses UK BACS Standard 18 / Faster Payments 106-byte fixed-width transmission files |
License
Licensed under the Apache License, Version 2.0. Any contribution submitted for inclusion shall be licensed as above, without additional terms.
Metadata
Release files for bankstatementparser-loader-bai2 0.0.19
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bankstatementparser_loader_bai2-0.0.19.tar.gz | 25.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bankstatementparser_loader_bai2-0.0.19-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 48.0 kB
Release files / bankstatementparser_loader_bai2-0.0.19.tar.gz
| Download URL | bankstatementparser_loader_bai2-0.0.19.tar.gz |
|---|---|
| Size | 25.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fb8a205823d6809d55df6e9300b236e7f2e59db4c711d35b6bab30c666cc602d
|
|
BLAKE2b-256 checksum How to use checksums |
6c6fa201328d0942430c50fa484cb240d7d8216cd0b1aef5b4c68459546f31e7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency logRelease files / bankstatementparser_loader_bai2-0.0.19-py3-none-any.whl
| Download URL | bankstatementparser_loader_bai2-0.0.19-py3-none-any.whl |
|---|---|
| Size | 22.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fc2d9fd7ccf429b14d9f52db58a87cc33d1faa8fe9956474f9c6f1737c64443c
|
|
BLAKE2b-256 checksum How to use checksums |
864e501ee74b3f8a29260b813b68d6ecf4af80e3fd7f7579280d36dc277b5751
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency log