# EU & Dutch News Monitor – RSS Keyword Alerts (`benelux-signals/eu-news-rss-monitor`) Actor

Monitor 90 verified Dutch, Belgian, German, French and EU news & official RSS feeds (NOS, NU.nl, NRC, VRT, Tagesschau, Le Monde, POLITICO Europe, European Commission…) or your own feeds for keywords. Only new articles across runs. Title, link, date, snippet. $0.002/article.

- **URL**: https://apify.com/benelux-signals/eu-news-rss-monitor.md
- **Developed by:** [Benelux Signals](https://apify.com/benelux-signals) (community)
- **Categories:** News, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 news articles

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

## EU & Dutch News Monitor: RSS Keyword Alerts for NL, BE and EU News

**Track keywords across 84 verified Dutch, Belgian, German, French and EU news and official RSS feeds, or your own feeds, for $0.002 per matching article.** Each match gives you the headline, a clean link to the article, the source, the publication date, the publisher's teaser snippet, the keywords that matched, and the language and country. Turn on **only new articles** and schedule it, and you get a simple media monitoring feed for brands, competitors, policy files or topics such as *AI Act*, *stikstof*, *ECB*, *Tata Steel* or *Schiphol*. Export to CSV, Excel or JSON, or pull it through the API into Slack, Sheets, Make, Zapier or n8n.

Failed feeds and invalid input lines show up in the dataset as clearly marked error rows and are **never charged**. Every field has a default, so a run with only `keywords` works fine.

### Who is this news monitor for?

- **PR, communications and public-affairs teams** who need brand mentions, competitor news or an issue (stikstof, netcongestie, AI Act, Omnibus) followed in Dutch and Belgian media and EU institutions.
- **Policy and EU-affairs professionals** who track press releases from the European Commission, Parliament, Council, ECB, EBA and EDPB next to POLITICO Europe, EUobserver and Euronews.
- **Analysts, investors and journalists** who want a daily keyword digest from NOS, NU.nl, NRC, FD, De Tijd, Handelsblatt, Le Monde and others, without reading 80 sites.
- **Developers and AI agents** who need a cheap, structured news feed for the Benelux and EU. The data is metadata only, with stable `articleId`s for deduplication.

### What does EU & Dutch News Monitor do?

1. Reads the curated list of **public RSS/Atom feeds** for the countries you choose, plus any feeds you add yourself. You can optionally also search the free **GDELT** news index.
2. Keeps articles published in the last `publishedWithinHours` hours (default 48) whose **headline or teaser** match your keywords and do not contain any of your exclude terms.
3. Removes duplicates (the same URL, or the same headline from two feeds), strips tracking parameters such as `utm_*` and `xtor` from links, and sorts the results newest first.
4. With `onlyNew`, it remembers what it delivered in a named key-value store and skips those articles next time. **You only pay for new articles.**
5. Writes a `RUN_SUMMARY` with counts per keyword and per source, any feeds that failed, and whether `maxResults` or your charge limit was reached.

#### Keyword matching

| You type | Matches | Doesn't match |
|---|---|---|
| `stikstof` | stikstof, Stikstofwet, stikstofuitstoot (words of 4+ letters also match compounds) | ministikstof |
| `AI`, `ECB`, `KLM` | an acronym in CAPITALS: the exact word, case-sensitive (AI-regels, de ECB) | mail, French *j'ai*, *Ai* |
| `"AI Act"` | the exact phrase | AI Actions |
| `energie*` | anything starting with energie | |
| `Tata Steel` | the words in sequence | |

Matching ignores accents (`Mélenchon` = `melenchon`) and, except for capital acronyms, ignores case. Use `matchMode: "all"` to require every keyword, and `excludeKeywords` to drop noise such as sport or advertorials.

#### Built-in sources (84 feeds, checked against robots.txt)

- **Netherlands (37):** NOS (news, domestic, foreign, politics, economy, tech, Nieuwsuur, sport), NU.nl (6 sections plus sport), RTL Nieuws, NRC, de Volkskrant, Trouw, Het Parool, AD, De Telegraaf, Nederlands Dagblad, Het Financieele Dagblad, Tweakers, Security.NL, Computable, Bright, and the regional broadcasters Omroep West, Rijnmond, RTV Utrecht, Omroep Gelderland, Omroep Brabant, RTV Noord and L1 Limburg.
- **Belgium (9):** VRT NWS, HLN, De Morgen, De Tijd, RTBF Info, La Libre Belgique, DH, 7sur7, L'Echo.
- **Germany (12):** tagesschau.de, DER SPIEGEL, ZEIT ONLINE, FAZ.NET, Süddeutsche Zeitung, ZDFheute, Deutsche Welle (DE and EN), Handelsblatt, heise online, t3n, Bundesregierung.
- **France (8):** Le Monde, Le Figaro, Libération, franceinfo, 20 Minutes, BFMTV, France 24 (FR and EN Europe).
- **EU and pan-European (18):** European Commission press corner (EN and NL), European Parliament, Council of the EU, ECB, EBA, EDPB, EUR-Lex Official Journal, POLITICO Europe (main, tech, energy), EUobserver, Euronews (main, business, next), BBC News Europe and Business, Sifted.

You can narrow the selection with `countries`, `languages` and `categories` (general, politics, business, tech, regional, official, sport). Sport is off by default. This Actor is independent and is not affiliated with or endorsed by any of these publishers.

### How to use it

1. Enter your **keywords**, and optionally exclude terms.
2. Pick the **countries** (default NL, BE and EU) and, if you like, languages and categories.
3. Add **your own RSS feeds** if needed, for example a trade journal, a competitor's newsroom, or a Google Alerts RSS feed you created yourself.
4. Set **published within** (hours) and **max articles**.
5. For a recurring monitor, switch on **only new articles**, give it a **monitor name**, save it as a task and schedule it (every few hours or daily).

#### Input example

```json
{
  "keywords": ["\"AI Act\"", "stikstof", "ECB"],
  "excludeKeywords": ["voetbal"],
  "countries": ["NL", "BE", "EU"],
  "customFeeds": [],
  "publishedWithinHours": 48,
  "onlyNew": true,
  "monitorName": "policy-watch",
  "maxResults": 100
}
```

| Field | Default | What it does |
|---|---|---|
| `keywords` | AI Act, stikstof, ECB | Words or phrases to find. Leave empty to get every article from the selected feeds. |
| `matchMode` | `any` | `any` (OR) or `all` (AND). |
| `excludeKeywords` | empty | Drop articles containing any of these. |
| `searchIn` | `titleAndSnippet` | `titleAndSnippet` or `title` only. |
| `countries` | NL, BE, EU | Which built-in feeds to read: NL, BE, DE, FR, EU. |
| `languages` | all | Only built-in feeds in nl, en, de, fr. |
| `categories` | all except sport | general, politics, business, tech, regional, official, sport. |
| `useBuiltInFeeds` | true | Switch off to read only your own feeds. |
| `customFeeds` | empty | Your RSS, RDF or Atom feed URLs (up to 200). |
| `searchGdelt` | false | Also search the GDELT index (beta; headline and link only). |
| `publishedWithinHours` | 48 | Time window, 0–2160 hours. 0 means everything still in the feeds. |
| `onlyNew` | false | Skip articles delivered in earlier runs. |
| `monitorName` | empty | Name of the only-new memory. Without it, the memory is tied to your settings. |
| `maxResults` | 100 | Maximum articles per run (1–10,000), and your cost cap. |
| `maxPerSource` | 0 | Optional cap per news site. 0 means no cap. |

#### Output example

One row per matching article, in the **Articles** table view:

```json
{
  "status": "ok",
  "title": "Kabinet presenteert nieuw stikstofplan voor boeren",
  "url": "https://nos.nl/artikel/2589123-kabinet-presenteert-nieuw-stikstofplan-voor-boeren",
  "source": "NOS Politiek",
  "sourceDomain": "nos.nl",
  "publishedAt": "2026-10-25T08:16:00.000Z",
  "snippet": "Het kabinet wil de uitstoot in 2030 halveren en trekt daarvoor extra geld uit…",
  "matchedKeywords": ["stikstof"],
  "matchedIn": "title+snippet",
  "language": "nl",
  "country": "NL",
  "category": "politics",
  "author": null,
  "imageUrl": "https://cdn.nos.nl/image/2026/10/25/1234567/1024x576a.jpg",
  "origin": "built-in",
  "feedUrl": "https://feeds.nos.nl/nosnieuwspolitiek",
  "articleId": "67a8eb3ea3e662b95add",
  "isNew": true,
  "fetchedAt": "2026-10-25T09:30:00.000Z",
  "charged": true,
  "error": null
}
```

A feed that could not be read becomes a free row with `status: "error"`, the feed URL and the reason (HTTP status, timeout, not a feed, or disallowed by robots.txt). The `RUN_SUMMARY` record in the key-value store contains `articles`, `charged`, `matchedBeforeLimits`, `duplicatesRemoved`, `alreadySeenSkipped`, `chargeLimitReached`, `maxResultsReached`, `feedsFailed`, `byKeyword`, `bySource` and a status for every feed.

### Speed

All feeds are read in parallel (8 at a time, with a polite delay per host). A default run with about 60 feeds takes **10–20 seconds** at 256 MB. GDELT adds up to about a minute when it is enabled.

### Pricing

This Actor uses **pay per event**: **$0.002 per matching article**, with platform usage included. Failed feeds, error rows, duplicates and articles skipped as already seen are free. Higher Apify plans pay less, down to $0.001.

| Event | Free plan | Bronze | Silver | Gold | Platinum / Diamond |
|---|---|---|---|---|---|
| News article | $0.002 | $0.0018 | $0.0015 | $0.0012 | $0.001 |
| Actor start (per run) | $0.005 | | | | |

#### Cost table (Free plan prices)

| Use | Articles | Cost |
|---|---|---|
| Quick keyword check | 10 | $0.025 |
| Default run | up to 100 | up to $0.205 |
| Big daily digest | 1,000 | $2.005 |
| Only-new monitor every 4 hours, about 10 new articles per run | 60/day | about $0.15/day (≈ $4.50/month) |

**Bill-shock protection:**

- A run never returns more than `maxResults` articles (default 100, so at most **$0.205 per run** on the Free plan).
- It stops cleanly at your *maximum cost per run*. `RUN_SUMMARY.chargeLimitReached` is then `true`, and the number of articles that did not fit is reported.
- With `onlyNew`, articles you already received are skipped **before** charging.
- Error rows, failed feeds and invalid input are never charged. Invalid input fails the run with a clear message instead of returning an empty result.

### FAQ

#### Does it scrape full articles?

No. It only uses what publishers put in their public RSS feeds: headline, link, date, teaser and image. It never opens article pages and never goes behind paywalls. Click through to read the article on the publisher's site.

#### Why don't I see older articles?

RSS feeds contain only the latest items (usually 10–150 per feed). For a continuous record, schedule the Actor with `onlyNew` every few hours, or enable GDELT for a wider search.

#### Does it include Google News?

No. Google News and Bing News RSS terms only allow personal, non-commercial use, and Google's robots.txt disallows automated access to its RSS search. That's why we read publishers' own feeds and GDELT, an open news index. You can add any RSS feed you are allowed to use, including a Google Alerts feed you created for yourself.

#### What is GDELT and why is it in beta?

GDELT is a free, open index of news from thousands of outlets worldwide. It finds articles beyond the built-in feeds, but only gives headline, link and date, not a snippet. It also throttles busy shared IP addresses. When GDELT is slow or unavailable, the run continues with the RSS feeds and says so in the status message and `RUN_SUMMARY.gdelt`.

#### Why was a feed marked as an error?

The `error` field says why: the site was down, the URL is not an RSS/Atom feed, or the site's robots.txt does not allow bots to fetch it. These rows are free. A run only fails when **no** source could be read at all.

#### Can I get Slack, email or Sheets alerts?

Yes. Save a task with `onlyNew: true`, schedule it, and connect an Apify integration (Slack, email, Google Sheets, Make, Zapier, n8n, webhooks) to new dataset items. It also works through the Apify API and MCP server for AI agents.

### Is it legal?

The Actor reads public RSS feeds, which publishers provide so that their headlines can be syndicated. It identifies itself with its own user agent (`NewsRssMonitor`), respects robots.txt, keeps request rates low, and returns only metadata and the publisher's own teaser with a link back to the source. It does not copy full articles. Headlines and snippets remain the publishers' copyright, so use the results for monitoring and linking, not for republishing.

### Related actors by benelux-signals

- **EU Tender Radar**: new public tenders from TED and TenderNed.
- **Benelux Trade Fair Exhibitors**: exhibitor lists from Dutch and Belgian trade fairs.
- **Bulk PageSpeed & SEO Audit**: Core Web Vitals, Lighthouse and on-page SEO for a list of URLs.

# Actor input Schema

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

Words or phrases to look for in the headline and teaser, one per line. Case- and accent-insensitive. Words of 4+ letters also match compounds (stikstof → stikstofuitstoot); short acronyms typed in CAPITALS (AI, ECB, KLM) are case-sensitive whole words, so French "ai" never matches "AI". Use "quotes" for an exact phrase and a trailing \* for a prefix (energie\*). Leave empty to get every article from the selected feeds.

## `matchMode` (type: `string`):

Whether an article needs one of the keywords or all of them.

## `excludeKeywords` (type: `array`):

Drop articles whose headline or teaser contains any of these (same matching rules), e.g. voetbal, horoscoop, advertorial.

## `searchIn` (type: `string`):

Where keywords must appear. Full article texts are never fetched.

## `countries` (type: `array`):

Which part of the curated feed list to read: 37 Dutch, 9 Belgian, 12 German, 8 French and 18 EU / pan-European feeds. Also filters GDELT results to outlets from these countries (EU alone = English-language coverage).

## `languages` (type: `array`):

Only read built-in feeds (and GDELT results) in these languages. Empty = all languages of the selected countries.

## `categories` (type: `array`):

Feed categories to include. Sport is off by default.

## `useBuiltInFeeds` (type: `boolean`):

Read the curated list of verified public feeds. Switch off to read only your own feeds.

## `customFeeds` (type: `array`):

Extra RSS, RSS 1.0 (RDF) or Atom feed URLs, e.g. a trade publication, a company newsroom or a Google Alerts RSS feed you created yourself. robots.txt is respected. A failing feed gives an error row (never charged).

## `searchGdelt` (type: `boolean`):

Additionally search the free GDELT news index (thousands of outlets worldwide) for your keywords, filtered to the selected countries/languages. Headline + link + date only (no snippet). GDELT throttles shared IPs, so this source can be slow or temporarily unavailable – the run then continues with the RSS feeds and says so.

## `publishedWithinHours` (type: `integer`):

Only articles published in the last N hours. 0 = everything still in the feeds (most feeds hold the latest 10–150 items). Undated items are skipped when a window is set.

## `onlyNew` (type: `boolean`):

Remember delivered article IDs in a named key-value store and skip them next time – perfect for a schedule. You only pay for new articles.

## `monitorName` (type: `string`):

Optional name for this monitor's memory, e.g. "brand-acme". Without a name, the memory is tied to the exact keyword/source settings, so changing them starts fresh.

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

Maximum articles returned (newest first). Also your cost cap: max cost = Max articles × the per-article price + $0.005 start.

## `maxPerSource` (type: `integer`):

Optional cap per news site so one outlet can't dominate. 0 = no cap.

## Actor input object example

```json
{
  "keywords": [
    "\"AI Act\"",
    "stikstof",
    "Tata Steel"
  ],
  "matchMode": "any",
  "excludeKeywords": [
    "voetbal",
    "sponsored"
  ],
  "searchIn": "titleAndSnippet",
  "countries": [
    "NL",
    "BE",
    "EU"
  ],
  "languages": [],
  "categories": [
    "general",
    "politics",
    "business",
    "tech",
    "regional",
    "official"
  ],
  "useBuiltInFeeds": true,
  "customFeeds": [
    "https://www.dutchnews.nl/feed/"
  ],
  "searchGdelt": false,
  "publishedWithinHours": 48,
  "onlyNew": false,
  "monitorName": "brand-acme",
  "maxResults": 100,
  "maxPerSource": 0
}
```

# Actor output Schema

## `overview` (type: `string`):

Matching articles in the 'overview' table view (date, headline, link, source, keywords, snippet).

## `runSummary` (type: `string`):

JSON summary (articles, charged, duplicates, already-seen skipped, per-feed status, failed feeds, per-keyword and per-source counts, charge-limit and maxResults flags) stored under RUN_SUMMARY.

# 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 = {
    "keywords": [
        "AI Act",
        "stikstof",
        "ECB"
    ],
    "countries": [
        "NL",
        "BE",
        "EU"
    ],
    "publishedWithinHours": 48,
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("benelux-signals/eu-news-rss-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 = {
    "keywords": [
        "AI Act",
        "stikstof",
        "ECB",
    ],
    "countries": [
        "NL",
        "BE",
        "EU",
    ],
    "publishedWithinHours": 48,
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("benelux-signals/eu-news-rss-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 '{
  "keywords": [
    "AI Act",
    "stikstof",
    "ECB"
  ],
  "countries": [
    "NL",
    "BE",
    "EU"
  ],
  "publishedWithinHours": 48,
  "maxResults": 100
}' |
apify call benelux-signals/eu-news-rss-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,benelux-signals/eu-news-rss-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/7J5RKboOFjzF9fkch/builds/wfPgpJ1rBKpE0UAtt/openapi.json
