Skip to main content

📒 pyxaf

CI PyPI Python License: MIT

Read and validate every version of the Dutch Auditfile Financieel in plain Python.

The Auditfile Financieel is the standard export of a general ledger that Dutch accounting software produces for tax inspectors and auditors. pyxaf opens all seven versions of it, from the fixed-width files of 1999 to XAF 4.0, gives you one clean, typed model to work with, and tells you exactly what is wrong with a file instead of choking on it. No runtime dependencies, no upload to anyone's server, no gigabytes of RAM.

import pyxaf

with pyxaf.open("2024.xaf") as af:
    print(af.version, af.company.name)  # 4.0 Voorbeeld & Zonen B.V.

🧭 Table of contents

✨ Why pyxaf

Real auditfiles are messy. Official test files break the official rules, vendors invent their own namespaces, and an "ISO-8859-1" file turns out to be Windows-1252. pyxaf was built for those files.

  • All versions, one model. XAF 4.0, 3.2.1, 3.2, 3.1, 3.0, CLAIR2 and the fixed-width ADF all map onto the same classes: Header, LedgerAccount, Transaction, Line and friends. The raw layer keeps every element exactly as written, vendor extensions included.
  • Lenient reading, honest reporting. pyxaf never refuses a file because of bad data. Every problem becomes a graded finding with a stable code, such as XAF5010 ERROR, so nothing is silently guessed or dropped.
  • A validator on your own machine. The official validation service is for subscribers and stops at 5 MB. pyxaf checks files of any size: structure, references, control totals, balance, uniqueness, data quality and RGS codes, including the official XAF 4.0 rules [0001]–[0010].
  • Exact money. Amounts are decimal.Decimal, never float, with the original text kept next to every parsed value.
  • Streaming. Master data is read when the file opens; transactions and lines are streamed, so memory stays flat even for files of several gigabytes.
  • Lightweight. The core uses only the standard library and is fully typed. pandas, polars, Arrow, lxml and the command line are optional extras that are only imported when you use them.
  • Safe with untrusted files. DOCTYPE and ENTITY declarations are refused (no "billion laughs", no XXE), nesting, text and decompression sizes are capped, and there is no "recover" mode that could quietly lose data.

📦 Installation

pyxaf needs Python 3.11 or newer.

pip install pyxaf

The core has no dependencies. Add an extra for each optional feature you need:

Extra Adds Install
cli the pyxaf command line pip install "pyxaf[cli]"
xsd validation against the official XSDs (lxml) pip install "pyxaf[xsd]"
polars to_polars(), without pyarrow pip install "pyxaf[polars]"
pandas to_pandas() with Arrow-backed types pip install "pyxaf[pandas]"
arrow Arrow streams for DuckDB, pyarrow and friends (nanoarrow) pip install "pyxaf[arrow]"
parquet Parquet export pip install "pyxaf[parquet]"
all everything above pip install "pyxaf[all]"

Using uv? uv add pyxaf works the same way, for example uv add "pyxaf[cli,polars]".

🚀 Quickstart

Download the two sample files into an empty folder. They hold a small, made-up ledger: 2024.xaf is valid, and 2024-broken.xaf is the same file with one mistyped amount.

curl -L -O https://raw.githubusercontent.com/SpireflyHQ/pyxaf/main/examples/2024.xaf \
        -O https://raw.githubusercontent.com/SpireflyHQ/pyxaf/main/examples/2024-broken.xaf

Your own exports work exactly the same way: any version, and .gz or .zip files too.

1. Open the file

import pyxaf

af = pyxaf.open("2024.xaf")
print(af.version, af.company.name, af.header.fiscal_year)
print(len(af.accounts), "ledger accounts")
4.0 Voorbeeld & Zonen B.V. 2024
11 ledger accounts

pyxaf detected the version and encoding, and read the master data: company, ledger accounts, customers and suppliers, VAT codes and periods.

2. Walk through the transactions

Transactions and their lines are streamed straight from the file, one at a time:

for tx in af.transactions():
    print(tx.journal_id, tx.number, tx.date, tx.description, tx.balanced)
    for line in tx.lines:
        print("   ", line.account_id, line.side, line.amount)
MEM 1 2024-01-05 Boeking 1 True
    1300 D 3612.16
    8000 C 2985.26
    1800 C 626.90
...

The opening balance gets the same treatment, whichever way the file stores it:

print(af.opening_balance().by_account())  # signed: debit positive, credit negative
af.close()
{'1100': Decimal('10000.00'), '0100': Decimal('2500.50'), '0500': Decimal('-12500.50')}

3. Validate it

for name in ["2024.xaf", "2024-broken.xaf"]:
    report = pyxaf.validate(name)
    print(name, report.ok)
    for finding in report.errors:
        print("   ", finding.code, finding.line, finding.message)

The valid file passes. In the broken one someone typed 3621.16 instead of 3612.16, and pyxaf spots both consequences:

2024.xaf True
2024-broken.xaf False
    XAF5007 None transactions totalDebit 38260.42 ≠ sum of debit lines 38269.42
    XAF5010 223 transaction '1' in journal 'MEM' does not balance: debit 3621.16, credit 3612.16

Every finding has a stable code, a severity (ERROR, WARNING or INFO) and, where possible, a line number. The finding-code reference explains each one.

4. Or use the command line

With pyxaf[cli] installed:

$ pyxaf validate 2024.xaf
== 2024.xaf
4.0 — 0 error(s), 0 warning(s), 0 info

$ pyxaf info 2024.xaf
version          4.0
company          Voorbeeld & Zonen B.V.
fiscal year      2024
transactions     12
lines            36
total debit      38260.42
total credit     38260.42
...

