OFX File Documentation
Summary
An Open Financial Exchange (OFX) file is a plain-text record of bank or credit-card transactions downloaded from online banking, meant to be imported into accounting or budgeting software. It carries dated entries with amounts, payees, and account identifiers. OFX 1.x uses an SGML syntax that opens with an OFXHEADER:100 block; OFX 2.x is well-formed XML. Its MIME type is application/x-ofx, and any text editor shows the raw data.
Technical details
| Feature | Value |
|---|---|
| Full name | Open Financial Exchange |
| File extension | .ofx |
| MIME type | application/x-ofx |
| Format type | Financial transaction data (text) |
| Syntax (1.x) | SGML with a colon-delimited header (DATA:OFXSGML) |
| Syntax (2.x) | Well-formed XML (<?OFX ...?> processing instruction) |
| Developer | Intuit, Microsoft and CheckFree (joint specification) |
| Introduced | 1997 (OFX 1.0); OFX 2.0 moved to XML in 2000 |
| Latest version | OFX 2.2 (and 2.3) |
| Open standard | Yes — published specification, royalty-free |
| Character encoding | USASCII, 1252 (Windows-1252) or UTF-8 |
| Header marker (1.x) | OFXHEADER:100 at offset 0 |
| Root element | <OFX> |
| Transaction record | <STMTTRN> (type, DTPOSTED, TRNAMT, NAME) |
| Related extensions | .qfx, .qbo, .qif, .ofc |
| Specification | ofx.net/downloads.html |
What is an OFX file?
OFX stands for Open Financial Exchange, a text format for moving financial transactions — bank statements, credit-card activity, investment positions — between a financial institution and personal-finance software. It was published in 1997 by Intuit, Microsoft, and CheckFree, who merged their three competing download formats (Intuit’s OFC, Microsoft’s OFX draft, and CheckFree’s protocol) into one specification. An .ofx file is the thing behind an online-banking button labelled “Download to Quicken”, “Export”, or “Download transactions”: it contains a set of dated transactions with amounts, payees, and the account they belong to.
OFX is a client/server protocol as well as a file format. The same message grammar that a bank server returns over HTTPS is what gets written to disk as a .ofx file, which is why the file contains a sign-on response and a statement response wrapped around the transaction list. Two syntaxes exist. OFX 1.x (1997) uses SGML, the tag-based ancestor of XML; OFX 2.0 (2000) re-cast the same element tree as strict XML. Both encode the same data model, so this article covers the header, the message-set wrappers, and the transaction records that make up either one.
The OFX 1.x SGML header
An OFX 1.x file does not start with an angle bracket. It starts with a block of colon-delimited KEY:VALUE lines, one per line, terminated by a blank line before the SGML body begins. A parser reads this header first to learn the version and character set, then switches into SGML mode.
OFXHEADER:100 header version (always 100 for OFX 1.x)
DATA:OFXSGML payload is OFX in SGML form
VERSION:102 DTD version: 102, 103, 151, 160, 200...
SECURITY:NONE application-level security: NONE or TYPE1
ENCODING:USASCII USASCII or UNICODE
CHARSET:1252 code page (1252 = Windows-1252) or NONE
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE
<-- one blank line separates header from body
<OFX>
...
The first line must read OFXHEADER:100; it identifies the header format itself, not the OFX version. DATA:OFXSGML tells the reader the body is SGML rather than XML. VERSION names the OFX document type definition (DTD) — common values are 102, 103, 151, and 160 — and controls which tags are legal in the body. SECURITY is NONE for a plain download or TYPE1 when a channel-encryption profile is in use. ENCODING combined with CHARSET fixes the character set: USASCII with code page 1252 is the common bank export. Because these are the only bytes that vary predictably at the start of the file, tools identify an OFX 1.x file by matching the literal string OFXHEADER: at offset 0.
OFX 2.x: the XML declaration and processing instruction
OFX 2.0 replaced the colon-delimited header with real XML. The nine header lines collapse into a single XML processing instruction, and the whole document must now be well-formed: every element that opens has to close.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?OFX OFXHEADER="200" VERSION="211" SECURITY="NONE"
OLDFILEUID="NONE" NEWFILEUID="NONE"?>
<OFX>
...
</OFX>
The <?OFX ...?> processing instruction carries the same fields as the old header (OFXHEADER is now 200, VERSION is a three-digit value such as 211). The important practical difference is well-formedness. In OFX 1.x SGML, leaf elements are frequently written open-ended — <TRNAMT>-24.99 with no </TRNAMT> — because SGML’s DTD lets a parser infer where the element ends. Feed that same text to an XML parser and it rejects the file. This single distinction is behind most “my OFX won’t import” problems: a strict XML importer refuses SGML-style OFX 1.x, and a converter that assumes closing tags mangles it.
Message sets: SIGNONMSGSRSV1 and BANKMSGSRSV1
Inside <OFX>, data is grouped into message sets, each a request (...RQ) or response (...RS) aggregate. A downloaded statement is a set of responses, so the wrappers you see end in RSV1 (response, version 1).
<OFX>
<SIGNONMSGSRSV1> sign-on response
<SONRS>
<STATUS><CODE>0<SEVERITY>INFO 0 = success
<DTSERVER>20260115120000 server timestamp
<FI><ORG>...<FID>... financial institution id
<BANKMSGSRSV1> bank statement response
<STMTTRNRS>
<STMTRS>
<CURDEF>USD account currency
<BANKACCTFROM> which account (see below)
<BANKTRANLIST> the transactions (see below)
<LEDGERBAL> closing balance
Every response opens with a <STATUS> aggregate whose <CODE> is 0 for success; a non-zero code plus a <SEVERITY> of ERROR tells an importer the download failed. Dates use the OFX timestamp format YYYYMMDDHHMMSS, optionally with a fractional-second part and a bracketed GMT offset such as [-5:EST]. Credit-card statements use <CREDITCARDMSGSRSV1> and <CCSTMTRS> instead of the bank wrappers, and investment accounts use <INVSTMTMSGSRSV1>, but the shape is the same: a status, an account identifier, a transaction list, and a balance.
BANKACCTFROM: identifying the account
The <BANKACCTFROM> aggregate names the account the transactions belong to. It is what lets an importer match a download to the right register instead of asking every time.
<BANKACCTFROM>
<BANKID>121000248 routing / bank number (ABA in the US)
<ACCTID>0123456789 account number
<ACCTTYPE>CHECKING CHECKING | SAVINGS | MONEYMRKT | CREDITLINE
</BANKACCTFROM>
For a credit card the aggregate is <CCACCTFROM> with just an <ACCTID>. Because BANKID and ACCTID together are effectively your full account coordinates, an OFX file is privacy-sensitive even though it is harmless plain text: it exposes real account numbers and a complete transaction history. That is the reason to avoid uploading raw OFX to unknown online converters.
STMTTRN: the transaction record, field by field
The transactions live inside <BANKTRANLIST>, which opens with the date range it covers (<DTSTART>, <DTEND>) and then holds one <STMTTRN> aggregate per transaction.
<BANKTRANLIST>
<DTSTART>20260101
<DTEND>20260131
<STMTTRN>
<TRNTYPE>DEBIT DEBIT|CREDIT|CHECK|FEE|INT|XFER...
<DTPOSTED>20260115 date posted (YYYYMMDD)
<TRNAMT>-24.99 signed amount, '.' decimal
<FITID>20260115-4471 unique id assigned by the bank
<NAME>COFFEE ROASTERS payee (<=32 chars in 1.x)
<MEMO>Card purchase free-text note (optional)
</STMTTRN>
</BANKTRANLIST>
Two fields do the heavy lifting. <TRNAMT> is a signed decimal: negative for money leaving the account, positive for money arriving, with a period as the decimal separator regardless of locale. <FITID> is the Financial Institution Transaction ID, a value the bank guarantees to be unique and stable for that account; importers use it as the deduplication key, so re-downloading an overlapping date range does not create duplicate entries. <TRNTYPE> is an enumerated code (DEBIT, CREDIT, CHECK, FEE, INT, DIV, XFER, and others) that lets software categorise a row without parsing the memo. The list closes with <LEDGERBAL>, holding <BALAMT> and <DTASOF>, the account balance as of a given date.
OFX, QFX and QBO: the same grammar, different tags
Several extensions share the OFX grammar. A .qfx file is OFX with extra Intuit-specific tags (an <INTU.BID> bank identifier and a user id) that tie it to Quicken; banks sometimes charge to enable QFX because it is Quicken’s branded “Web Connect” channel. A .qbo file is the QuickBooks flavour, again OFX plus a QuickBooks routing tag. Because the underlying tree is identical, converting between them is mostly adding or stripping a handful of institution tags rather than a real format transform. The older .qif (Quicken Interchange Format) is a genuinely different, line-oriented format that predates OFX and carries no account or balance structure, which is why moving OFX to QIF loses information. See also QIF and the generic XML family that OFX 2.x belongs to.
Frequently asked questions
Why does a strict app reject my bank’s OFX file?
Most likely the file is SGML OFX 1.x with unclosed leaf tags and a header your importer parses too rigidly, or the bank wrote a slightly non-standard field (a bad date format, a missing <STATUS> code, an ACCTTYPE the app doesn’t expect). GnuCash’s importer is more forgiving than most; a dedicated OFX cleaner can also fix the header and re-close tags before import.
What is FITID and why does it matter?
The <FITID> is a per-account unique transaction id assigned by the bank. Personal-finance software uses it to recognise transactions it has already imported, so overlapping downloads do not create duplicates. If a bank reuses or changes FITIDs between downloads, you get either missed or duplicated entries — a common cause of reconciliation drift.
References
- Open Financial Exchange — specification downloads
- GnuCash Manual — importing OFX/QFX transactions
- Financial Data Exchange — steward of the OFX specification
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.