# German News Monitor — Tagesschau, Spiegel, FAZ, Zeit & more (`nerdrx/german-news-monitor`) Actor

Keyword-monitor 10 major German news outlets in one run (Tagesschau API + RSS of Spiegel, Zeit, FAZ, SZ, Welt, heise, Handelsblatt, n-tv, t-online). Umlaut-insensitive keyword filter, cross-run dedupe, optional full article text. Pay per article.

- **URL**: https://apify.com/nerdrx/german-news-monitor.md
- **Developed by:** [Nerdrx](https://apify.com/nerdrx) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 articles

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

## German News Monitor

Unofficial keyword monitor across ~10 major German news outlets. **Not affiliated with, endorsed by, or officially connected to Tagesschau/ARD, Der Spiegel, Die Zeit, FAZ, Süddeutsche Zeitung, Welt/Axel Springer, heise, Handelsblatt, n-tv, or t-online.** It reads each outlet's own public RSS feed (or, for Tagesschau, its public JSON API) — the same data anyone gets by subscribing to that feed in a reader.

### What is German News Monitor?

A single Apify actor run that pulls the latest headlines from Tagesschau, Der Spiegel, Die Zeit, FAZ, Süddeutsche Zeitung, Welt, heise online, Handelsblatt, n-tv, and t-online, filters them by your keywords, and hands you back a clean, deduplicated JSON dataset — instead of you writing and maintaining ten separate RSS scrapers.

### What can German News Monitor do?

- Fetch current headlines from all 10 outlets (or a subset you pick) in one run.
- Filter by keywords: OR-matched, case-insensitive, and **umlaut/ß-insensitive** — a keyword like `Buergermeister` also matches `Bürgermeister`, and `strasse` matches `Straße`.
- Exclude items matching a separate exclude-keyword list.
- Optionally fetch each matched article's page and extract its main text (JSON-LD `articleBody` first, falling back to an `<article>`/`<p>` heuristic), flagging `truncated: true` when a paywall is detected.
- Restrict to items published within a configurable recency window.
- Deduplicate across runs, so a scheduled run only returns what's new.
- Continue gracefully if one outlet's feed is temporarily down — you still get results from the rest, with a clear log entry for the failure.

### What data does it extract?

| Field | Type | Description |
|---|---|---|
| `id` | string | Deterministic hash of the article URL. |
| `outlet` | string | Outlet slug, e.g. `spiegel`. |
| `outletName` | string | Human-readable outlet name, e.g. `Der Spiegel`. |
| `title` | string | Article headline. |
| `summary` | string | null | Short summary/teaser from the feed. |
| `text` | string | null | Full article text — only populated when `fullText` input is enabled. |
| `author` | string | null | Author name, when the source provides one. |
| `publishedAt` | string | null | ISO-8601 publish date. |
| `url` | string | Canonical article URL. |
| `imageUrl` | string | null | Teaser/hero image URL. |
| `categories` | string\[] | Section/tag names from the source. |
| `matchedKeywords` | string\[] | Which of your input keywords matched this item. |
| `language` | string | Always `"de"`. |
| `sourceUrl` | string | Same as `url` (kept for pipeline compatibility). |
| `scrapedAt` | string | ISO-8601 timestamp this item was scraped. |
| `truncated` | boolean | `true` when full text was requested but is incomplete (e.g. paywall). |

### Outlets and sources

| Outlet | Slug | Source |
|---|---|---|
| Tagesschau | `tagesschau` | Public JSON API: `tagesschau.de/api2u/news/` + `.../search/` |
| Der Spiegel | `spiegel` | RSS: `spiegel.de/schlagzeilen/index.rss` |
| Die Zeit | `zeit` | RSS: `newsfeed.zeit.de/index` |
| FAZ | `faz` | RSS: `faz.net/rss/aktuell/` |
| Süddeutsche Zeitung | `sz` | RSS: `rss.sueddeutsche.de/rss/Topthemen` |
| Welt | `welt` | RSS: `welt.de/feeds/latest.rss` |
| heise online | `heise` | Atom: `heise.de/rss/heise-atom.xml` |
| Handelsblatt | `handelsblatt` | RSS: `handelsblatt.com/contentexport/feed/schlagzeilen` |
| n-tv | `ntv` | RSS: `n-tv.de/rss` |
| t-online | `t-online` | RSS: `t-online.de/nachrichten/feed.rss` |

**These are the outlets' own public feeds**, not a paid data license. Feed availability and structure can change without notice — see `NOTES.md` for which of these were live-verified versus taken from the outlet's documented feed URL. If a feed 404s or an outlet changes its markup, that outlet is skipped for that run (logged as an error) while the rest of the run continues.

### How to use

1. (Optional) Add `keywords` — e.g. `["Bundestag", "Inflation", "Klimapolitik"]`. Leave empty to get everything.
2. (Optional) Pick which `outlets` to include — all 10 by default.
3. (Optional) Enable `fullText` if you need full article bodies, not just headlines/summaries.
4. Run the actor. Results land in the run's default dataset.
5. Schedule it (e.g. hourly) with `dedupe: true` (default) to get a running feed of only new matches.

### How much does it cost?

This actor uses the **Pay-per-event** pricing model — no platform usage charges beyond the events below.

| Event | Name | Price | When it's charged |
|---|---|---|---|
| Article delivered | `article` | $0.001 | Once per item pushed to the dataset (after keyword/recency/dedupe filtering). |
| Full-text extracted | `article-fulltext` | $0.002 | Additionally, only when `fullText` was enabled **and** extraction returned text for that item. |

Example: a run with `fullText: true` that returns 100 matched articles, of which 90 yield extractable text, costs `100 × $0.001 + 90 × $0.002 = $0.28`. The actor stops pushing further items once your Apify event-charge limit for a run is reached (`eventChargeLimitReached`), so a run never overspends past your configured cap.

### Input

See `.actor/input_schema.json` for the full schema. Key fields:

```json
{
  "keywords": ["Klimapolitik", "Bundestag"],
  "excludeKeywords": ["Werbung"],
  "outlets": ["tagesschau", "spiegel", "zeit", "faz", "sz", "welt", "heise", "handelsblatt", "ntv", "t-online"],
  "fullText": false,
  "maxItems": 100,
  "maxPerOutlet": 50,
  "publishedWithinHours": 48,
  "dedupe": true
}
```

An empty input (`{}`) is valid and runs successfully — it uses every default above.

### Output (sample)

```json
{
  "id": "3f2a9c8e1b7d4a56f0e9c1b2",
  "outlet": "spiegel",
  "outletName": "Der Spiegel",
  "title": "Bürgermeister kündigt neue Steuererhöhung an",
  "summary": "Der Bürgermeister der Stadt hat heute eine Steuererhöhung angekündigt.",
  "text": null,
  "author": "Anna Beispiel",
  "publishedAt": "2026-09-02T06:15:00.000Z",
  "url": "https://www.spiegel.de/politik/beispiel-artikel",
  "imageUrl": "https://www.spiegel.de/img/beispiel.jpg",
  "categories": ["Politik", "Lokales"],
  "matchedKeywords": ["Steuer"],
  "language": "de",
  "sourceUrl": "https://www.spiegel.de/politik/beispiel-artikel",
  "scrapedAt": "2026-09-02T09:00:12.345Z",
  "truncated": false
}
```

### FAQ & support

**Is this affiliated with any of the news outlets?**
No. This is an independent, unofficial tool that reads each outlet's own publicly available RSS feed or, for Tagesschau, its public JSON API — the same data available to any RSS reader or app.

**Why did an outlet return 0 items in my run?**
Outlets occasionally change their feed URL or format. Check the run log — a failed outlet is logged with an error and the run continues with the rest. Open an issue if a feed listed above consistently fails.

**Does `fullText` bypass paywalls?**
No. It extracts whatever text is publicly present in the page HTML (via JSON-LD `articleBody` or an `<article>` heuristic). Paywalled articles typically expose only a teaser; those are returned with `truncated: true` rather than being faked or completed.

**Why umlaut-insensitive keyword matching?**
German text on the web inconsistently uses `ü/ö/ä/ß` versus their `ue/oe/ae/ss` transliterations (headline styling, ASCII-only CMSes, etc.). Matching folds both forms so you don't have to enter every keyword twice.

**How do I get only new articles on a schedule?**
Leave `dedupe: true` (default) and schedule the actor (e.g. hourly). Each run remembers previously-seen article IDs in its key-value store and only returns items not seen before.

**I found a bug or a broken feed URL.**
Please open an issue in the actor's source repository (`nerdrx/apify-actors`) with the outlet, the run log, and, if possible, the current feed URL from the outlet's own site.

# Actor input Schema

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

Only keep items whose title or summary contains at least one of these keywords (case-insensitive, umlaut/ß-insensitive OR match). Leave empty to keep everything.

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

Drop any item whose title or summary contains one of these keywords, even if it matched a keyword above.

## `outlets` (type: `array`):

Which outlets to include in this run.

## `fullText` (type: `boolean`):

When enabled, fetches each matched article's page and extracts its main text (charged extra per successfully-extracted article — see Pricing). Paywalled articles return whatever public text is available with `truncated: true`.

## `maxItems` (type: `integer`):

Maximum number of items to output in this run, across all outlets combined.

## `maxPerOutlet` (type: `integer`):

Maximum number of raw items to consider from each outlet before keyword filtering.

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

Only keep items published within this many hours of the run. Items with no parseable publish date are kept.

## `dedupe` (type: `boolean`):

Skip items already seen in a previous run of this actor (tracked in the actor's key-value store).

## `maxSeenIds` (type: `integer`):

Upper bound on how many previously-seen item IDs are kept in storage for deduplication.

## `proxyConfiguration` (type: `object`):

Optional proxy to use for all outbound requests (recommended if you run this frequently, to reduce the chance of rate limiting).

## Actor input object example

```json
{
  "keywords": [],
  "excludeKeywords": [],
  "outlets": [
    "tagesschau",
    "spiegel",
    "zeit",
    "faz",
    "sz",
    "welt",
    "heise",
    "handelsblatt",
    "ntv",
    "t-online"
  ],
  "fullText": false,
  "maxItems": 100,
  "maxPerOutlet": 50,
  "publishedWithinHours": 48,
  "dedupe": true,
  "maxSeenIds": 20000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

## `resultsCsv` (type: `string`):

No description

# 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": [],
    "excludeKeywords": [],
    "outlets": [
        "tagesschau",
        "spiegel",
        "zeit",
        "faz",
        "sz",
        "welt",
        "heise",
        "handelsblatt",
        "ntv",
        "t-online"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nerdrx/german-news-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": [],
    "excludeKeywords": [],
    "outlets": [
        "tagesschau",
        "spiegel",
        "zeit",
        "faz",
        "sz",
        "welt",
        "heise",
        "handelsblatt",
        "ntv",
        "t-online",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("nerdrx/german-news-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": [],
  "excludeKeywords": [],
  "outlets": [
    "tagesschau",
    "spiegel",
    "zeit",
    "faz",
    "sz",
    "welt",
    "heise",
    "handelsblatt",
    "ntv",
    "t-online"
  ]
}' |
apify call nerdrx/german-news-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nerdrx/german-news-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/CNld1CZLxi3s7xElt/builds/m5oVuiEIpmHQ4iRSd/openapi.json
