# German Construction Tenders + GAEB Bill of Quantities Extractor (`brsksr/german-construction-tenders-gaeb`) Actor

German public construction tenders from the official oeffentlichevergabe.de API with their GAEB bills of quantities (.x83/.d83/.p83) as clean JSON: OZ, short/long text, quantity, unit. Plus deadline and location. No portal account needed.

- **URL**: https://apify.com/brsksr/german-construction-tenders-gaeb.md
- **Developed by:** [BRK Data Systems](https://apify.com/brsksr) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.035 / extracted gaeb tender (boq)

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## German Construction Tenders + GAEB Bill of Quantities Extractor

**Every German public construction tender, with its complete bill of quantities as clean JSON. No portal account needed.**

This Actor reads the official open-data feed of [oeffentlichevergabe.de](https://oeffentlichevergabe.de) (the German federal notice service), opens the public tender documents on the e-procurement portals, finds the **GAEB bill of quantities** (`.x83`, `.d83`, `.p83`) and turns every position into structured data: ordinal number (OZ), short text, long text, quantity, unit, position type. You also get the **submission deadline**, **place of performance** (postal code, city, NUTS region, federal state) and the contracting authority.

🇩🇪 [Deutsche Beschreibung weiter unten](#deutsch)

### Why this Actor

- **The bill of quantities, not just the notice.** Tender notices tell you *that* a school gets a new roof. The GAEB LV tells you *what exactly* is needed: 1,240 m² bitumen membrane, 86 m gutter, 14 roof outlets. That is the information sales teams and AI agents need.
- **Keyword search inside LV positions.** Search for your product, material or brand ("Wärmepumpe", "Gussasphalt", "Rohrrahmentür") in the short and long texts of all positions, not only in the notice title. Matching positions are returned with their OZ.
- **No account, no login, no manual downloads.** Only publicly accessible tender documents are used (German law, § 41 VgV, requires them to be freely and directly accessible).
- **All three GAEB formats.** GAEB DA XML 3.x (`.x83`), GAEB 90 (`.d83`) and GAEB 2000 (`.p83`). Each format was cross-checked against the others using real tenders: identical OZ, quantities, units and texts.
- **Fair pricing.** You only pay for tenders with **at least one extracted position**. Tenders without documents, without GAEB or with unparseable files are free.
- **Built for automation.** Flat, stable JSON. One item per tender, positions as an array, plus a ready-made dataset view with one row per position.

### What you get

| Field | Description |
|---|---|
| `title`, `description`, `cpvCodes`, `procedure`, `lots` | From the official notice |
| `submissionDeadline`, `deadlineType` | Deadline for offers (`offer`) or participation requests (`participationRequest`), ISO 8601, Europe/Berlin. Usually date and time; date only (`2026-11-19`) when the portal shows no time |
| `location` | `postalCode`, `city`, `street`, `nutsCode`, `region` (federal state), `source` (`placeOfPerformance` / `buyer` / `mixed`, `+portal` if gaps were filled from the portal page) |
| `buyer` | Contracting authority: name, address, NUTS code, e-mail |
| `referenceNumber` | Reference number (Geschäftszeichen / tender ID) of the portal |
| `documentsUrl`, `portal` | Public documents page |
| `gaebFiles[]` | Parsed GAEB files: format, version, project name, LV name, position count |
| `positions[]` | `oz`, `category`, `categoryPath`, `shortText`, `longText`, `quantity`, `unit`, `positionType`, `isLumpSum`, `refOz`, `sourceFile` |
| `matchedKeywords`, `keywordPositionHits[]` | Which keywords matched and in which positions |

`positionType` is one of `normal`, `lumpSum` (Pauschalposition), `provisional` (Bedarfsposition), `base` / `alternative` (Grund-/Wahlposition), `reference` / `repetition` (Bezugs-/Wiederholungsposition; repetitions inherit the long text of the referenced position).

#### Example output (real tender from 30 Oct 2026, shortened to 3 of 36 positions)

```json
{
  "title": "Los 24 - Schlosserarbeiten; Landestheater Altenburg, Theaterplatz 19, 04600 Altenburg, Sanierung Kronenboden",
  "pubDay": "2026-10-30",
  "submissionDeadline": "2026-12-08T10:00:00+01:00",
  "deadlineType": "offer",
  "referenceNumber": "HB-B 023-2025-24",
  "cpvCodes": ["45262670"],
  "procedure": "Open",
  "location": {
    "postalCode": "04600",
    "city": "Altenburg",
    "street": "Theaterplatz 19",
    "nutsCode": "DEG0M",
    "region": "Thüringen",
    "country": "DEU",
    "source": "placeOfPerformance"
  },
  "buyer": {
    "name": "Landkreis Altenburger Land",
    "street": "Lindenaustraße 9",
    "postalCode": "04600",
    "city": "Altenburg",
    "nutsCode": "DEG0M",
    "email": "vergabestelle@altenburgerland.de"
  },
  "documentsUrl": "https://www.evergabe-online.de/tenderdocuments.html?id=903055",
  "portal": "evergabe-online.de",
  "gaebFiles": [
    {
      "fileName": "LB_LV_GAEB_HB-B_023-2025-24_vom_261026.X83",
      "format": "GAEB DA XML",
      "version": "3.2",
      "exchangePhase": "83",
      "projectName": "LTA Landestheater Altenburg",
      "boqName": "Schlosserarbeiten",
      "positionCount": 36
    }
  ],
  "positionCount": 36,
  "positions": [
    {
      "oz": "01.01.0001",
      "category": "Planung",
      "categoryPath": ["Stahlbau", "Planung"],
      "shortText": "Werk - und Montageplanung",
      "longText": "Erstellung einer Werk- und Montageplanung auf Grundlage eines präzisen durch den AN erstellten Ortsaufmaßes, einschl. einmaliger Einpflege von Änderungen und Plananpassung.\nAusführung erfolgt erst nach Freigabe der Pläne durch den Architekten.",
      "quantity": 1.0,
      "unit": "psch",
      "positionType": "lumpSum",
      "isLumpSum": true,
      "refOz": null,
      "sourceFile": "LB_LV_GAEB_HB-B_023-2025-24_vom_261026.X83"
    },
    {
      "oz": "01.01.0003",
      "category": "Planung",
      "categoryPath": ["Stahlbau", "Planung"],
      "shortText": "Statischer Nachweis Anprallast Geländer UG Ergänzung Bestand",
      "longText": "Prüffähiger statische Nachweis für Anprallast\nfür Ergänzung Geländer Bestand UG erstellen und zum Prüfstatiker zur Prüfung als Papierformat in 2-facher Ausfertigung vorlegen.\nEin Prüfvorlauf von 4 KW ist seitens AN einzurechnen.",
      "quantity": 1.0,
      "unit": "St",
      "positionType": "normal",
      "isLumpSum": false,
      "refOz": null,
      "sourceFile": "LB_LV_GAEB_HB-B_023-2025-24_vom_261026.X83"
    },
    {
      "oz": "01.01.0005",
      "category": "Planung",
      "categoryPath": ["Stahlbau", "Planung"],
      "shortText": "Muster Beschichtung Acrylharzlack RAL",
      "longText": "Rundrohr 40, Acrylharzlack RAL",
      "quantity": 3.0,
      "unit": "St",
      "positionType": "repetition",
      "isLumpSum": false,
      "refOz": "01.01.0004",
      "sourceFile": "LB_LV_GAEB_HB-B_023-2025-24_vom_261026.X83"
    }
  ],
  "matchedKeywords": ["Geländer"],
  "keywordPositionHits": [
    { "oz": "01.01.0003", "keywords": ["Geländer"] },
    { "oz": "01.04.0004", "keywords": ["Geländer"] }
  ],
  "status": "gaeb_extracted"
}
```

### Input examples

**Daily nationwide feed: all construction tenders with a parsed bill of quantities**

```json
{ "pubDaysBack": 1, "cpvPrefixes": ["45"], "onlyWithGaeb": true }
```

**Building products across Germany: only tenders whose LV mentions your products**

```json
{ "pubDaysBack": 3, "cpvPrefixes": ["45"], "keywords": ["Wärmepumpe", "Beton", "Fenster", "Brandschutz"] }
```

Add `"postalCodePrefixes": ["10", "80"]` or `"nutsPrefixes": ["DE1", "DE2"]` to limit the search to your sales region.

**Road construction, last 7 days, complete ZIP download**

```json
{ "pubDaysBack": 7, "cpvPrefixes": ["45233"], "downloadMode": "zip" }
```

All filters except `keywords` are applied **before** any document is downloaded, so narrow filters make runs faster and cheaper.

### Use cases

- **n8n**: a *Schedule Trigger* runs the Actor every morning through the Apify node. An *IF* node routes tenders with `matchedKeywords` to Slack, Microsoft Teams or your CRM (HubSpot, Pipedrive) as new leads with deadline, location and matching LV positions.
- **Make.com**: the Apify module *Watch Actor Runs* plus *Get Dataset Items* writes the **positions** view (one row per LV position) to Google Sheets or Airtable. Sales gets a live list of quantities per product group.
- **LangChain / LlamaIndex**: load the dataset with `ApifyDatasetLoader`, embed `shortText` + `longText` per position and let an agent answer questions like *"Which tenders with a deadline in the next 3 weeks need more than 500 m² of facade insulation?"*.
- **AI agents via MCP**: through the [Apify MCP server](https://mcp.apify.com) the Actor becomes a tool for Claude, ChatGPT, Cursor and other MCP clients. The agent can fetch current tenders and reason over real bills of quantities.
- **Building material suppliers and manufacturers**: find every tender that specifies your product category, with quantities for lead scoring, before your competitors read the PDF.
- **Contractors and estimators**: pre-screen tenders by scope and quantity, and import positions into your own calculation tool.

### Coverage and status values

Notices come from all of Germany (about 300 to 400 construction notices per working day). Documents are currently read from:

- **evergabe-online.de** (federal platform)
- **evergabe.de** (most tenders from Saxony, Saxony-Anhalt and Thuringia, plus many municipalities nationwide)
- **subreport ELViS** (subreport-elvis.de, widely used by municipalities in NRW, Lower Saxony, Rhineland-Palatinate, Saarland and Baden-Württemberg). The portal is a JavaScript application, so the Actor opens it in a headless Chromium that is only started when a run actually contains subreport tenders. Procedures whose contracting authority allows downloads only after login are reported as `login_required` (free)
- **AI NetServer platforms**: Die Autobahn GmbH des Bundes (vergabe.autobahn.de), HAD / eHAD Hessen (had.de), vergabe.hessen.de, Land Baden-Württemberg, Bremen, Brandenburg, Düsseldorf, Köln, Frankfurt, Halle, vergabekooperation.berlin, tender24, Fraunhofer and other platforms running the same software
- **Healy Hudson bidder portals**: Deutsche Bahn (bieterportal.noncd.db.de) and the City of Hamburg (fbhh-evergabe). Railway tenders often pack the bill of quantities into 150-550 MB archives with drawings. Archives up to 2.5 × `maxZipSizeMb` (250 MB by default) are streamed to disk and only their GAEB files are read; raise `maxZipSizeMb` to include larger ones
- **Deutsche eVergabe** (deutsche-evergabe.de, including procedures hosted in its partner system)
- **RIB meinauftrag** (Vergabeplattform Bayern, Berlin, Stuttgart, Hannover, Wuppertal, GMSH). Note: RIB publishes the bill of quantities as a generated PDF; GAEB files are only public when the contracting authority uploads them, so only a small share of RIB tenders yields positions
- **cosinex marketplaces**: dtvp.de, evergabe.nrw.de, vergabe.niedersachsen.de, vergabemarktplatz.brandenburg.de, vergabeportal-bw.de, vergabe.rlp.de, vergabe.metropoleruhr.de and others with the same software

Live check on two working days (29/30 Oct 2026, 648 construction notices): **551 notices (85 %) are on supported portals; 347 of them yielded a parsed bill of quantities with 70,983 positions in total.**

| portal | tenders | with GAEB positions | hit rate |
|---|---|---|---|
| cosinex marketplaces | 154 | 74 | 48 % (Niedersachsen 95 %, Brandenburg 91 %) |
| subreport ELViS | 101 | 79 | 78 % |
| evergabe.de | 68 | 60 | 88 % |
| RIB meinauftrag | 62 | 1 | 2 % |
| AI NetServer: other platforms (Hessen, Land BW, Bremen, Düsseldorf, ...) | 61 | 45 | 74 % |
| Deutsche eVergabe | 35 | 30 | 86 % |
| Deutsche Bahn (Healy Hudson) | 18 | 12 | 67 % (4 more above the default 250 MB archive limit) |
| evergabe-online.de | 17 | 15 | 88 % |
| HAD Hessen (AI NetServer) | 14 | 10 | 71 % |
| Die Autobahn (AI NetServer) | 13 | 13 | 100 % in a local test; currently not reachable from the Apify cloud (`portal_unreachable`, free) |
| Hamburg (Healy Hudson) | 8 | 8 | 100 % |

The remaining tenders have no public GAEB (PDF-only documents, login-only procedures, ex-post notices, informal procedures), use another software version (e.g. the new xvergabe.de portal) or the portal was temporarily unreachable. They are free.

Each tender gets a `status`:

| status | meaning | charged |
|---|---|---|
| `gaeb_extracted` | at least one GAEB position extracted | **yes** |
| `no_gaeb_listed` | documents are public but contain no GAEB file | no |
| `no_public_documents` | no public documents (e.g. ex-post transparency notices, restricted procedures) | no |
| `login_required` | the portal requires a login / registration for the documents | no |
| `gaeb_not_found` / `gaeb_failed` | archive without GAEB / file not parseable | no |
| `unsupported_portal` / `portal_unreachable` / `error` | portal not supported yet / temporarily unreachable | no |

With `onlyWithGaeb: true` (default) only `gaeb_extracted` tenders are written to the dataset.

### Pricing

Pay per event: **one event per tender with at least one extracted LV position** (`gaeb-tender-extracted`). Notice filtering, failed downloads and tenders without a bill of quantities are never charged. Set a maximum cost per run in the Apify Console; the Actor stops cleanly before exceeding it. See the *Pricing* tab for the current price.

***

<a id="deutsch"></a>

## 🇩🇪 Öffentliche Bauausschreibungen + GAEB-Leistungsverzeichnisse als JSON

**Alle öffentlichen Bauausschreibungen in Deutschland, inklusive vollständigem Leistungsverzeichnis als strukturierte Daten. Ohne Portal-Account.**

Der Actor liest den amtlichen Open-Data-Bekanntmachungsservice [oeffentlichevergabe.de](https://oeffentlichevergabe.de), öffnet die frei zugänglichen Vergabeunterlagen auf den Vergabeplattformen, findet das **GAEB-Leistungsverzeichnis** (`.x83`, `.d83`, `.p83`) und liefert jede Position als JSON: Ordnungszahl (OZ), Kurztext, Langtext, Menge, Einheit, Positionsart. Dazu kommen **Angebotsfrist**, **Ausführungsort** (PLZ, Ort, NUTS-Region, Bundesland) und Vergabestelle.

### Ihre Vorteile

- **Das Leistungsverzeichnis statt nur der Bekanntmachung.** Die Bekanntmachung sagt, *dass* eine Schule ein neues Dach bekommt. Das LV sagt, *was genau* gebraucht wird: 1.240 m² Bitumenbahn, 86 m Dachrinne, 14 Dachgullys. Genau das brauchen Vertrieb und Kalkulation.
- **Keyword-Suche direkt in den LV-Positionen.** Suchen Sie nach Ihrem Produkt, Material oder Fabrikat („Wärmepumpe“, „Gussasphalt“, „Rohrrahmentür“) in Kurz- und Langtexten aller Positionen. Treffer kommen mit Ordnungszahl zurück.
- **Kein Account, kein Login, kein manueller Download.** Es werden nur öffentlich zugängliche Vergabeunterlagen verarbeitet (§ 41 VgV: unentgeltlich, uneingeschränkt, vollständig und direkt abrufbar).
- **Alle drei GAEB-Formate.** GAEB DA XML 3.x (`.x83`), GAEB 90 (`.d83`) und GAEB 2000 (`.p83`). Die Formate wurden an echten Ausschreibungen gegeneinander geprüft: identische OZ, Mengen, Einheiten und Texte.
- **Faire Abrechnung.** Sie zahlen nur für Ausschreibungen mit **mindestens einer extrahierten LV-Position**. Ausschreibungen ohne Unterlagen, ohne GAEB oder mit defekten Dateien sind kostenlos.
- **Gebaut für Automatisierung.** Flaches, stabiles JSON: ein Datensatz pro Ausschreibung, Positionen als Array, dazu eine fertige Ansicht mit einer Zeile pro LV-Position.

### Typische Einsatzszenarien

- **Baustoffhandel und Hersteller**: Jeden Morgen alle Ausschreibungen im Vertriebsgebiet (PLZ- oder NUTS-Filter), deren LV Ihr Produkt enthält, inklusive Mengen für die Lead-Priorisierung.
- **Bauunternehmen und Kalkulation**: Ausschreibungen nach Leistungsumfang vorfiltern, Positionen direkt in die eigene Kalkulation übernehmen und Fristen im Blick behalten.
- **n8n / Make.com**: täglicher Lauf per Zeitplan; Treffer gehen als Lead ins CRM, in Teams oder Slack, die Positionen landen in Google Sheets oder Excel.
- **KI-Agenten (LangChain, MCP)**: Fragen wie *„Welche Ausschreibungen mit Frist in den nächsten drei Wochen enthalten mehr als 200 m Geländer?“* beantwortet der Agent auf Basis der echten LV-Daten.

### Eingabe-Beispiel: Bauausschreibungen von gestern aus ganz Deutschland, deren LV Ihre Produkte enthält

```json
{ "pubDaysBack": 1, "cpvPrefixes": ["45"], "keywords": ["Wärmepumpe", "Beton", "Fenster", "Brandschutz"] }
```

Für ein Vertriebsgebiet ergänzen Sie z. B. `"postalCodePrefixes": ["10", "80"]` oder `"nutsPrefixes": ["DE1", "DE2"]`.

Die wichtigsten Filter:

- `cpvPrefixes`: z. B. `45` Bau gesamt, `4526` Dach, `4533` Gebäudetechnik, `45233` Straßenbau
- `postalCodePrefixes`: PLZ des Ausführungsorts
- `nutsPrefixes`: z. B. `DED` Sachsen, `DEA` NRW, `DE2` Bayern
- `keywords`: durchsucht Titel, Beschreibung **und** alle LV-Positionen

Alle Filter außer `keywords` greifen **vor** dem Download. Enge Filter sparen also Zeit und Kosten.

### Abdeckung

Bekanntmachungen aus ganz Deutschland (ca. 300 bis 400 Bau-Bekanntmachungen pro Werktag). Vergabeunterlagen werden derzeit gelesen von **evergabe-online.de**, **evergabe.de** (der Großteil der Ausschreibungen aus Sachsen, Sachsen-Anhalt und Thüringen sowie viele Kommunen bundesweit), **subreport ELViS** (viele Kommunen in NRW, Niedersachsen, Rheinland-Pfalz, Saarland und Baden-Württemberg; das Portal wird in einem Headless-Browser geöffnet, der nur startet, wenn ein Lauf subreport-Ausschreibungen enthält), den **AI-NetServer-Plattformen** (Die Autobahn GmbH des Bundes, HAD/eHAD Hessen, Vergabeplattform Hessen, Land Baden-Württemberg, Bremen, Brandenburg, Düsseldorf, Köln, Frankfurt, Halle, Vergabekooperation Berlin, tender24, Fraunhofer u. a.), den **Healy-Hudson-Bieterportalen** (Deutsche Bahn, Freie und Hansestadt Hamburg; Archive bis 2,5 × `maxZipSizeMb`, also standardmäßig 250 MB, werden auf die Festplatte gestreamt und nur ihre GAEB-Dateien gelesen; für größere Bahn-Archive `maxZipSizeMb` erhöhen), **Deutsche eVergabe** (inkl. Partnersystem), **RIB meinauftrag** (Vergabeplattform Bayern, Berlin, Stuttgart, Hannover, Wuppertal, GMSH) und den **cosinex-Vergabemarktplätzen** (u. a. DTVP, Vergabemarktplatz NRW, Niedersachsen, Brandenburg, Baden-Württemberg, Rheinland-Pfalz, Metropole Ruhr). Bei RIB liegt das Leistungsverzeichnis meist nur als von RIB erzeugtes PDF vor; GAEB-Dateien sind nur öffentlich, wenn die Vergabestelle sie selbst hochlädt. Daher liefern nur wenige RIB-Ausschreibungen Positionen (die übrigen sind kostenlos). Stichprobe an zwei Werktagen (29./30.10.2026, 648 Bau-Bekanntmachungen): **551 Bekanntmachungen (85 %) liegen auf unterstützten Plattformen, bei 347 davon wurde das Leistungsverzeichnis mit insgesamt 70.983 Positionen extrahiert.** Trefferquoten: Die Autobahn 100 % (im lokalen Test; aus der Apify-Cloud antwortet die Plattform derzeit nicht, Status `portal_unreachable`, kostenlos), Hamburg 100 %, evergabe.de 88 %, evergabe-online.de 88 %, Deutsche eVergabe 86 %, subreport ELViS 78 %, weitere AI-NetServer-Plattformen 74 %, HAD Hessen 71 %, Deutsche Bahn 67 % (4 weitere Archive über dem Standardlimit von 250 MB), cosinex 48 % (Niedersachsen 95 %), RIB 2 %. Unterlagen, die erst nach Registrierung verfügbar sind (Status `login_required`), werden nicht verarbeitet und nicht berechnet. Die Angebotsfrist enthält in der Regel Datum und Uhrzeit; zeigt das Portal keine Uhrzeit, wird nur das Datum geliefert (`2026-11-19`).

### Abrechnung

Pay-per-Event: **ein Event pro Ausschreibung mit mindestens einer extrahierten LV-Position**. Alles andere ist kostenlos. Über das Kostenlimit pro Lauf in der Apify Console behalten Sie die volle Kostenkontrolle. Der Actor hält vor Überschreiten des Limits sauber an.

***

*Data source: open-data notice exports of oeffentlichevergabe.de and the public documents pages of the respective e-procurement portals. Tender documents are processed in memory (subreport ELViS downloads and large Healy Hudson archives are kept in a temporary folder on the container disk that is cleared after each tender); this Actor stores nothing but the extracted JSON in your dataset.*

# Actor input Schema

## `pubDaysBack` (type: `integer`):

Number of calendar days to load, counting back from yesterday (today's export is published the next day). Weekends are included; they usually carry fewer notices.

## `pubDays` (type: `array`):

Optional list of specific publication days, each in YYYY-MM-DD format (one day per entry). Overrides 'Days back'. Leave empty for a rolling daily search.

## `cpvPrefixes` (type: `array`):

Keep notices with at least one CPV code starting with one of these prefixes. 45 = all construction work, 453 = building installation work, 4526 = roof works, 45233 = road construction. Leave empty for all categories.

## `postalCodePrefixes` (type: `array`):

Keep only tenders whose place of performance (or, if missing, the contracting authority's seat in the same district) has a German postal code starting with one of these prefixes, e.g. \["10", "80"] for the Berlin and Munich areas. Notices without a postal code are excluded while this filter is set.

## `nutsPrefixes` (type: `array`):

Keep only tenders whose place of performance lies in these NUTS regions. DE = all of Germany. Federal states: DE1 Baden-Württemberg, DE2 Bayern, DE3 Berlin, DE4 Brandenburg, DE5 Bremen, DE6 Hamburg, DE7 Hessen, DE8 Mecklenburg-Vorpommern, DE9 Niedersachsen, DEA Nordrhein-Westfalen, DEB Rheinland-Pfalz, DEC Saarland, DED Sachsen, DEE Sachsen-Anhalt, DEF Schleswig-Holstein, DEG Thüringen. Districts work too, e.g. DEA22 (Bonn).

## `keywords` (type: `array`):

Optional, case-insensitive. A tender is kept if a keyword appears in its title/description OR in the short or long text of any GAEB position, e.g. a product or material such as "Wärmepumpe", "Gussasphalt" or "Rohrrahmentür". Matching positions are listed in 'keywordPositionHits'.

## `onlyWithGaeb` (type: `boolean`):

If enabled (recommended), only tenders with at least one extracted GAEB position are written to the dataset. If disabled, all matching notices are written, including those without documents; these extra records are free of charge.

## `maxTenders` (type: `integer`):

The run stops after this many dataset items. Use it to cap your costs.

## `portals` (type: `array`):

Document portals to download from.

## `downloadMode` (type: `string`):

direct: download only the listed GAEB files and LV sub-archives (fast, little traffic) and fall back to the complete ZIP only if nothing was found. zip: always download the complete tender ZIP.

## `maxZipSizeMb` (type: `integer`):

Largest file loaded into memory; keep the Actor memory at least 4 times this value. Tender archives on subreport ELViS and the Healy Hudson portals (e.g. Deutsche Bahn) may be up to 2.5 times larger: they are streamed to the container disk and only their GAEB files are read into memory. Raise this value to search very large archives (e.g. 500 MB Deutsche Bahn track-construction packages); longer downloads mean higher run costs.

## `includeLongText` (type: `boolean`):

Include the full long text (Langtext) of every position. Disable for smaller items if you only need short texts and quantities.

## `maxConcurrency` (type: `integer`):

Number of tenders processed in parallel. Keep it low to stay polite to the public portals.

## Actor input object example

```json
{
  "pubDaysBack": 3,
  "cpvPrefixes": [
    "4526",
    "4533"
  ],
  "postalCodePrefixes": [
    "10",
    "80"
  ],
  "nutsPrefixes": [
    "DE"
  ],
  "keywords": [
    "Wärmepumpe",
    "Beton",
    "Fenster",
    "Brandschutz"
  ],
  "onlyWithGaeb": true,
  "maxTenders": 20,
  "portals": [
    "evergabe-online",
    "cosinex",
    "evergabe-de",
    "subreport",
    "netserver",
    "healy-hudson",
    "deutsche-evergabe",
    "rib"
  ],
  "downloadMode": "direct",
  "maxZipSizeMb": 100,
  "includeLongText": true,
  "maxConcurrency": 3
}
```

# Actor output Schema

## `tenders` (type: `string`):

Structured tenders including parsed GAEB positions, quantities, units, and metadata.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "pubDaysBack": 3,
    "cpvPrefixes": [
        "45"
    ],
    "maxTenders": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("brsksr/german-construction-tenders-gaeb").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "pubDaysBack": 3,
    "cpvPrefixes": ["45"],
    "maxTenders": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("brsksr/german-construction-tenders-gaeb").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "pubDaysBack": 3,
  "cpvPrefixes": [
    "45"
  ],
  "maxTenders": 20
}' |
apify call brsksr/german-construction-tenders-gaeb --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,brsksr/german-construction-tenders-gaeb"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/wPOtPnacm6hux56iV/builds/wGAx7tPlp09t6Er45/openapi.json