pyxaf validate exits with 0 when the file is fine, 1 for warnings (with --strict), 2 for errors and 3 when pyxaf itself could not do its job, so it slots straight into scripts and CI.

🧰 More examples

Export to tables

pyxaf turns a file into twelve normalized tables, such as accounts, transactions and lines, with the same columns for every version:

with pyxaf.open("2024.xaf") as af:
    af.export("out/", format="csv")  # standard library only; also "jsonl"
    af.export("out/", format="parquet")  # pyxaf[parquet]
    frames = af.to_polars()  # pyxaf[polars]: a dict of DataFrames
    frames["lines"]  # amounts as Decimal(20, 2), not float

Query with DuckDB or pyarrow

Tables speak the Arrow PyCapsule interface, so Arrow-aware tools read them directly (needs pyxaf[arrow]):

import duckdb

with pyxaf.open("2024.xaf") as af:
    lines = af.tables["lines"]
    duckdb.sql("select account_id, sum(signed_amount) from lines group by account_id").show()

Check RGS codes

pyxaf reads each account's RGS (Referentie Grootboekschema) code. Load the official RGS Excel release you downloaded, and the validator checks every code against it:

import pyxaf.rgs

schema = pyxaf.rgs.load_excel("RGS 3.8-def.xlsx")
report = pyxaf.validate("2024.xaf", rgs=schema)

Split files and problem files

# a split auditfile ("Vervolgbestand 2 van 3") or one file per period, read as one
with pyxaf.open(["big.xaf", "big-2.xaf", "big-3.xaf"]) as af:
    ...

# a file that lies about its encoding, with stray control characters and bare "&"
af = pyxaf.open("old.xaf", encoding="cp1252", repair={"control-chars", "bare-ampersand"})

Repairs are opt-in and every repair shows up as a finding, so you always know what was changed.

Go deeper: raw records, detection details and the official XSDs
with pyxaf.open("2024.xaf") as af:
    af.format  # FormatInfo: version, namespace status, encoding, reasons
    af.accounts["1000"].rgs  # RgsRef(code='BLimKas', source='RGScode', ...)
    for journal_id, record in af.raw.transactions():
        ...  # lossless raw records, about twice as fast

report = pyxaf.validate("2024.xaf", xsd=True)  # also check the official XSD (pyxaf[xsd])

📜 Supported versions

Version Year How pyxaf recognises it
XAF 4.0 (4.0.3) 2025 namespace (including known variants) and 4.0-only elements
XAF 3.2.1 2024 its own ODB namespace
XAF 3.2 2014, 2017 http://www.auditfiles.nl/XAF/3.2
XAF 3.1 and 3.0 ~2010, ~2008 namespace and vocabulary (no public XSD exists)
CLAIR2 2003 header/auditfileVersion is CLAIR2.00.00, no namespace
ADF 1999 fixed-width ASCII starting with CLAIR1.00.00

From 1 January 2027 the Belastingdienst only accepts XAF 4.0. Older files stay around for years because of the seven-year retention period, which is why pyxaf reads them all.

📚 Documentation

The full documentation lives at https://spireflyhq.github.io/pyxaf/:

Release notes are in the changelog. For questions, see SUPPORT.md.

🤝 Contributing

Contributions are very welcome, especially reports of files from software that pyxaf does not handle well yet. Start with CONTRIBUTING.md for the development setup, and ARCHITECTURE.md for a map of the code. Issues labelled good first issue are a good place to begin.

Never attach a real auditfile to an issue or pull request. Auditfiles contain personal data and confidential financial records. The version, the exporting software, the finding codes and a small hand-made snippet are all we need.

Security problems are reported privately, as described in SECURITY.md.

📄 License and attribution

pyxaf is released under the MIT license.

The bundled XML schemas are published by the Belastingdienst (XAF 3.2.1 and 4.0), the former auditfiles.nl platform (XAF 3.2) and SRA (CLAIR2). The XAF 3.0 and 3.1 element lists are derived from the AnalyticsLibrary "XAF Mapping en Namen" table (Apache-2.0). RGS data is not bundled; load the official Excel release yourself with pyxaf.rgs.load_excel.

pyxaf is an independent open-source project. It is not affiliated with or endorsed by the Belastingdienst, the Taakgroep RGS or any software vendor, and a clean pyxaf report does not guarantee that the Belastingdienst will accept a file.

Metadata

Release files for pyxaf 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyxaf 0.1.1
File Size Uploaded
pyxaf-0.1.1.tar.gz 151.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyxaf 0.1.1
File Interpreter ABI Platform
pyxaf-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 284.3 kB

Release files / pyxaf-0.1.1.tar.gz

Download URL pyxaf-0.1.1.tar.gz
Size 151.4 kB
Tags Source
SHA-256 checksum
How to use checksums
80b20024a9b800506c9cc06e137233fefed5d5058871860d36d8f2244993f682
BLAKE2b-256 checksum
How to use checksums
62286059dfe357000d3e538ec032fb5a7f913416b3909453f98312c63cbdf06e
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 Oct 1, 2026.

Transparency log

Release files / pyxaf-0.1.1-py3-none-any.whl

Download URL pyxaf-0.1.1-py3-none-any.whl
Size 132.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f37eee9411d5f8da8c4db69c8198ef5c6c6ea2d65d707a83c717cb97ecf8d660
BLAKE2b-256 checksum
How to use checksums
2289fb11d4594fc7b0c11a0e3548a8102b059b71fe69b4d1ba225845a05d9b84
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 Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

0.1.2

2 release files

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page