# NL Tweede Kamer Open Data Monitor (`codeclouds/nl-tweede-kamer-open-data-monitor`) Actor

Fetch Dutch parliament (Tweede Kamer) kamerstukken, motions, amendments and votes from the official OData Open Data API, filtered by topic keyword, date range and document type, with voting results per party in one call. From $0.003/result, PPE.

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

## Pricing

from $3.00 / 1,000 kamerstuk results

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?

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 Tweede Kamer Open Data Monitor 🇳🇱

Fetch **Dutch parliament (Tweede Kamer) open data** — kamerstukken, motions, amendments and voting
results — filtered by topic keyword, date range and document type, straight from the official OData
Open Data API.

The Tweede Kamer publishes an enormous amount of parliamentary data (tens of thousands of cases,
documents and votes) through `gegevensmagazijn.tweedekamer.nl`, a public OData v4 endpoint that requires
no API key or registration. But it's a raw, generic query interface with no topic search and OData-specific
query syntax (`$filter`, `$expand`, `$skiptoken` paging) — this actor turns it into a simple, filtered,
structured feed so you don't have to learn OData or the Kamer's own portal search.

### Who is this for?

- **Lobbyists & public affairs professionals** tracking parliamentary developments on a specific dossier
- **Policy advisors & government relations teams** monitoring motions and amendments on their topic
- **Journalists** following how a bill or motion moves through parliament, including the vote outcome
- **Legal/compliance teams** tracking legislative status (e.g. related to a law they already classify against, such as this developer's other Cluster A actors)
- **Researchers** building datasets of Dutch parliamentary activity
- **AI agents** — flat JSON output, MCP-ready, ideal for legislative-research or policy-monitoring workflows

### When should an AI agent use this?

- "Find all motions about climate policy submitted this year and how each party voted."
- "Track the legislative dossier for \[bill topic] — what documents and votes have happened?"
- "Which amendments on \[topic] were adopted (aangenomen) versus rejected (verworpen)?"
- "Show me recent parliamentary activity (motions, bills, letters) since last month."
- "Get the voting result per party for motion \[number]."
- "List all written questions (Schriftelijke vragen) about \[topic] this quarter."

### What this Actor does

- **Topic search** — free-text match against the case title and subject (`Titel`/`Onderwerp`), since the
  source API itself has no full-text search index; this actor uses OData's `contains()` filter as the
  closest structural equivalent
- **Document type filter** — restrict to motions, amendments, bills, written questions, EU proposals, etc.
- **Date range filter** — only cases last updated within a given period
- **Voting results in one call** — when enabled, each case is expanded with its linked decisions
  (`Besluit`) and, per decision, the actual per-party vote (`Stemming`) — a single `$expand` chain, no
  extra round-trips per result
- **Server-side pagination** — follows the API's own `@odata.nextLink` (skip-token based), so runs with
  hundreds of results don't hit the API's 250-per-page limit
- **Flat, structured output** — one JSON object per parliamentary case, ready for datasets, webhooks or
  MCP tools

### Input options

| Field | Description | Example |
|---|---|---|
| `zoekterm` | Free text matched against the case title and subject | `"klimaat"` |
| `documentTypes` | Filter by case type. Leave empty for all types. | `["Motie", "Amendement"]` |
| `periodeVanaf` | Only cases last updated on/after this date (`YYYY-MM-DD`) | `"2026-01-01"` |
| `periodeTot` | Only cases last updated on/before this date (`YYYY-MM-DD`) | `"2026-06-30"` |
| `includeStemmingen` | Include linked decisions and per-party voting results | `true` |
| `maxResults` | Maximum number of cases to return (1-500) | `100` |

### Output

Each result is one parliamentary case (`Zaak` — a kamerstuk, motion, amendment or other document type):

```json
{
  "zaakId": "e0976e6c-54d9-4711-8eb9-e94525974654",
  "nummer": "2026Z07976",
  "titel": "Wijziging van Boek 7 van het Burgerlijk Wetboek in verband met het invoeren van een rechtsvermoeden van arbeidsovereenkomst op basis van een uurtarief",
  "onderwerp": "Motie van de leden Patijn en Westerveld over de Dienst Toeslagen de naheffingen en boetes bij schijnzelfstandigen laten vergoeden",
  "type": "Motie",
  "status": "Vrijgegeven",
  "datum": "2026-04-15T00:00:00+02:00",
  "gewijzigdOp": "2026-07-24T12:09:40.02+02:00",
  "vergaderjaar": "2025-2026",
  "afgedaan": true,
  "kabinetsappreciatie": "Ontijdig",
  "documenten": [
    {
      "documentId": "feb59372-6898-4dc1-9f52-9e7cf6d0e699",
      "documentNummer": "2026D17989",
      "soort": "Motie",
      "titel": "Wijziging van Boek 7 van het Burgerlijk Wetboek...",
      "datum": "2026-04-15T00:00:00+02:00"
    }
  ],
  "stemuitslagen": [
    {
      "besluitId": "994e2590-c7db-46cc-9a8c-ac7be34517f4",
      "uitslag": "Stemmen - verworpen",
      "stemmingsSoort": "Hoofdelijk",
      "stemmingen": [
        { "fractie": "GroenLinks-PvdA", "stem": "Voor", "zetels": 25 },
        { "fractie": "VVD", "stem": "Tegen", "zetels": 24 }
      ]
    }
  ],
  "bronUrl": "https://www.tweedekamer.nl/zoeken?qry=2026Z07976"
}
```

- **`type`** is the source API's `Zaak.Soort` — one of ~37 possible values (Motie, Amendement, Wetgeving,
  Brief regering, Schriftelijke vragen, etc.); the `documentTypes` input filters on this field
