# NL Netstoringen Monitor (Liander Power/Gas Outage Feed) (`codeclouds/nl-netstoringen-monitor`) Actor

Structured feed of electricity and gas grid outages (storingen) in the Liander network area, from the official public Liander IStoringen ArcGIS feed. Filter by postcode, energy type, and active/resolved status.

- **URL**: https://apify.com/codeclouds/nl-netstoringen-monitor.md
- **Developed by:** [Dennis](https://apify.com/codeclouds) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 storing-records

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## NL Netstoringen Monitor (Liander Power/Gas Outage Feed) ⚡🔧

Structured, queryable JSON feed of **electricity and gas grid outages (storingen)** in the Netherlands —
straight from **Liander's own public IStoringen feed**, the exact data source behind Liander's official
[storingen-en-onderhoud](https://www.liander.nl/storingen-en-onderhoud) map. No scraping, no HTML parsing:
this actor queries the underlying public ArcGIS Feature Service directly.

### Who is this for?

- **Datacenters & industrial grid users** — get notified the moment an outage in your postcode area changes status
- **Insurers** — correlate outage claims (business interruption, spoiled goods) with confirmed grid outage records
- **Facility managers & building operators** — check whether a reported power/gas issue is a known, confirmed grid outage before dispatching your own technician
- **Local news / community sites** — surface current outages for a region
- **AI agents** — structured JSON output, ideal as a tool for agentic workflows (MCP-ready)

### When should an AI agent use this?

- "Is there a known power outage at postcode 9105?"
- "List all currently active gas outages in the Liander area."
- "Has the outage near Arnhem been resolved yet?"
- "Alert me when outage 8217875 changes status."

### What this Actor does

- Queries Liander's public **IStoringen** ArcGIS Feature Service (no authentication required)
- Filters by postcode (4-digit or full 6-character), energy type (electricity/gas), and active-vs-resolved status
- Returns one structured record per outage: type, cause, affected customers (bucketed range), affected
  postcodes/streets/places, reported/estimated-resolved/actual-resolved timestamps
- Optional `trackWijzigingen` mode: compares each outage's status with the previous scheduled run (via a
  persistent key-value store), so you get a direct signal the moment an outage progresses (e.g.
  `"monteur onderweg"` → `"opgelost"`) instead of polling manually

### Coverage — important limitation

This actor covers the **Liander network area only** (large parts of Gelderland, Flevoland, Friesland,
Noord-Holland, and parts of Zuid-Holland/Utrecht — see [Liander's own service area map](https://www.liander.nl)
for the exact boundary). Other Dutch regional grid operators (Enexis, Stedin, Coteq, Westland Infra, Rendo) are
**not** included in this feed — no equivalent public, no-auth outage API was found for them at the time of
building this actor. If your area of interest falls outside Liander's network, this actor will simply return no
results for that postcode.

### Input

| Field | Type | Description |
|---|---|---|
| `postcodes` | array of strings | 4-digit or full postcodes to filter on, e.g. `"9105"` or `"9105AZ"`. Empty = all outages (bounded by `maxResultaten`). |
| `energiesoort` | string | `"alle"` (default), `"Elektriciteit"`, or `"Gas"` |
| `alleenActueel` | boolean | Only active/unresolved outages (default `true`). Set `false` to also include recently resolved ones. |
| `maxResultaten` | integer | Upper bound on outages fetched per run, 1-5000 (default `500`) |
| `trackWijzigingen` | boolean | Compare each outage's status with the previous run (default `false`) |

### Output

One result per outage:

```json
{
  "storingNummer": 8217875,
  "energiesoort": "Elektriciteit",
  "type": "S",
  "typeLabel": "Spontane storing",
  "status": "monteur onderweg",
  "opgelost": false,
  "oorzaak": "Spontane storing",
  "component": "Nog niet bekend",
  "getroffenKlanten": "< 25",
  "getroffenKlantenBovengrens": 25,
  "postcodes": ["9105AZ"],
  "straten": ["Klaarkampsterwei"],
  "plaatsen": ["RINSUMAGEAST"],
  "datumGemeld": "2026-07-15T11:34:00.000Z",
  "datumSchattingHersteld": "2026-07-15T14:45:00.000Z",
  "datumEind": null,
  "duurMinuten": 187,
  "datumChange": "2026-07-15T11:44:25.000Z",
  "bron": "Liander — IStoringen (services1.arcgis.com)",
  "statusGewijzigdSindsVorigeRun": null,
  "vorigeStatus": null,
  "nieuweStoring": null
}
```

- **`type`/`typeLabel`**: `S` = spontaneous outage, `P` = planned works, `storing` = unspecified type (older
  records in the feed predate the S/P distinction)
- **`status`**: free-text status as published by Liander (e.g. `"gemeld"`, `"monteur onderweg"`,
  `"monteur ter plaatse"`, `"opgelost"`) — `opgelost` is also exposed as the boolean `opgelost` field
- **`getroffenKlanten`**: a bucketed range (e.g. `"< 25"`, `"< 100"`), never an exact count — no individual
  customer data is involved
- **`getroffenKlantenBovengrens`**: the number extracted from `getroffenKlanten` (e.g. `25` for `"< 25"`), for
  sorting/filtering by impact size. `null` if no number could be extracted — never a guessed lower bound
- **`datumEind`** is `null` while the outage is still ongoing
- **`duurMinuten`**: `datumEind - datumGemeld` for resolved outages, or time-until-now for still-active ones.
  `null` if `datumGemeld` is missing

#### Regional summary (free extra dataset record)

Whenever there's at least one outage in the run, one extra record with `"recordType":
"REGIONALE_SAMENVATTING"` is pushed to the dataset (no extra charge) — the total outage count, a breakdown per
`energiesoort` (electricity/gas), and a count per place (a multi-place outage counts in each place),
sorted descending:

```json
{
  "recordType": "REGIONALE_SAMENVATTING",
  "peildatum": "2026-07-16T09:00:00.000Z",
  "totaalStoringen": 14,
  "perEnergiesoort": { "Elektriciteit": 11, "Gas": 3 },
  "perPlaats": [{ "plaats": "RINSUMAGEAST", "aantal": 3 }, { "plaats": "AMSTERDAM", "aantal": 2 }]
}
```

#### Change tracking (`trackWijzigingen`, optional, separately charged)

With `trackWijzigingen: true`, every outage is compared with the previous run by its outage number:

- `statusGewijzigdSindsVorigeRun`: `null` (no previous run for this outage, e.g. it's new), or `true`/`false`
- `vorigeStatus`: the previous run's status, when available
- `nieuweStoring`: `true` if this outage number wasn't seen in the previous run's snapshot — i.e. it's a
  brand-new outage, not just a status change on an already-known one. Always `false` on the very first run
  with `trackWijzigingen` enabled (there's no previous snapshot yet to compare against).

Meant for scheduled/recurring runs — you get a direct signal the moment an outage's status changes, or a new
outage appears, instead of manually re-checking.

### Use cases

**Monitor all active outages in a set of postcode areas:**

```json
{
  "postcodes": ["9105", "6815", "1057"],
  "alleenActueel": true
}
```

**Get notified when outages progress, on a scheduled run:**

```json
{
  "postcodes": ["9105"],
  "alleenActueel": false,
  "trackWijzigingen": true
}
```

**Fetch all currently active gas outages nationwide (within the Liander area):**

```json
{
  "energiesoort": "Gas",
  "alleenActueel": true,
  "maxResultaten": 2000
}
```

### Pricing

This Actor uses Apify's Pay-Per-Event (PPE) pricing model.

- **Actor Start:** $0.00005 (Apify default)
- **storing-record:** $0.005 per outage record returned
- **storing-statuswijziging:** $0.015 extra, only with `trackWijzigingen` enabled and only when a status change
  vs. the previous run is actually detected
- **nieuwe-storing-signaal:** $0.015 extra, only with `trackWijzigingen` enabled and only for an outage number
  that wasn't in the previous run's snapshot (never charged on the very first `trackWijzigingen` run)

### Legal

- Data source: **Liander's public IStoringen ArcGIS Feature Service**, the same backend that powers Liander's own
  public [storingen-en-onderhoud](https://www.liander.nl/storingen-en-onderhoud) map. Public, no authentication
  required.
- This is grid infrastructure/outage data at the postcode/street level, not personal data — affected-customer
  counts are bucketed ranges (e.g. `"< 25"`), never individual identities. No GDPR concerns.
- Covers the Liander network area only — see "Coverage" above.
- Not a substitute for Liander's own official outage notifications for safety-critical decisions.

### FAQ

**Q: How current is the data?**
A: The feed is Liander's live operational outage system — the same one shown on their own public map. Reported
times reflect when Liander logged the outage.

**Q: Does this include Enexis/Stedin/other grid operators?**
A: No — see "Coverage" above. Only Liander's network area is covered in this version.

**Q: What does `type: "storing"` mean (instead of `S`/`P`)?**
A: Older records in Liander's feed predate the spontaneous/planned (`S`/`P`) distinction and are simply labeled
`"storing"` — this is a source-data characteristic, not a mapping error.

***

*Zoekwoorden: stroomstoring, gasstoring, netstoring, storing liander, energieonderbrekingen, stroomuitval
postcode, storingen kaart, netbeheerder storing.*

### Keywords

stroomstoring, power outage, gas outage, liander, storing, netbeheerder, netherlands, energieonderbrekingen,
grid outage, electricity outage

### Changelog

#### 0.2.1

- Added `getroffenKlantenBovengrens`: the number extracted from the bucketed `getroffenKlanten` text (e.g.
  `25` for `"< 25"`), for sorting/filtering by impact size. No pricing change.
- Added `duurMinuten`: outage duration in minutes (resolved outages use `datumEind - datumGemeld`, ongoing
  ones use time-until-now). No pricing change.
- Added a free `REGIONALE_SAMENVATTING` dataset record per run (totals per energiesoort/place). No pricing
  change.

#### 0.2.0

- Added `nieuweStoring` field and a new charged event `nieuwe-storing-signaal` ($0.015, same tier as
  `storing-statuswijziging`): flags an outage number that wasn't in the previous run's snapshot, i.e. a
  brand-new outage rather than a status change on an already-known one. Only with `trackWijzigingen`
  enabled; never fires on the very first `trackWijzigingen` run. Price confirmed by the user (2026-07-16,
  see PRIJSBESLISSINGEN.md).

#### 0.1.0

- Initial release: postcode/energy-type/status-filtered feed of Liander grid outages, with optional
  cross-run status-change tracking.

# Actor input Schema

## `postcodes` (type: `array`):

4-digit (or full 6-character) Dutch postcodes to filter on, e.g. "9105" or "9105AZ". Leave empty to fetch all outages (bounded by maxResultaten).

## `energiesoort` (type: `string`):

Filter by energy type.

## `alleenActueel` (type: `boolean`):

Only include outages that are not yet resolved (status "opgelost"). Turn off to also include recently resolved outages.

## `maxResultaten` (type: `integer`):

Upper bound on the number of outages fetched/pushed per run (cost control when postcodes is empty or broad).

## `trackWijzigingen` (type: `boolean`):

Compare each outage's status (keyed on its outage number) with the previous run, via a persistent key-value store. Ideal for scheduled runs to get notified the moment an outage changes status (e.g. "monteur onderweg" -> "opgelost"). Charges the extra storing-statuswijziging event when a change is found.

## Actor input object example

```json
{
  "postcodes": [],
  "energiesoort": "alle",
  "alleenActueel": true,
  "maxResultaten": 500,
  "trackWijzigingen": false
}
```

# 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 = {
    "postcodes": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("codeclouds/nl-netstoringen-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 = { "postcodes": [] }

# Run the Actor and wait for it to finish
run = client.actor("codeclouds/nl-netstoringen-monitor").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "postcodes": []
}' |
apify call codeclouds/nl-netstoringen-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=codeclouds/nl-netstoringen-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/lOGc562fMwIJV8FzV/builds/XXZJSgmY6Do5tRobE/openapi.json
