OFXParse
OFXParse (version 0.21) is a Python library designed for parsing and working with the Open Financial Exchange (OFX) file format. It extracts data such as transactions, account information, and balance statements from OFX files, making it easier to integrate financial data into applications. The library is actively maintained with a generally stable release cadence.
Common errors
-
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x__ in position __: invalid start byte
cause The OFX file contains characters not compatible with the default UTF-8 decoding, and the file was opened in text mode or processed as a string without specifying the correct encoding, or not handled as binary.fixAlways open OFX files in binary read mode (`"rb"`) and pass the file handle directly to `OfxParser.parse()`. The library is designed to handle various encodings when given a binary stream. -
lxml.etree.XMLSyntaxError: Document is empty, line 1, column 1
cause The input file is empty or `OfxParser.parse()` received an empty or invalid stream/string. This often indicates a corrupted or improperly generated OFX file.fixVerify the content of your OFX file. Ensure it contains actual OFX data and is not zero-bytes. If generating the file, ensure proper content is written. If downloading, try re-downloading the file. -
AttributeError: 'OfxAccount' object has no attribute 'transactions'
cause The specific OFX file being parsed does not contain any transaction data for the account, or the `BANKTRANLIST` section is missing or empty.fixBefore accessing `ofx.accounts[0].transactions`, check if `ofx.accounts` exists and if `ofx.accounts[0].transactions` is not empty. Example: `if ofx.accounts and ofx.accounts[0].transactions:`
Warnings
- gotcha OFX files can have varying structures, encodings, and optional tags. Directly accessing attributes like `transaction.memo` or `ofx.accounts[0].balance` without checking for their existence can lead to `AttributeError`.
- gotcha The library expects OFX files to be opened in binary read mode (`"rb"`). Opening in text mode (`"r"`) without explicit encoding, or attempting to parse a plain string directly, can lead to `UnicodeDecodeError` or `lxml.etree.XMLSyntaxError`.
- gotcha Parsing malformed or non-standard OFX files can lead to `lxml.etree.XMLSyntaxError` (e.g., 'Premature end of data', 'Document is empty') or incomplete data extraction. This is common with files generated by less compliant financial institutions.
Install
-
pip install ofxparse
Imports
- OfxParser
from ofxparse import OfxParser
Quickstart
import os
from ofxparse import OfxParser
# Create a dummy OFX file for demonstration
ofx_content_simple = """
OFXHEADER:100
DATA:OFXSGML
AOFX:V1
<OFX>
<BANKMSGSRQV1>
<STMTTRNRS>
<TRNUID>0</TRNUID>
<STATUS><CODE>0</CODE><SEVERITY>INFO</SEVERITY></STATUS>
<STMTRS>
<CURDEF>USD</CURDEF>
<BANKACCTFROM>
<BANKID>123456789</BANKID>
<ACCTID>0000000000</ACCTID>
<ACCTTYPE>CHECKING</ACCTTYPE>
</BANKACCTFROM>
<BANKTRANLIST>
<DTSTART>20230101000000</DTSTART>
<DTEND>20230131000000</DTEND>
<STMTTRN>
<TRNTYPE>DEBIT</TRNTYPE>
<DTPOSTED>20230105000000</DTPOSTED>
<TRNAMT>-50.00</TRNAMT>
<FITID>12345</FITID>
<NAME>Grocery Store</NAME>
<MEMO>Weekly groceries</MEMO>
</STMTTRN>
<STMTTRN>
<TRNTYPE>CREDIT</TRNTYPE>
<DTPOSTED>20230110000000</DTPOSTED>
<TRNAMT>1000.00</TRNAMT>
<FITID>67890</FITID>
<NAME>Payroll</NAME>
<MEMO>Monthly salary</MEMO>
</STMTTRN>
</BANKTRANLIST>
<LEDGERBAL>
<BALAMT>1500.00</BALAMT>
<DTASOF>20230131000000</DTASOF>
</LEDGERBAL>
</STMTRS>
</STMTTRNRS>
</BANKMSGSRQV1>
</OFX>
"""
file_path = "example.ofx"
with open(file_path, "w", encoding="utf-8") as f:
f.write(ofx_content_simple)
try:
# Always open OFX files in binary read mode ('rb')
with open(file_path, "rb") as f:
ofx = OfxParser.parse(f)
if ofx.accounts:
account = ofx.accounts[0]
print(f"Bank ID: {account.bank_id}")
print(f"Account ID: {account.account_id}")
print(f"Account Type: {account.account_type}")
if account.transactions:
print("\nTransactions:")
for transaction in account.transactions:
print(f" Date: {transaction.date.strftime('%Y-%m-%d')}, Amount: {transaction.amount}, Description: {getattr(transaction, 'memo', 'N/A')}")
else:
print("\nNo transactions found.")
if account.ledger_balance:
print(f"\nLedger Balance: {account.ledger_balance}")
else:
print("\nNo ledger balance found.")
else:
print("No accounts found in OFX file.")
except Exception as e:
print(f"Error parsing OFX: {e}")
finally:
# Clean up the dummy file
if os.path.exists(file_path):
os.remove(file_path)