- **`kabinetsappreciatie`** is the cabinet's response to a motion/amendment where applicable (e.g.
  "Ontraden", "Overgenomen"), `null` for case types where it doesn't apply
- **`stemuitslagen`** is only populated for decisions that actually recorded a per-party vote — many
  `Besluit` records (e.g. "Ingediend", "Aangehouden") have no `Stemming` and are excluded automatically
- **`bronUrl`** links to the Tweede Kamer's own search page for this case number. The site uses a
  different detail-page URL structure per case type, which isn't derivable from the API response without
  guessing — the search page is a verified, working link for every case type

### Use cases

**Track motions on a specific topic, with voting results:**

```json
{
  "zoekterm": "klimaat",
  "documentTypes": ["Motie"],
  "includeStemmingen": true,
  "maxResults": 100
}
```

**Follow a legislative dossier (bills + amendments) over a period, without voting detail:**

```json
{
  "zoekterm": "",
  "documentTypes": ["Wetgeving", "Initiatiefwetgeving", "Amendement"],
  "periodeVanaf": "2026-01-01",
  "includeStemmingen": false,
  "maxResults": 200
}
```

**Recent parliamentary activity across all case types:**

```json
{
  "zoekterm": "",
  "documentTypes": [],
  "periodeVanaf": "2026-08-01",
  "maxResults": 100
}
```

### Scheduling & integrations

- **Schedules** — run daily or weekly to catch new activity on a dossier (combine with `periodeVanaf`)
- **Integrations** — push results to Google Sheets, Slack, Zapier, Make, or any webhook
- **API** — fetch the dataset as JSON, CSV or Excel from your own code
- **MCP** — expose this actor as a tool for AI agents (Claude, Cursor, and other MCP clients)

### Data source & legality

This actor uses the **official Tweede Kamer Open Data API** (`gegevensmagazijn.tweedekamer.nl`, OData v4),
the same data source behind the Kamer's own open-data portal (`opendata.tweedekamer.nl`).

- ✅ No scraping — public, auth-free JSON API, explicitly published as open data for reuse
- ✅ Parliamentary cases, motions and votes concern public-role activity of politicians in their official
  capacity (how a Member of Parliament voted), not private personal data
- ⚠️ This actor is a data-monitoring tool, not legal or political advice. Always consult the Tweede
  Kamer's own site (`bronUrl`) or `officielebekendmakingen.nl` for the authoritative, legally binding
  version of any document.

### Pricing

Pay-per-event: you only pay for cases actually delivered, plus a small extra charge when a case comes
with an actual recorded vote.

| Event | Price | Description |
|---|---|---|
| `apify-actor-start` | $0.00005 | Apify default |
| `kamerstuk-result` | $0.003 | Each parliamentary case delivered to your dataset |
| `stemuitslag-result` | $0.006 | Each delivered case that includes an actual recorded vote (only with `includeStemmingen` enabled) |

### FAQ

**Why doesn't `zoekterm` search the full text of documents?**
The source API has no full-text search index. `zoekterm` matches against the case title (`Titel`) and
subject (`Onderwerp`) fields using the API's own `contains()` filter — the closest structural equivalent
available without downloading and indexing every document body yourself.

**Why is `maxResults` capped at 500?**
The API itself caps a single page at 250 results (`$top` above 250 returns an error); the actor follows
the API's own pagination link (`@odata.nextLink`) to go beyond that, but keeps a sane run-size ceiling.

**Why do some cases have an empty `stemuitslagen` array even with `includeStemmingen: true`?**
Not every parliamentary decision (`Besluit`) involves an actual vote — many are procedural (e.g.
"Ingediend"/submitted, "Aangehouden"/postponed). Only decisions where the API records per-party votes
produce a `stemuitslagen` entry.

