Lints German property-ad HTML for the energy-certificate fields GEG Section 87 demands, and flags an efficiency class that contradicts the kWh value. Runs entirely in your browser — nothing is uploaded.
Full version: scans every listing template in the workspace or the CI feed in one run and writes a dated audit record you keep as proof of what each ad said on the day it went live.
$29 once · one licence key per person or team seat · A Bedarfsausweis with an on-site visit runs about €300–€500 in 2026, and the Energieberater never opens your template.
Same engine as the VS Code extension, byte for byte.
Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.
VS Code MarketplaceOpen VSX (Cursor, VSCodium)npmDocker Hubext install ReadyStack.geg-87-listing-lint npx @readystack/geg-87-listing-lint <file> docker run --rm -v "$PWD:/w" getreadystack/geg-87-listing-lint /w
Team? One key for every ReadyStack linter, 5 seats, $149 once
Scans every listing template in the workspace or the CI feed in one run and writes a dated audit record you keep as proof of what each ad said on the day it went live.
One payment, one licence key for this tool. The key is shown right after payment.
A Bedarfsausweis drawn up by an Energieberater with an on-site visit costs about 300 to 500 euro in 2026, while a missing or wrong mandatory field in the ad is an Ordnungswidrigkeit fined up to 10,000 euro per ad under Section 108
Buy the full version — $29· ReadyStack
Real numbers from this tool, line by line.

Seven GEG breaches sat in a single Leipzig estate agency's listing template, and the web developer who maintained it had proof-read the page twice. The template rendered fine. It was the law underneath it that it broke, in seven separate places, on every one of the flats that partial served.
Section 87 of the German Gebäudemodernisierungsgesetz — the statute nearly every portal still calls GEG — is short. If an Energieausweis exists when a commercial ad is placed, the ad must carry five things: the type of certificate (Energiebedarfsausweis or Energieverbrauchsausweis), the Endenergiebedarf or Endenergieverbrauch value, the wesentliche Energieträger used for heating, and, for a residential building, the Baujahr and the Energieeffizienzklasse named in the certificate. For a Nichtwohngebäude, Section 87 (2) wants the figure split into Wärme and Strom. Get one of them wrong and the published ad is an Ordnungswidrigkeit under Section 108 (1) no. 16, fined up to 10,000 euro.
Here is what it shipped.
The Energiedaten block opened with "Energieausweis vom 12.03.2015 liegt vor". Two problems at once: it never names which kind of certificate it is, mandatory field number one, and a certificate is valid ten years from its issue date under Section 79 (4), so that one expired on 12.03.2025.
The next line read "Endenergiebedarf: 182 kWh". The number is there, the unit is not — the certificate states kWh per square metre and year.
Then came the line no proof-reader catches: "Energieeffizienzklasse: C". Anlage 10 fixes the bands: C stops at 100 kWh/(m²·a) and F runs from 160 to 200. 182 is class F. The ad states two contradictory facts about one building, because the class and the consumption figure come from different database columns that drifted apart.
Between them sat "Wesentlicher Energieträger: {{ traeger }}" — a template token that never rendered. That is two findings: the mandatory heating energy source is absent, and an unrendered placeholder sits in a mandatory field.
And the Baujahr, mandatory for a residential building, was nowhere on the page.
Seven findings in one partial. The clean version of the same expose returns zero.
This is the class of defect that survives review: every line looks plausible on its own. The contradiction only appears if you hold the kWh figure and the class letter together and remember which band table applies. A chatbot will not do that reliably — and it never sees the file anyway. The defect does not live in a pasted sentence; it lives in the Twig, Blade or Liquid partial a CMS renders once per listing, several hundred times a night.
So the linter reads the file. Thirteen rules, each carrying the clause it enforces: the five mandatory fields, the Wärme/Strom split for non-residential ads, the unit, an implausible figure, an invalid class letter, the band check, the ten-year expiry and unrendered tokens. Every finding names its line.
The free version lints the ad you have open, in the editor or in the browser, with no key and nothing withheld. What it cannot answer is the question that arrives months later, when a notice lands about an ad that ran in March: what did that listing say on the day it published? That needs one run across every template in the workspace or the CI feed, and a dated record the agency keeps.
For scale: a Bedarfsausweis with an on-site visit runs about 300 to 500 euro in 2026, and the Energieberater who writes it never opens your template.
It reads a German property-ad template and checks the five fields Section 87 GModG makes mandatory: certificate type, Endenergie value, heating energy source, Baujahr and Energieeffizienzklasse. It then compares the class letter against the kWh value using the Anlage 10 bands, flags a certificate older than ten years, a missing unit, and template tokens left unrendered.
Web developers and agencies who ship listing templates for German estate agents, for Makler CMS installations and for portal feeds. If you maintain the Twig, Blade or Liquid partial that renders the Energiedaten block of an expose, this linter runs over that file and names the line that breaks Section 87.
Neither one opens your template. A chatbot sees the sentence you paste, not the partial your CMS renders per listing, and it does not hold Anlage 10, so it will happily agree that 182 kWh per square metre and year is class C. The band table says F. The linter reads the file and applies the table.
Free is complete for one ad: open the template, run the lint, get every Section 87 defect with its line number and clause, in the editor or on the web page. The licence key adds scope and ownership: one run over every template in the workspace or the CI feed, and a dated audit record you keep.
A Bedarfsausweis with an on-site visit runs about 300 to 500 euro in 2026, and the Energieberater who writes it never opens your template. A missing or contradictory mandatory field in the published ad is an Ordnungswidrigkeit that can be fined up to 10,000 euro per ad under Section 108 GModG.
A general AI chat answers from training data with a cutoff date, cannot read your repository and names no rule version. Immobilienanzeige GEG 87 Lint checks the file you open against 13 rules, each naming its legal or official basis, from a rule set dated 2026-09-20, and points at the exact line with the fix. For a filing, an audit or a client you need that dated result on your own files.
One question, answered by the person who built it. Your email only if you want the answer sent.
Paste this code into your page. It runs in your visitor’s browser and stays up to date. Nothing your visitors type is stored.
For businesses: your name and logo instead of ours, and each visitor’s result + e-mail in your inbox (only when they tick consent) — Pro from $19/month. See Pro →