preisindex.<domain>)
zeigt. Wer per Skript zugreifen will (Python, Excel-PowerQuery, Power BI,
Make/Zapier, n8n etc.), nutzt direkt die Endpoints unten.
Ein Markt-Token (JWT) in der URL als Query-Parameter ?t=….
Token bekommst du:
preisindex-partner.<domain> → Block
„Markt-Links verwalten" → „Token erstellen"Der Token ist token-gebunden, nicht user-gebunden — jeder mit dem Token kann lesen. Token können im UI revoked werden. Default-Laufzeit 90 Tage, maximal 365.
Liefert alle anonymisierten Angebote als JSON. Anonymisierung:
(Anbieter, Projekt, Kategorie)-Trio nur ein Datensatz
(Rechnung > Auftragsbestätigung > Angebot)review_status='verworfen' werden ausgeblendet{
"angebote": [
{
"id": "heimrich-aufdach-2025-04",
"konfidenz": 0.92,
"review_status": "reviewed",
"datum": "2025-04-12",
"daten": {
"id": "heimrich-aufdach-2025-04",
"kategorie": "Aufdachdaemmung_PUR_16",
"subkategorie": "Schraegdach",
"produkt": {
"hersteller": "Bauder",
"produktname": "PIR-PLUS-160",
"material": "PUR_PIR",
"dicke_mm": 160,
"wls": 0.023
},
"menge": 142,
"einheit": "m2",
"preis": {
"ep_netto": 240.00,
"ep_brutto": 285.60,
"ep_netto_vergleichbar": 258.00,
"summe_netto": 34080.00,
"summe_brutto": 40555.20,
"aufschlaege": { "geruest": 18.00 }
},
"projekt": {
"region": "Mittelhessen",
"bauweise": "EFH"
},
"datum": "2025-04-12",
"leistungsumfang": {
"enthalten": ["demontage_bestand", "entsorgung", "geruest"],
"nicht_enthalten": ["kran"]
},
"nebengewerke_zusammenfassung": [
{ "pos": "Dachfenster", "preis_netto": 950.0, "kommentar": "VELUX GGL" }
],
"tags": ["plausi-auffaellig"]
}
},
...
]
}
daten-Block| Feld | Typ | Beschreibung |
|---|---|---|
id | string | Slug — eindeutig pro Angebot |
kategorie | string | Slug aus 21 Vergleichs-Kategorien (siehe kategorien_spec.json) |
subkategorie | string | Freitext-Verfeinerung |
produkt.material | string | z.B. EPS, PUR_PIR, Mineralwolle, Holz, Aluminium, ... |
produkt.dicke_mm | number | Dämmstoff-Dicke in mm |
produkt.wls | number | Wärmeleitstufe |
produkt.u_wert | number | U-Wert in W/m²K (Fenster/Dachfenster) |
produkt.leistung_kw | number | Heizleistung (Wärmepumpe) |
produkt.leistung_kwp | number | PV-Modul-Peak |
menge | number | Menge |
einheit | string | m2, Stk, Anlage, kWp, ... |
preis.ep_netto | number | Einheitspreis netto |
preis.ep_brutto | number | Einheitspreis brutto inkl. 19 % MwSt. |
preis.ep_netto_vergleichbar | number | EP netto inkl. kalk. Aufschläge für fehlende Pflicht-Posten |
preis.aufschlaege | object | Was wurde kalk. dazugerechnet (slug → €/Einheit) |
projekt.region | string | Mittelhessen, Rhein-Main, Westerwald, Taunus, Sonstiges |
projekt.bauweise | string | EFH, DHH, RH, MFH, WEG, Nichtwohngebaeude, Unbekannt |
datum | string | Angebotsdatum, Format YYYY-MM-DD |
leistungsumfang.enthalten | string[] | Slugs der enthaltenen Nebenleistungen |
leistungsumfang.nicht_enthalten | string[] | Slugs der nicht enthaltenen Nebenleistungen |
tags | string[] | z.B. plausi-auffaellig, pflicht-posten-fehlen, BAFA-Antrag |
Die vollständige Schema-Definition mit allen Enums und optionalen Feldern: data/schema.json.
Liefert die Whitelabel-Brand-Informationen des Partners, der den Token erstellt hat (oder leer wenn kein Branding gesetzt).
{
"anzeigename": "Energieberatung Bloemer",
"logo_url": "/api/brand-logo/cbloemer.png?v=1747234567",
"akzent_farbe": "#2A6F4A",
"footer_zusatz": "Energieberatung Bloemer · 06195/12345",
"eigene_domain": "marktpreise.bloemer.de"
}
Nützlich wenn du Markt-Daten in eine eigene UI einbettest und das Partner-Branding mit anzeigen willst.
Alle 21 Vergleichs-Kategorien mit Einheit, Pflicht-Posten und Plausi-Ranges:
Diese Spec ist öffentlich, kein Token nötig — sie ist rein konfigurativ (keine Nutzerdaten). Antwort-Beispiel:
{
"labels": {
"WDVS_EPS_16": "Außendämmung · EPS · 16 cm",
"Waermepumpe_LuftWasser": "Wärmepumpe · Luft-Wasser · EFH",
...
},
"specs": {
"WDVS_EPS_16": {
"einheit": "m2",
"match": {
"material_in": ["EPS"],
"dicke_mm_min": 140,
"schluesselwoerter": ["wdvs", "wärmedämm-verbundsystem", ...]
},
"pflicht_enthalten": ["putz_anschlussarbeiten"],
"darf_fehlen_mit_aufschlag": {
"geruest": 12.0,
"demontage_bestand": 6.0,
"entsorgung": 4.0
},
"range_min": 80.0,
"range_max": 220.0
},
...
}
}
| Status | Bedeutung | Antwort-Body |
|---|---|---|
401 | Token ungültig, abgelaufen oder revoked | {"detail":"…"} |
422 | ?t=-Parameter fehlt | Pydantic-Validation-Error |
429 | Rate-Limit überschritten | {"detail":"Zu viele Anfragen. Bitte in ~Xs erneut versuchen."} |
5xx | Server-Fehler — Marco kontaktieren | generisch |
ALLOWED_ORIGINS ergänzen.TOKEN="eyJhbGciOiJIUzI1NiIs..." # dein Markt-Token
# Alle Angebote holen, nach Wärmepumpen filtern
curl -s "https://preisindex.<domain>/api/markt/angebote?t=$TOKEN" \
| jq '.angebote[] | select(.daten.kategorie == "Waermepumpe_LuftWasser")
| {datum: .daten.datum, kw: .daten.produkt.leistung_kw,
ep: .daten.preis.ep_brutto}'
# Spec separat holen (kein Token nötig)
curl -s "https://preisindex.<domain>/api/spec" | jq '.labels'
import requests
TOKEN = "eyJhbGciOiJIUzI1NiIs..."
BASE = "https://preisindex.<domain>"
# Alle Angebote
r = requests.get(f"{BASE}/api/markt/angebote", params={"t": TOKEN}, timeout=15)
r.raise_for_status()
angebote = r.json()["angebote"]
# Median-Preis pro Kategorie ausrechnen
from statistics import median
from collections import defaultdict
preise = defaultdict(list)
for a in angebote:
d = a["daten"]
p = d["preis"].get("ep_netto_vergleichbar") or d["preis"].get("ep_netto")
if p:
preise[d["kategorie"]].append(p * 1.19) # brutto
for kat, vals in preise.items():
print(f"{kat:30s} n={len(vals):3d} median={median(vals):.2f} €")
const TOKEN = "eyJhbGciOiJIUzI1NiIs...";
const BASE = "https://preisindex.";
const r = await fetch(`${BASE}/api/markt/angebote?t=${TOKEN}`);
if (!r.ok) throw new Error(`HTTP ${r.status}`);
const { angebote } = await r.json();
const wp = angebote.filter(a => a.daten.kategorie === "Waermepumpe_LuftWasser");
console.log(`${wp.length} Wärmepumpen-Angebote`);
let
Quelle = Json.Document(Web.Contents(
"https://preisindex.<domain>/api/markt/angebote?t=DEIN_TOKEN")),
angebote = Quelle[angebote],
tbl = Table.FromList(angebote, Splitter.SplitByNothing()),
daten = Table.ExpandRecordColumn(tbl, "Column1", {"daten"}, {"daten"}),
flach = Table.ExpandRecordColumn(daten, "daten",
{"kategorie","datum","preis","produkt","projekt"},
{"kategorie","datum","preis","produkt","projekt"})
in
flach
Komfortablere Tools mit Filtern und Aggregation:
mcp-preisindex.<domain>/mcp.
Bearer-Token-Auth (im Partner-UI generieren), JSON-RPC 2.0. Tools wie
markt_statistik, markt_angebote_listen,
angebot_bewerten liefern bereits aggregierte/gefilterte Daten.
Beispiel-Aufruf:
curl -X POST https://mcp-preisindex.<domain>/mcp \
-H "Authorization: Bearer sp_partner_..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "markt_statistik",
"arguments": { "kategorie": "Waermepumpe_LuftWasser" }
}
}'
Wenn du als Partner eine eigene Domain hast (z.B. marktpreise.deinedomain.de),
funktioniert die Markt-API unter deiner Domain genauso —
einfach den Host austauschen:
curl "https://marktpreise.deinedomain.de/api/markt/angebote?t=$TOKEN"