**Does this actor cover the Eerste Kamer (Senate)?**
No — the underlying API and this actor cover the Tweede Kamer (House of Representatives) only, the
chamber where kamerstukken, motions and amendments in the input scope of this actor originate.

**What does `kabinetsappreciatie` mean?**
It's the cabinet's (government's) formal response to a motion or amendment — e.g. "Ontraden" (advised
against), "Overgenomen" (adopted by the cabinet), "Overlaten aan het oordeel van de Kamer" (left to
parliament's judgment). `null` for case types where this doesn't apply (e.g. written questions).

### Related Actors

Also by this developer — Dutch public-data actors with a similar official-open-data → structured-feed
pattern:

- **[NL Jurisprudentie Monitor per Rechtsgebied](https://apify.com/codeclouds/nl-jurisprudentie-monitor)** — same pattern applied to Dutch court rulings, for legal teams tracking case law alongside legislative developments.
- **[NL Wetgeving Sectortracker](https://apify.com/codeclouds/nl-wetgeving-sectortracker)** — tracks Dutch legislation relevant to a business sector; pairs well with tracking a bill's parliamentary progress here.

***

*Zoekwoorden: tweede kamer monitor, kamerstukken volgen, moties zoeken, stemuitslagen tweede kamer,
wetgeving volgen, parlementaire data, open data tweede kamer, dossier tracker, amendementen, politiek
monitoring, lobbyist tool.*

### Keywords

tweede kamer, kamerstukken, moties, stemmingen, wetgeving, parlement, politiek, open data, netherlands,
dutch parliament, government, legal monitoring, amendementen, dossier, lobbying, public affairs

### Changelog

#### 0.1.0 - Initial release

- Topic/period/type-filtered fetch of Tweede Kamer `Zaak` (kamerstuk/motion/amendment/etc.) records via
  the official OData v4 API
- Linked documents and per-party voting results in one `$expand` call (no N+1 queries)
- Server-side pagination via `@odata.nextLink`
- PPE pricing: `kamerstuk-result` @ $0.003, `stemuitslag-result` @ $0.006

# Actor input Schema

## `zoekterm` (type: `string`):

Vrije tekst die tegen de titel EN het onderwerp van elk kamerstuk/motie/zaak wordt gematcht (case-sensitive 'bevat'-match op de bron-API). Leeg = geen tekstfilter. Voorbeeld: "klimaat", "kinderopvang", "Wet harmonisatie".

## `documentTypes` (type: `array`):

Filter op het type parlementair stuk. Leeg = alle typen. Voorbeeld: \["Motie", "Amendement"].

## `periodeVanaf` (type: `string`):

Alleen zaken met een laatste statuswijziging (GewijzigdOp) op of na deze datum. Formaat YYYY-MM-DD. Leeg = geen ondergrens. Voorbeeld: "2026-01-01".

## `periodeTot` (type: `string`):

Alleen zaken met een laatste statuswijziging (GewijzigdOp) op of vóór deze datum. Formaat YYYY-MM-DD. Leeg = geen bovengrens.

## `includeStemmingen` (type: `boolean`):

Voeg per zaak de gekoppelde besluiten en stemuitslagen per fractie toe (1 extra $expand in dezelfde API-call, geen losse queries). Uitzetten levert alleen de kale zaak- en documentmetadata.

## `maxResults` (type: `integer`):

Bovengrens op het aantal opgeleverde zaken in deze run (1-500). Voorbeeld: 100.

## Actor input object example

```json
{
  "zoekterm": "klimaat",
  "documentTypes": [],
  "periodeVanaf": "2026-01-01",
  "periodeTot": "",
  "includeStemmingen": true,
  "maxResults": 100
}
```

# Actor output Schema

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

Results stored 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 = {
    "zoekterm": "klimaat",
    "periodeVanaf": "2026-01-01"
};

// Run the Actor and wait for it to finish
const run = await client.actor("codeclouds/nl-tweede-kamer-open-data-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 = {
    "zoekterm": "klimaat",
    "periodeVanaf": "2026-01-01",
}

# Run the Actor and wait for it to finish
run = client.actor("codeclouds/nl-tweede-kamer-open-data-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 '{
  "zoekterm": "klimaat",
  "periodeVanaf": "2026-01-01"
}' |
apify call codeclouds/nl-tweede-kamer-open-data-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,codeclouds/nl-tweede-kamer-open-data-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/cXkJa59kgHI9pOwK6/builds/Q77yFgJ3VlbmxRTL8/openapi.json
