# German Grid Congestion Monitor (Redispatch) (`codeclouds/de-netzengpass-redispatch-monitor`) Actor

Track Germany's official redispatch measures: day-level Netzengpass data with MW/MWh, reason, direction and transmission operator, plus a congestion status per operator and month that excludes commissioning tests. Official BNetzA source, from $0.001/measure (PPE).

- **URL**: https://apify.com/codeclouds/de-netzengpass-redispatch-monitor.md
- **Developed by:** [Dennis](https://apify.com/codeclouds) (community)
- **Categories:** Other, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 redispatch massnahmes

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Grid Congestion Monitor (Redispatch)

Structured, diff-based tracking of Germany's official **redispatch measures** — the only country-wide, day-level, machine-readable measure of real grid congestion. Source is the redispatch publication of the German Federal Network Agency (Bundesnetzagentur, SG HoBA) on `netztransparenz.de`.

**Field names and values are in German** (`grundDerMassnahme`, `congestieStatus: "hoch"`, `anweisenderUENB`, …) because that is what the source publishes and what a German grid audience expects. Listing text and this page are in English for reach; the local search terms are in the [Keywords](#keywords) section and in the [Häufige Fragen (deutsch)](#häufige-fragen-deutsch) section at the bottom.

### When should an AI agent use this?

- "Where were the worst grid congestion hotspots in the German transmission grid last month?"
- "Which plants was Amprion ordered to curtail, and how often?"
- "How much congestion-relevant redispatch energy did each transmission operator shift, and did it escalate?"
- "Which redispatch measures exceeded 1000 MW, and what triggered them?"
- "Were output cuts or offsetting increases dominant in September 2026?"
- "Is transmission operator X's congestion level worse than last month?"

### What this Actor does

- Downloads the official redispatch CSV through the publication page's download export (an ASP.NET WebForms postback). No API key, no login, no guessing a rotating filename.
- Normalises every measure: start/end as ISO timestamps, duration, MW (average and maximum), MWh, reason, direction, the ordering and requesting transmission operators, the affected plant, the primary energy carrier and whether the row is a genuine congestion measure.
- Separates **congestion causes from commissioning tests**: rows with reason `Probefahrt` (probe starts and function tests of reserve plants) are flagged `istEngpassrelevant: false` and excluded from every congestion metric, while remaining visible in the totals.
- Additionally delivers one **monthly profile** per transmission operator and calendar month, with the total and congestion-relevant energy, the energy per covered calendar day, measure and plant counts, the direction split, the energy-carrier mix, the main reason and **two derived congestion statuses**.
- Detects new and changed measures between runs via a durable named key-value store.
- Fires a dedicated pay-per-event alert when a monthly profile drops into a stricter status band than the previous run.
- Caps delivery (`maxMassnahmen`, default 500) and orders by significance, so a country-wide overview is cheap; a free summary always covers the full filtered set, and records outside the cap are re-detected on the next run instead of being dropped.

### Scope and limitations

This is a **transmission-level** indicator (high voltage, 220/380/400 kV) covering the four German transmission system operators. It says **nothing** about connection-level capacity at distribution level: Germany has no country-wide machine-readable `Netzengpassgebiet` or connection queue per region. The Apify Store searches for `Netzengpass`, `Netzausbaugebiet` and `netzkonflikt` returned zero direct competitors on 2026-09-29.

#### Congestion status

The statuses are **documented threshold classifications of the published energy** — not a forecast, not a queue statement, and not a statement about any individual plant's connectability. Both are defined in one place (`classifyCongestionStatus` and `classifyCongestionStatusTagessatz`) so they stay auditable.

`congestieStatus` — on the **congestion-relevant energy of the calendar month** (excluding commissioning tests):

| Status | Congestion-relevant energy per month |
|---|---|
| `keine_engpassmassnahmen` | 0 MWh (only tests, or no measures) |
| `gering` | below 150,000 MWh |
| `erhoeht` | 150,000 – 500,000 MWh |
| `hoch` | 500,000 – 1,000,000 MWh |
| `kritisch` | above 1,000,000 MWh |

`congestieStatusTagessatz` — on the **energy per covered calendar day**, which is the fair comparison for a current, still incomplete month:

| Status | Congestion-relevant energy per day |
|---|---|
| `keine_engpassmassnahmen` | 0 MWh |
| `gering` | below 5,000 MWh |
| `erhoeht` | 5,000 – 15,000 MWh |
| `hoch` | 15,000 – 30,000 MWh |
| `kritisch` | above 30,000 MWh |

`abgedeckteKalendertage` is the denominator: for the **running month** it is the calendar days elapsed so far (the source is still incomplete), and for a **completed month** it is the month length. That way a partial month stays comparable without a month that simply ended quietly looking artificially intense. Both scales are calibrated on the 2026-01 to 2026-09 data set (observed range roughly 19,000 to 1,587,000 MWh per operator-month, and 620 to 52,900 MWh per day). The monthly escalation alert uses the absolute status.

#### Other source limitations the Actor does not hide

- **Day-level granularity only.** The agency deliberately publishes daily averages and maxima ("Mittelwerte und Maximalwerte beziehen sich auf die jeweiligen Tagesanteile"), not the second-by-second curve.
- **`Probefahrt` is not a congestion measure.** The source explains these are probe starts, probe calls and function tests of domestic reserve plants, performed to maintain operational readiness and explicitly *not* triggered by a congestion. They made up about 5.9% of published 2026 energy. Every congestion figure excludes them; `gesamtArbeitMWh`, `massnahmenJeGrund` and `arbeitMWhJeGrund` keep them visible, and `nurEngpassursachen` can drop them from the delivered rows too.
- **Measures crossing a month boundary** (104 in the 2026 set) are attributed to their start month.
- **`dauerStunden` is a naive local-time difference.** Timestamps carry no offset, so a measure spanning a daylight-saving switch can be off by one hour. With typical durations of half an hour to a few hours this affects at most a couple of days per year.
- **No sub-plant geography.** Grid assignment is carried via the *requesting* operator in `anforderndeBetreiber`; `anweisenderUENB` is the control-zone assigner. The publication contains no coordinates per plant.
- **No simultaneous peak.** `summierteMittlereLeistungMW` sums daily averages over the month and therefore adds up across time. `maximaleLeistungMW` is the peak; `durchschnittlicheMittlereLeistungMW` is the average per measure.
- **The source has no period filter.** The page's download export always returns the year-to-date set, whatever the date fields in its form say. `zeitraum` therefore filters inside the Actor, on day level, and that is stated here rather than passed off as a source feature.
- **No foreign exchange volumes.** For cross-border redispatch and countertrade, the source publishes only the German part.

### Input

| Field | Type | Description |
|---|---|---|
| `anweisenderUENB` | array of strings | Optional: `50Hertz`, `Amprion`, `TenneT DE`, `TransnetBW` (case-insensitive). Empty = all. |
| `grundDerMassnahme` | array of strings | Optional: official reason, e.g. `Strombedingter Redispatch`, `Probefahrt`, `Spannungsbedingter Redispatch`, `Strombedingter Countertrade DE-DK1`. Empty = all. |
| `richtung` | array of strings | Optional: `Wirkleistungseinspeisung reduzieren` or `Wirkleistungseinspeisung erhöhen`. Empty = both. |
| `primaerenergieart` | array of strings | Optional: `Konventionell`, `Erneuerbar`, `Sonstiges`. Empty = all. |
| `betroffeneAnlage` | string | Optional: substring of the official plant name, e.g. `Mehrum`, `Börse`, `Dörpen`. Case-insensitive with German umlaut folding: `Doerpen` and `dörpen` both find `Dörpen-West`. |
| `nurEngpassursachen` | boolean | Default `false`. If `true`, drop rows that are not congestion measures (in practice `Probefahrt`). The statuses and the summary exclude them regardless. |
| `minLeistungMW` | number | Optional: minimum for the maximum capacity in MW. |
| `minArbeitMWh` | number | Optional: minimum for the total energy in MWh. |
| `maxMassnahmen` | number | Default `500`, minimum `1`. Cap on delivered measure records; the most significant come first (highest energy, then highest peak, then most recent). Records outside the cap stay out of the change-detection baseline, so the next run detects them again and delivers the next batch — nothing is silently lost, you just receive large sets in batches. Set a high value for everything at once. |
| `zeitraum` | string | `aktuelles_jahr` (default), `letzte_30_tage`, `letzte_7_tage`. |
| `liefermonatsprofil` | boolean | Default `true`: also deliver monthly profiles per operator. |
| `nurNeueOderGeaenderte` | boolean | Default `true`: only deliver and charge new/changed measures. The baseline is shared by all runs of this Actor, so it is not a per-user "first run" switch. |

### Output

Every dataset item carries a `typ` field to tell the three item types apart.

**`redispatch_massnahme`** — one individual measure:

```json
{
  "typ": "redispatch_massnahme",
  "massnahmeId": "15.06.2026|02:00|15.06.2026|06:00|TenneT DE|Wirkleistungseinspeisung reduzieren|Strombedingter Redispatch|Mehrum|Erneuerbar@1500.5~1955.85~5999.4",
  "massnahmeBasis": "15.06.2026|02:00|15.06.2026|06:00|TenneT DE|Wirkleistungseinspeisung reduzieren|Strombedingter Redispatch|Mehrum|Erneuerbar",
  "land": "DE",
  "beginn": "2026-06-15T02:00:00",
  "ende": "2026-06-15T06:00:00",
  "zeitzone": "CEST",
  "dauerStunden": 4,
  "monat": "2026-06",
  "grundDerMassnahme": "Strombedingter Redispatch",
  "richtung": "Wirkleistungseinspeisung reduzieren",
  "mittlereLeistungMW": 1500.5,
  "maximaleLeistungMW": 1955.85,
  "gesamtArbeitMWh": 5999.4,
  "anweisenderUENB": "TenneT DE",
  "anforderndeBetreiber": ["TenneT DE", "TransnetBW"],
  "betroffeneAnlage": "Mehrum",
  "primaerenergieart": "Erneuerbar",
  "istEngpassrelevant": true,
  "isNeuSeitLetztemLauf": true,
  "isGeaendertSeitLetztemLauf": false,
  "vorherigeGesamtArbeitMWh": null,
  "quellenUrl": "https://www.netztransparenz.de/de-de/Systemdienstleistungen/Betriebsfuehrung/Redispatch",
  "abgerufenAm": "2026-09-29T12:00:00.000Z"
}
```

`massnahmeBasis` is the **event slot** — time window, operator, direction, reason, plant and energy carrier — taken from the source fields, never from the CSV row number, so a record stays identifiable when the source reorders or inserts rows. `massnahmeId` adds the value tuple (`<basis>@<average>~<peak>~<energy>`, a missing value as `n`), with `#2`, `#3`, … only when the source publishes exact duplicates. Because the identity is the slot plus the values and not a position within the group, a record keeps the same ID when the source adds or removes a neighbouring row in the same window, and a corrected value shows up as `isGeaendertSeitLetztemLauf` on the same slot instead of as a brand-new record.

**`monatsprofil`** — aggregate per operator and month:

```json
{
  "typ": "monatsprofil",
  "profilId": "profil-TenneT DE|2026-09",
  "land": "DE",
  "uebertragungsnetzbetreiber": "TenneT DE",
  "monat": "2026-09",
  "anzahlMassnahmen": 834,
  "anzahlMassnahmenEngpassrelevant": 822,
  "anzahlBetroffeneAnlagen": 89,
  "abgedeckteKalendertage": 30,
  "gesamtArbeitMWh": 1647382,
  "arbeitMWhEngpassrelevant": 1586788,
  "tagessatzArbeitMWh": 52893,
  "summierteMittlereLeistungMW": 205166.61,
  "durchschnittlicheMittlereLeistungMW": 1930.05,
  "maximaleLeistungMW": 2368,
  "anzahlReduzierungen": 653,
  "anzahlErhoehungen": 181,
  "richtungVerteilung": { "Wirkleistungseinspeisung reduzieren": 653, "Wirkleistungseinspeisung erhöhen": 181 },
  "congestieStatus": "kritisch",
  "congestieStatusTagessatz": "kritisch",
  "congestieStatusVorherigemLauf": "hoch",
  "istEskaliert": true,
  "hauptGrund": "Strombedingter Redispatch",
  "energietraeger": { "Erneuerbar": 1119131.91, "Konventionell": 257091.79, "Sonstiges": 201383.5 },
  "quellenUrl": "https://www.netztransparenz.de/de-de/Systemdienstleistungen/Betriebsfuehrung/Redispatch",
  "abgerufenAm": "2026-09-29T12:00:00.000Z"
}
```

**`zusammenfassung`** — one free item per run covering the **full** filtered set (also when delivery was capped): reporting period, measure and congestion-measure counts, total and congestion-relevant energy, maximum capacity, overall status, per-operator counts and energy (both variants) with statuses, and counts and energy per reason and per energy carrier. It reports `anzahlVorAbschneidung`, `anzahlGeliefert` and `abgeschnitten` so a truncated delivery can never look complete.

### Use cases

- Project developers and energy consultants who need to know whether their region is under network pressure, and how hard.
- Transmission operator and regulatory watchers tracking the distribution of redispatch load per control zone, trends and escalations.
- Energy traders and analysts comparing curtailments against offsetting measures.
- Journalists and analysts who need a citable monthly figure for German grid congestion.
- Data pipelines that need stable JSON instead of parsing the source CSV themselves.

### Pricing

This Actor uses Apify Pay-Per-Event pricing.

- **Actor Start:** Apify default.
- **`redispatch-massnahme`:** $0.001 per delivered measure, once the shared baseline has been seeded.
- **`monatsprofil`:** $0.01 per delivered monthly profile.
- **`congestie-meldung`:** $0.002 extra per measure that is new or changed since the previous run.
- **`congestie-eskalation`:** $0.05 per monthly profile that dropped into a stricter status band than the previous run.
- **Summary:** free, no separate event.

**How the billing actually works.** The change-detection baseline lives in a named store shared by every run of this Actor, and it is seeded once before publication. It is therefore **not** a per-user "first run is free" switch: your runs are compared against that shared baseline from the start. This is deliberate and cheap in practice:

- With the default `nurNeueOderGeaenderte: true`, a run delivers only measures that are new or changed since the shared baseline. Once the baseline covers the year set, a run on unchanged data delivers **0** measure records, so you pay only `monatsprofil` (about $0.08 for the eight operator-months). That is the normal monitoring cost.
- With `nurNeueOderGeaenderte: false`, a run delivers the full filtered set subject to `maxMassnahmen` and bills `redispatch-massnahme` per delivered record — at most $0.50 with the default cap of 500.
- A one-off complete export of the year set (~16,900 records) therefore costs about $16.90; set `maxMassnahmen: 20000` for that. Raising `maxMassnahmen` later also works, because records outside the cap were never added to the baseline.
- The free summary always covers the full filtered set, so the country-wide picture never costs more than the delivered records.

All events are charged once per run with an aggregate count.

### Legal and source

Source: redispatch publication of the **HoBA** special unit (transmission grid) of the Bundesnetzagentur, published on `netztransparenz.de` and produced by the four German transmission system operators.

`https://www.netztransparenz.de/de-de/Systemdienstleistungen/Betriebsfuehrung/Redispatch`

Only official, publicly accessible data is processed. No credentials are bypassed, no private areas are scraped, and no personal data is collected — the source names industrial plants, exchanges and operators, never natural persons. The Bundesnetzagentur licence governs reuse of the raw data; this Actor delivers normalised aggregate and metric objects.

The Actor gives no project, investment or legal advice. The current official Bundesnetzagentur publication is always authoritative.

### FAQ

**How current is the source?**
The publication is updated day by day and grows continuously. The export returns the year-to-date set at the moment of retrieval. Two runs on the same day normally return the same set.

**Why is there a `maxMassnahmen` cap?**
Because a full year set is around 17,000 records. Without a cap, one default run would deliver and later bill for all of them. The cap keeps a first look cheap, the summary still covers everything, and `abgeschnitten` tells you when you are seeing a subset. Records outside the cap are deliberately left out of the change-detection baseline, so the next run picks them up again: you receive a large set in batches rather than losing the tail.

**Is the first run free?**
No, not for you personally — and the listing never promised otherwise once you account for how the baseline works. The change-detection baseline is shared by every run of this Actor and is seeded before publication, so your runs compare against it immediately. The good news is that the default mode makes this almost free anyway: once the baseline covers the data, a run on unchanged data delivers 0 measure records and bills only the monthly profiles.

**Why doesn't the Actor report a queue or connection capacity?**
Because Germany has no country-wide machine-readable source for it. The `Netzausbaugebiet`/`Netzengpassgebiet` disclosures under § 13 EnWG are published per distribution operator, mostly as a map or PDF, and the § 13a EnWG curtailment volumes sit behind a login. Redispatch is the only bundesweit day-current, officially quantified congestion signal.

**Why are commissioning tests excluded from the status?**
`Probefahrt` covers probe starts and function tests of reserve plants. The source states they are not triggered by a congestion, yet they were about 5.9% of 2026 energy. Leaving them in would inflate the congestion picture. They remain in `gesamtArbeitMWh` and in the per-reason breakdown, so nothing is hidden.

**Why two congestion statuses?**
The absolute monthly status answers "how much congestion happened this month". It is misleading for the current, still incomplete month. The per-day status answers "how intense is it here, right now" and stays comparable across months of different lengths. `abgedeckteKalendertage` shows the denominator.

**What is the difference between `anweisenderUENB` and `anforderndeBetreiber`?**
The operators apply the Anschluss-ÜNB principle: publication is the responsibility of the operator in whose control zone the plant sits — that is `anweisenderUENB`. `anforderndeBetreiber` lists who requested the measure, possibly several operators or foreign partners (RTE, Statnett, APG, Swissgrid, Elia, CEPS, EnDK). Multi-value entries are `&`-separated in the source and split into an array here.

**What does `Börse` in `betroffeneAnlage` mean?**
Since 2019 the source also publishes the TSOs' exchange trades. Such rows have no local plant reference — they occur for voltage-driven redispatch, where there is no local countermeasure. They count in the aggregates.

**Why does capacity or energy sometimes come back as `null`?**
Because the source leaves the value out for some rows (e.g. `n.v.`). The Actor writes `null` rather than `0` so a missing value stays distinguishable from a real zero. `minLeistungMW` and `minArbeitMWh` exclude such rows instead of letting them through.

**How does the change detection survive a growing source?**
It stores a compact snapshot of every measure ever seen, in a named key-value store shared with scheduled runs. Entries older than 400 days are pruned, because the source only ever publishes the running year — without pruning the snapshot would eventually exceed the key-value record limit.

### Häufige Fragen (deutsch)

**Wann wird die Quelle aktualisiert?**
Die Publikation ist tagesscharf und wächst laufend weiter. Der Export liefert den Bestand seit Jahresbeginn bis zum Abrufzeitpunkt. Zwei Läufe am selben Tag liefern in der Regel denselben Stand.

**Warum gibt es die Grenze `maxMassnahmen`?**
Weil der Jahresbestand rund 17.000 Datensätze umfasst. Ohne Grenze würde ein Standardlauf alle liefern und später auch alle abrechnen. Die Grenze hält den ersten Blick günstig, die Zusammenfassung deckt weiterhin alles ab, und `abgeschnitten` zeigt an, wenn nur ein Teil geliefert wurde. Datensätze außerhalb der Grenze kommen bewusst NICHT in die Änderungs-Baseline: der nächste Lauf erkennt sie wieder und liefert die nächste Charge. Große Mengen kommen also in Portionen, statt dass der Rest verloren geht.

**Ist der erste Lauf gratis?**
Nein, nicht für Sie persönlich. Die Änderungs-Baseline liegt in einem Store, den alle Läufe dieses Actors teilen, und wird schon vor der Veröffentlichung befüllt; Ihre Läufe werden also sofort damit verglichen. Mit der Standardeinstellung ist das trotzdem fast kostenlos: sobald die Baseline die Daten abdeckt, liefert ein Lauf ohne Neuigkeiten 0 Datensätze und berechnet nur die Monatsprofile.

**Warum werden Probefahrten nicht als Engpass gezählt?**
`Probefahrt` sind Probeabrufe, Funktionstests und Testfahrten inländischer Reservekraftwerke. Die Quelle stellt ausdrücklich klar, dass sie nicht durch einen Engpass ausgelöst werden — 2026 machten sie trotzdem rund 5,9 % der veröffentlichten Arbeit aus. Sie mitzuzählen würde das Engpassbild aufblähen. Sie bleiben in `gesamtArbeitMWh` und in der Aufschlüsselung nach Grund sichtbar.

**Warum zwei Congestion-Status?**
Der absolute Monatsstatus beantwortet „wie viel Engpass war in diesem Monat". Für den laufenden, noch unvollständigen Monat ist er irreführend. Der Tagesstatus beantwortet „wie intensiv ist es hier gerade" und bleibt über Monate unterschiedlicher Länge vergleichbar. `abgedeckteKalendertage` nennt den Nenner.

**Worin unterscheidet sich `anweisenderUENB` von `anforderndeBetreiber`?**
Die ÜNB wenden das Prinzip des Anschluss-ÜNB an: Für die Veröffentlichung ist der Betreiber zuständig, in dessen Regelzone die Anlage liegt — das ist `anweisenderUENB`. `anforderndeBetreiber` nennt, wer die Maßnahme angefordert hat, gegebenenfalls mehrere Betreiber oder ausländische Partner (RTE, Statnett, APG, Swissgrid, Elia, CEPS, EnDK).

**Was bedeutet `Börse` bei `betroffeneAnlage`?**
Die Quelle veröffentlicht seit 2019 auch Börsengeschäfte der deutschen ÜNB. Solche Zeilen haben keinen örtlichen Anlagenbezug, etwa bei spannungsbedingtem Redispatch, wo es keine räumliche Gegenmaßnahme gibt. Sie zählen in den Aggregaten mit.

**Wie finde ich eine Anlage mit Umlaut?**
Die Namenssuche faltet Umlaute auf die deutsche ASCII-Konvention: `Doerpen` findet `Dörpen-West`, und `dörpen` ebenfalls. Eine Suche nach `Dorpen` findet bewusst nichts — einfaches `o` und `oe` bleiben getrennt.

**Kostet ein Lauf über das ganze Jahr pauschal 16,75 $?**
Nein. Das ist der Preis für die vollständige Auslieferung von rund 16.900 Einzeldatensätzen (`$0.001` je Datensatz), also ein Einmalextrakt mit `maxMassnahmen: 20000`. Mit den Standardeinstellungen (`maxMassnahmen: 500`, nur neue/geänderte Datensätze) liegt ein Lauf bei höchstens `$0.50` plus Monatsprofile, und die kostenlose Zusammenfassung liefert trotzdem das Gesamtbild. Ein reiner Überwachungslauf ohne Neuigkeiten kostet nur die Monatsprofile (rund `$0.08`).

### Related Actors

- [nl-netcongestie-monitor](../nl-netcongestie-monitor) — same netcongestie pattern for the Netherlands (live, same portfolio author).
- [uk-netcongestie-grid-capacity-tracker](../uk-netcongestie-grid-capacity-tracker) — transmission connection queue for Great Britain (live, same portfolio author).

### Keywords

Netzengpass, Redispatch, Stromnetz, Übertragungsnetz, Übertragungsnetzbetreiber, Engpass, netztransparenz, Bundesnetzagentur, 50Hertz, Amprion, TenneT DE, TransnetBW, Regelleistung, Stromdaten, Kraftwerk, Einspeisemanagement, Gegenmaßnahmen, Probefahrt, Netzausbaugebiet, netzkonflikt, Germany electricity grid, grid congestion, redispatch, transmission system operator, curtailment, countertrade

### Changelog

#### 0.1.0

- Initial release.
- Live-verified download of the official redispatch CSV through the WebForms export endpoint, with retry, an HTML body check against error pages, and automatic discovery of the download module so a layout change cannot silently select the wrong dataset.
- Monthly aggregation per transmission operator with two documented congestion statuses (absolute and per day).
- Commissioning tests (`Probefahrt`, ~5.9% of 2026 energy) are flagged and excluded from every congestion metric while staying visible in the totals; `nurEngpassursachen` can filter them from the delivered rows.
- Filters for operator, reason, direction, energy carrier, plant (with German umlaut folding), capacity, energy and period.
- `maxMassnahmen` cap with significance ordering and transparent truncation reporting. Records outside the cap are deliberately not added to the change-detection baseline, so a later run detects and delivers them too: large sets arrive in batches instead of losing the tail.
- Change detection is keyed on the event slot (window, operator, direction, reason, plant, energy carrier) rather than on a full row ID, so a source row added to or removed from a window no longer makes a neighbouring row look new, and a corrected value is reported as changed on the same slot.
- Durable, size-bounded baseline in a named key-value store (compact slot form, pruned to 400 days, tolerant of older stored shapes), plus an escalation alert for monthly profiles.
- Free per-run summary over the full filtered set, with `abgeschnitten`, `anzahlVorAbschneidung` and `anzahlGeliefert` from a single source so the log, the summary and the billing cannot diverge.

# Actor input Schema

## `anweisenderUENB` (type: `array`):

Only measures ordered by these Übertragungsnetzbetreiber (transmission system operators): "50Hertz", "Amprion", "TenneT DE", "TransnetBW". Case is ignored. Empty = all four.

## `grundDerMassnahme` (type: `array`):

Only measures with this official reason. Valid values: "Strombedingter Redispatch" (congestion-related redispatch), "Strombedingter Redispatch kurativ", "Strom- und Spannungsbedingter RD", "Spannungsbedingter Redispatch", "Probefahrt" (commissioning test), "Strombedingter Countertrade DE-DK1", "Strombedingter Countertrade DE-DK2". Empty = all.

## `richtung` (type: `array`):

"Wirkleistungseinspeisung reduzieren" = output cut (the congestion trigger) or "Wirkleistungseinspeisung erhöhen" = the offsetting measure. Empty = both.

## `primaerenergieart` (type: `array`):

Only measures on plants of these energy carriers: "Konventionell", "Erneuerbar", "Sonstiges". Empty = all.

## `betroffeneAnlage` (type: `string`):

Free-text filter on the official plant name, e.g. "Mehrum", "Börse" or "Dörpen". Case is ignored, and German umlauts are folded to their ASCII convention: "Doerpen" finds "Dörpen-West", and "dörpen" does too. ("Dorpen" does not — plain "o" and "oe" are kept distinct.)

## `nurEngpassursachen` (type: `boolean`):

If true, drop rows whose reason is not a grid-congestion measure. In practice this removes "Probefahrt" (commissioning tests of reserve plants), which the source publishes in the same table and which made up about 5.9% of 2026 energy. The monthly congestion status and the summary already exclude these rows regardless of this setting; this field only filters the delivered individual measures.

## `minLeistungMW` (type: `number`):

Only measures whose maximum capacity is at least this many MW.

## `minArbeitMWh` (type: `number`):

Only measures whose total energy is at least this many MWh. Useful to strip routine small measures when you only want the significant congestion events.

## `maxMassnahmen` (type: `number`):

Cap on how many individual measure records are delivered per run (whole number, at least 1). Default 500; set a very high value for the complete set. Without a cap, the first run of a year set would deliver roughly 17,000 records and a later full-snapshot run would bill for all of them. With a cap the most significant measures come first (highest total energy, then highest peak capacity, then most recent). Records that fall outside the cap are NOT added to the change-detection baseline, so a following run detects them again and delivers the next batch: nothing is silently lost, you just receive the set in batches. The free summary always covers the full filtered set and reports abgeschnitten together with anzahlVorAbschneidung and anzahlGeliefert. The cap also applies to a first run, but a first run never charges.

## `zeitraum` (type: `string`):

Which measures to deliver. "aktuelles\_jahr" (default) = the year-to-date set published by the source. "letzte\_30\_tage" and "letzte\_7\_tage" = only measures that overlap the last 30 or 7 days before the run. The filtering is done in the Actor, because the source ignores the date fields of its own download form.

## `liefermonatsprofil` (type: `boolean`):

If true (default), the Actor also delivers one aggregated profile per transmission operator and calendar month. Each profile carries the total energy, the congestion-relevant energy (excluding commissioning tests), the MWh per covered calendar day, the measure and plant counts, the direction split, the main reason and two derived congestion statuses: an absolute one on the month total and a daily-rate one that stays comparable for a partial current month.

## `nurNeueOderGeaenderte` (type: `boolean`):

If true (default), only measures that are new or changed since the previous run are delivered and charged. If false, every run delivers the full filtered set (subject to maxMassnahmen), but only new/changed measures are still charged as a congestion alert. Uses a persistent baseline in a named key-value store that all runs of this Actor share — including the pre-publication validation run — so the baseline is actor-wide, not per user. Only a baseline that does not exist yet makes the entire filtered set count as new; the default true setting therefore delivers nothing when the baseline already covers the filtered set.

## Actor input object example

```json
{
  "nurEngpassursachen": false,
  "maxMassnahmen": 500,
  "zeitraum": "aktuelles_jahr",
  "liefermonatsprofil": true,
  "nurNeueOderGeaenderte": true
}
```

# Actor output Schema

## `results` (type: `string`):

All results in the default dataset.

# 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 = {
    "nurEngpassursachen": false,
    "maxMassnahmen": 500,
    "zeitraum": "aktuelles_jahr",
    "liefermonatsprofil": true,
    "nurNeueOderGeaenderte": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("codeclouds/de-netzengpass-redispatch-monitor").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 = {
    "nurEngpassursachen": False,
    "maxMassnahmen": 500,
    "zeitraum": "aktuelles_jahr",
    "liefermonatsprofil": True,
    "nurNeueOderGeaenderte": True,
}

# Run the Actor and wait for it to finish
run = client.actor("codeclouds/de-netzengpass-redispatch-monitor").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 '{
  "nurEngpassursachen": false,
  "maxMassnahmen": 500,
  "zeitraum": "aktuelles_jahr",
  "liefermonatsprofil": true,
  "nurNeueOderGeaenderte": true
}' |
apify call codeclouds/de-netzengpass-redispatch-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,codeclouds/de-netzengpass-redispatch-monitor"
        }
    }
}
```

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/rhFsbjDFaTTygBVO3/builds/7mrihjwUtbjwyzePc/openapi.json
