Reading GAEB files in Python with pyGAEB
A practical guide to parsing GAEB DA XML files in Python with the open-source pyGAEB library — from a one-line parse to iterating items, validation, and writing a bid back.
GAEB DA XML is the standard for exchanging construction bills of quantities in Germany — and parsing it by hand is miserable. Multiple schema versions (2.0 through 3.3), a dozen exchange phases, and XML that is happy to hide an XXE payload or a billion-laughs bomb. pyGAEB is an open-source (MIT) Python library that turns all of that into one clean, typed model. Here is how to actually use it.
Install
# Core parser + writer + export, zero LLM dependencies pip install pyGAEB # Optional: LLM-powered item classification (100+ providers via LiteLLM) pip install pyGAEB[llm]
Parse any file with one call
You do not need to know the version or phase up front — pyGAEB auto-detects both:
from pygaeb import GAEBParser
doc = GAEBParser.parse("tender.X83") # DA XML 3.x
doc = GAEBParser.parse("old.D83") # DA XML 2.x — same call
print(doc.source_version) # SourceVersion.DA_XML_33
print(doc.exchange_phase) # ExchangePhase.X83
print(doc.grand_total) # Decimal("1234567.89")
Note the Decimal — money and quantities are never floats. That matters the moment you sum a few thousand positions.
Iterate the items
iter_items() works across every document kind — procurement, trade, cost, and quantity determination — so you can write generic code:
for item in doc.iter_items():
print(item.oz) # "01.02.0030" — ordinal number
print(item.short_text) # "Mauerwerk der Innenwand…"
print(item.qty) # Decimal("1170.000")
print(item.unit) # "m2"
print(item.unit_price) # Decimal("45.50")
print(item.total_price) # Decimal("53235.00")
Validate what you parsed
By default pyGAEB is lenient — it keeps parsing and collects issues so you can decide what to do with them. You can also fail fast, or register your own rules:
from pygaeb import GAEBParser, ValidationMode
# Lenient (default): collect warnings, keep going
doc = GAEBParser.parse("tender.X83")
for issue in doc.validation_results:
print(issue.severity, issue.message)
# Strict: raise on the first ERROR
doc = GAEBParser.parse("tender.X83", validation=ValidationMode.STRICT)
Price it and write the bid back
The most common real-world task: you receive a D83/X83 request, fill in unit prices, and return a D84/X84 bid with the identical structure. That round-trip is a first-class operation:
from pygaeb import GAEBParser, GAEBWriter, ExchangePhase
from decimal import Decimal
doc = GAEBParser.parse("tender.X83")
item = doc.award.boq.get_item("01.02.0030")
item.unit_price = Decimal("48.00")
GAEBWriter.write(doc, "bid.X84", phase=ExchangePhase.X84)
Because the writer starts from the original document, the returned bid keeps the exact positions and identifiers the client's software expects — no structural drift.
Convert between versions
Received a 2.x file but your downstream tooling only speaks 3.3? Or need to downgrade for a partner on an older system?
from pygaeb import GAEBConverter, SourceVersion
report = GAEBConverter.convert("old.D83", "modern.X83")
report = GAEBConverter.convert(
"tender.X83", "compat.X83",
target_version=SourceVersion.DA_XML_32,
)
print(report.items_converted, report.has_data_loss)
The has_data_loss flag is honest about what a downgrade cost you — useful to log rather than discover later.
Export for humans
For quick analysis or handing data to non-developers:
from pygaeb.convert import to_json, to_csv to_json(doc, "boq.json") # full nested BoQ tree to_csv(doc, "items.csv") # flat item table with classification columns
Where this fits
pyGAEB is the parsing and writing foundation — the layer that means software (and estimators) stop caring which format a tender arrived in. It is what the DatumOS platform is built on: the same engine, plus classification, pricing, and a review workflow on top.
It is MIT-licensed and on GitHub — issues, PRs, and edge-case sample files are all welcome. If you are wrestling with GAEB in Python, start with GAEBParser.parse() and go from there.
Not writing code? pyGAEB now also ships a Model Context Protocol server, so you can ask an AI assistant about a tender file in plain language — no Python required.
Was this article helpful?
No cookies, no tracking — one vote per reader per day.
Comments
Comments are reviewed before they appear.
Loading comments…