# Keyword News & RSS Monitor (`woundless_yellowwood/keyword-news-rss-monitor`) Actor

Give it RSS feeds (or news domains) and keywords — it checks them on a schedule and returns only matching articles, deduplicated across runs. No external API keys.

- **URL**: https://apify.com/woundless\_yellowwood/keyword-news-rss-monitor.md
- **Developed by:** [Proyecto Apify](https://apify.com/woundless_yellowwood) (community)
- **Categories:** News, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 matching 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/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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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

## Keyword News & RSS Monitor

Give it your RSS feeds and your keywords — it checks them on a schedule and hands back only the articles that match. Deduplicated across runs, so a daily recurring run never returns the same article twice.

No external API keys. No paywall scraping. No sentiment analysis. Just accurate keyword matching on the title and summary that the feed itself provides.

### What it does

1. **Takes feeds or bare domains.** Pass a full RSS/Atom URL or a domain like `bbc.co.uk` and the Actor auto-discovers the feed via the homepage `<link rel="alternate" type="application/rss+xml">` tag, then common paths (`/rss`, `/feed`, `/rss.xml`, `/atom.xml`, `/feed.xml`, `/index.xml`, `/feeds/posts/default`).
2. **Parses RSS 2.0 and Atom 1.0** with `fast-xml-parser` (no paid API). Extracts `title`, `summary` (HTML stripped), `source`, `publishedAt` (ISO 8601), `link`.
3. **Filters by lookback window** — only articles published within `lookbackHours` (default 24h) are kept. Articles with no parseable date are treated as fresh.
4. **Matches keywords** against `title + summary`. `matchMode: "any"` = at least one keyword; `"all"` = every keyword. `caseSensitive` defaults to `false`.
5. **Deduplicates across runs** via a persisted KeyValueStore record (`dedup-state`), capped at 10,000 entries with LRU eviction so it stays bounded over months of scheduled runs.
6. **Pushes one row per matching new article** to the default dataset.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `feeds` | array | — | RSS/Atom URLs or bare domains. Required. |
| `keywords` | array | — | Keywords or phrases to match. Required (pass `[]` to disable matching). |
| `matchMode` | `any` \| `all` | `any` | How to combine multiple keywords. |
| `caseSensitive` | boolean | `false` | Case-sensitive matching. |
| `maxArticlesPerFeed` | integer | `100` | Cap on matching articles emitted per feed per run. |
| `lookbackHours` | integer | `24` | Only articles newer than this are kept. |

### Output

One row per matching article in the default dataset:

```json
{
  "title": "OpenAI announces new regulation policy",
  "summary": "The company said it will publish quarterly safety reports...",
  "source": "Hacker News",
  "publishedAt": "2026-07-23T09:14:00.000Z",
  "matchedKeywords": ["AI", "regulation"],
  "link": "https://news.ycombinator.com/item?id=12345678"
}
````

### Setting up a daily scheduled run

1. Open the Actor in Apify Console → **Schedules** → **Create new**.
2. Pick **Cron** = `0 8 * * *` (08:00 UTC every day) or your preferred time.
3. Set the input. Example:
   ```json
   {
     "feeds": [
       { "url": "https://hnrss.org/frontpage" },
       { "url": "https://feeds.bbci.co.uk/news/rss.xml" },
       { "url": "reuters.com" }
     ],
     "keywords": ["AI", "regulation", "antitrust"],
     "matchMode": "any",
     "lookbackHours": 24
   }
   ```
4. Save. Each scheduled run reads the persisted `dedup-state` from the default KeyValueStore, so articles seen in yesterday's run are automatically filtered out.

### Reading the results programmatically

```bash
curl "https://api.apify.com/v2/acts/<your-username>~keyword-news-rss-monitor/runs/last/dataset/items?format=json" \
  -H "Authorization: Bearer $APIFY_TOKEN"
```

### Error handling

- Unreachable, malformed, or non-RSS feeds are logged (`WARNING: No RSS feed discovered for …` or `Failed to parse feed …`) and skipped. The run continues.
- Per-feed retries: 2 (Crawlee default), then `failedRequestHandler` logs and moves on.
- If no feeds resolve at all, the Actor exits non-zero so the schedule surfaces the failure.

### Limitations

- Only reads what the feed itself exposes — typically title + summary. Does not fetch full article bodies.
- No paywall bypass, no login flows.
- Keyword matching is plain substring (regex-escaped). No stemming, no fuzzy match.
- Discovery probes the homepage and a fixed list of common paths; if a site publishes its feed at a non-standard URL and doesn't advertise it in `<link rel="alternate">`, pass the full feed URL directly.

# Actor input Schema

## `feeds` (type: `array`):

List of RSS/Atom feed URLs (e.g. https://hnrss.org/frontpage) OR bare news domains (e.g. bbc.co.uk). For bare domains, the Actor will auto-discover the RSS feed by checking the homepage <link rel='alternate' type='application/rss+xml'> tag and then common paths (/rss, /feed, /rss.xml, /atom.xml, /feed.xml, /index.xml, /feeds/posts/default).

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

Keywords or short phrases to match against the article title + summary. Match is performed on substrings; e.g. 'openai' matches 'OpenAI launches...'. Empty list disables keyword filtering (all in-window articles are returned).

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

any = article matches if at least one keyword is present. all = article matches only if every keyword is present.

## `caseSensitive` (type: `boolean`):

If true, keyword matching is case-sensitive. Default false (case-insensitive).

## `maxArticlesPerFeed` (type: `integer`):

Caps the number of matching articles emitted per feed per run. Protects against runaway output from large feeds.

## `lookbackHours` (type: `integer`):

Only articles published within the last N hours are kept. Articles without a parseable publish date are always kept (treated as fresh).

## Actor input object example

```json
{
  "feeds": [
    "https://hnrss.org/frontpage",
    "https://feeds.bbci.co.uk/news/rss.xml"
  ],
  "keywords": [
    "AI",
    "regulation"
  ],
  "matchMode": "any",
  "caseSensitive": false,
  "maxArticlesPerFeed": 100,
  "lookbackHours": 24
}
```

# 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 = {
    "feeds": [
        "https://hnrss.org/frontpage",
        "https://feeds.bbci.co.uk/news/rss.xml"
    ],
    "keywords": [
        "AI",
        "regulation"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("woundless_yellowwood/keyword-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 = {
    "feeds": [
        "https://hnrss.org/frontpage",
        "https://feeds.bbci.co.uk/news/rss.xml",
    ],
    "keywords": [
        "AI",
        "regulation",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("woundless_yellowwood/keyword-news-rss-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 '{
  "feeds": [
    "https://hnrss.org/frontpage",
    "https://feeds.bbci.co.uk/news/rss.xml"
  ],
  "keywords": [
    "AI",
    "regulation"
  ]
}' |
apify call woundless_yellowwood/keyword-news-rss-monitor --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Keyword News & RSS Monitor",
        "description": "Give it RSS feeds (or news domains) and keywords — it checks them on a schedule and returns only matching articles, deduplicated across runs. No external API keys.",
        "version": "1.0",
        "x-build-id": "aKC1aSYZ2YuJSSyVw"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/woundless_yellowwood~keyword-news-rss-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-woundless_yellowwood-keyword-news-rss-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/woundless_yellowwood~keyword-news-rss-monitor/runs": {
            "post": {
                "operationId": "runs-sync-woundless_yellowwood-keyword-news-rss-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/woundless_yellowwood~keyword-news-rss-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-woundless_yellowwood-keyword-news-rss-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "feeds",
                    "keywords"
                ],
                "properties": {
                    "feeds": {
                        "title": "Feeds or news domains",
                        "type": "array",
                        "description": "List of RSS/Atom feed URLs (e.g. https://hnrss.org/frontpage) OR bare news domains (e.g. bbc.co.uk). For bare domains, the Actor will auto-discover the RSS feed by checking the homepage <link rel='alternate' type='application/rss+xml'> tag and then common paths (/rss, /feed, /rss.xml, /atom.xml, /feed.xml, /index.xml, /feeds/posts/default).",
                        "items": {
                            "type": "string"
                        }
                    },
                    "keywords": {
                        "title": "Keywords / phrases",
                        "type": "array",
                        "description": "Keywords or short phrases to match against the article title + summary. Match is performed on substrings; e.g. 'openai' matches 'OpenAI launches...'. Empty list disables keyword filtering (all in-window articles are returned).",
                        "items": {
                            "type": "string"
                        }
                    },
                    "matchMode": {
                        "title": "Match mode",
                        "enum": [
                            "any",
                            "all"
                        ],
                        "type": "string",
                        "description": "any = article matches if at least one keyword is present. all = article matches only if every keyword is present.",
                        "default": "any"
                    },
                    "caseSensitive": {
                        "title": "Case sensitive",
                        "type": "boolean",
                        "description": "If true, keyword matching is case-sensitive. Default false (case-insensitive).",
                        "default": false
                    },
                    "maxArticlesPerFeed": {
                        "title": "Max articles per feed",
                        "minimum": 1,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Caps the number of matching articles emitted per feed per run. Protects against runaway output from large feeds.",
                        "default": 100
                    },
                    "lookbackHours": {
                        "title": "Lookback window (hours)",
                        "minimum": 1,
                        "maximum": 720,
                        "type": "integer",
                        "description": "Only articles published within the last N hours are kept. Articles without a parseable publish date are always kept (treated as fresh).",
                        "default": 24
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
