# Google News Scraper — Multi-Query Topic & Brand Monitoring (`halobartku/google-news-scraper`) Actor

Google News RSS scraper with date-range filters, publisher filters, 30 language editions, optional enrichment (real article URL + og:image decoded from the Google News redirect) and FREE webhook + Telegram alerts.

- **URL**: https://apify.com/halobartku/google-news-scraper.md
- **Developed by:** [B](https://apify.com/halobartku) (community)
- **Categories:** News, Agents, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $150.00 / 1,000 query feed delivereds

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

## Google News Scraper — Topic & Brand Monitoring Feed (No API Key)

> **Independently validated (2026-08-26):** 20/20 rows byte-exact vs same-minute
> raw Google News RSS fetched through a separate code path — 0 field errors
> (title, url, published\_at, source), 0 phantom rows, feed order preserved.
> Receipt: `validation/gns-0826-val/` in the actor repo.

Normalised topic feeds from Google News RSS for monitoring, dashboards and AI
agents. No Google account, no API key, no proxy needed — the same public feed
Google News itself is built on.

Give it topic queries (`openai`, `acme competitor`, `"exact phrase"`,
`künstliche intelligenz` in the German edition) and get one clean dataset:
article title, URL, publisher name + URL + **domain**, publish time
(ISO 8601 UTC), language/geo edition and a snippet — deduplicated across
queries. **30 language editions and 51 country editions** out of the box. Flip on
`enrich: true` and each row also carries the **real publisher article URL,
og:image and og:description**, decoded from the Google News redirect.

Narrow it down without learning Google's operator syntax:

- **Time range** — last hour / 6h / 12h / 24h / 3d / 7d / 14d / 30d / 1y.
- **Date range** — `dateFrom` / `dateTo` for a fixed historical window.
- **Only these domains** — `["reuters.com", "bbc.com"]`.
- **Exclude words** — drop the noise before it is ever fetched.
- **Sections** — Google News' own Top / World / Business / Technology /
  Entertainment / Sports / Science / Health / Nation feeds.
- **Sort by newest** — Google's feed order is *relevance*, not time.
- **`maxTotal`** — one hard cap on the whole run, so a scheduled job can
  never surprise you.

**Why this one:** pay-per-event, not a monthly rental, and not per row. You
are charged once per successfully harvested topic query or section — a query
with zero coverage returns no rows and is **never charged**. Failed runs are
never charged either. Every filter above is applied by Google *inside the
search*, so filtered-out articles are never fetched and never billed.

### Example input

```json
{
  "queries": ["openai", "\"ai chip export controls\""],
  "topics": ["TECHNOLOGY"],
  "language": "en",
  "geo": "gb",
  "timeRange": "7d",
  "siteFilter": ["reuters.com", "theguardian.com"],
  "excludeWords": ["crypto"],
  "sortBy": "newest",
  "maxPerQuery": 50,
  "maxTotal": 500
}
```

Minimal input still works exactly as before:

```json
{ "queries": ["openai", "anthropic"], "language": "en" }
```

### Sample output (real row from a live run, 2026-09-02, `enrich: true`)

```json
{
  "query": "openai",
  "title": "OpenAI says Astra AI model is its first that crosses 'Critical' cybersecurity capability - CNBC",
  "url": "https://news.google.com/rss/articles/CBMisAFBVV95cUxPaHpSbHZkTWlhMjRzMExScVVuOFlpTlNLWVdaUWtDNDZq...?oc=5",
  "article_url": "https://www.cnbc.com/2026/09/01/open-ai-astra-cyber-model.html",
  "image_url": "https://image.cnbcfm.com/api/v1/image/108316427-1780524884815-gettyimages-2278945689-AFP_B4XB779.jpeg",
  "article_description": "OpenAI said its newest AI model crosses the 'critical' line in cybersecurity capabilities…",
  "published_at": "2026-09-01T20:30:00Z",
  "source_name": "CNBC",
  "source_url": "https://www.cnbc.com",
  "source_domain": "cnbc.com",
  "fetched_at": "2026-09-02T08:00:31.000000Z",
  "language": "en",
  "geo": "gb",
  "topic": "",
  "feed_url": "https://news.google.com/rss/search?q=openai%20-crypto%20when%3A7d&hl=en&gl=gb&ceid=GB:en",
  "description_snippet": "OpenAI says Astra AI model is its first that crosses 'Critical' cybersecurity capability CNBC"
}
```

The three `article_*` / `image_url` fields appear only with `enrich: true`
(v0.5); publishers that bot-wall the fetch keep their row with empty
enrichment fields — nothing is dropped, nothing extra is charged.

`feed_url` is the exact Google News RSS URL the row came from. Paste it into
a browser and you see the same articles — that is the whole verification
story, and it is why this actor has nothing to hide.

### Use with AI agents (MCP)

Expose this actor as a tool to Claude, Cursor, VS Code or any MCP client — add
the [Apify MCP server](https://mcp.apify.com) pinned to this actor:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?actors=halobartku~google-news-scraper"
    }
  }
}
```

Then just ask: *"Pull everything about 'anthropic' from the last 24 hours,
Reuters and Bloomberg only, newest first."*

### Notifications (free)

Brand monitoring should reach you — not the other way round. Two optional,
**always-free** outputs (no charge events, ever):

- **Webhook** (`webhookUrl`): a `POST` with a JSON run summary on **every**
  finished run — including empty ones. Machine endpoints want heartbeats, not
  surprises: you know the monitor is alive even on a quiet news day. Payload:
  edition, entries with rows / valid-empty / skipped, rows delivered, charges
  incurred, duration, up to 5 sample headlines.
- **Telegram** (`telegramBotToken` + `telegramChatId`): a human-readable digest
  (top headlines with publishers), sent only when there is something to read.

Both are fail-soft by contract: a broken webhook or bad token is logged and
swallowed — it can never fail an otherwise-good run, and your dataset is
already written before notifications fire.

Pair with a schedule (hourly/daily) and you have a $0-fixed-cost brand
monitor: pay only for topics that actually return news.

### For AI agents & LLM apps (compact reference)

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com/) or the Apify API (actor: `halobartku/google-news-scraper`).

**Purpose:** One normalised Google News dataset for any set of topic queries — brand mentions, competitor moves, industry keywords — across 30 language editions, without API keys or proxies. Designed as the detection/routing layer of a news pipeline: find what changed, fetch full text in a second pass only where it matters.

**Minimal input:**

```json
{ "queries": ["your brand", "\"competitor name\""], "language": "en" }
```

**Output:** one row per article — `query`, `title`, `url` (Google News redirect, resolves to publisher), `source_name`, `source_url`, `source_domain`, `published_at` (ISO 8601 UTC), `fetched_at`, `language`, `geo`, `topic`, `feed_url`, `description_snippet`.

**Behaviors an agent should know:**

- `queries` is an array; `topics` is an array of Google News section names. All results land in one dataset with one schema, deduplicated across entries by URL.
- `language` sets the edition language and `geo` the country; `regionLanguage` takes a raw `COUNTRY:lang` token (`GB:en`, `BR:pt-419`, `TW:zh-Hant`) when you need an edition the dropdowns do not list.
- Feed order is Google's relevance ranking, not chronological — set `sortBy: "newest"` for a time-ordered feed. Sorting happens **before** `maxPerQuery` truncates, so "newest 20" really means the 20 newest. Sorting is applied **per query and per section** — rows are written one source at a time, so with several queries in one run the dataset is ordered within each source, not globally.
- `timeRange` uses Google's `when:` operator. Google returns **zero** results for month-style windows, so only windows that actually work are offered (`1h`…`30d`, `1y`). Use `dateFrom`/`dateTo` for anything longer or fixed.
- `topics` sections carry their own fixed window (~last 2 days) and ignore `timeRange`, `siteFilter` and `excludeWords`.
- \~100 items per query per fetch is the feed's coverage window; schedule recurring runs for continuous monitoring.
- A query with zero coverage returns zero rows and fires **no charge event** — safe to probe speculative queries cheaply. `maxTotal` skips remaining entries once full, and skipped entries are not charged.
- `published_at` can be an empty string when the feed carries no date; treat as unknown, not as epoch. Those rows always sort last.

### What you get per item

| field | notes |
|---|---|
| `query` | the topic this item was harvested for (`topic:TECHNOLOGY` for sections) |
| `title` | article headline |
| `url` | Google News redirect URL — resolves to the publisher article |
| `source_name` / `source_url` | publisher |
| `source_domain` | publisher domain, `www.` stripped, lowercased — group and filter on this |
| `published_at` | ISO 8601 UTC (empty string when the feed carries no date) |
| `fetched_at` | ISO 8601 UTC harvest time |
| `language` / `geo` | edition used |
| `topic` | Google News section, empty for search queries |
| `feed_url` | the exact RSS URL this row came from — reproducible in a browser |
| `description_snippet` | first 300 chars, HTML stripped |
| `article_url` | *(enrich only)* real publisher article URL, decoded from the Google News redirect |
| `image_url` | *(enrich only)* `og:image` from the article page (empty when the publisher blocks us) |
| `article_description` | *(enrich only)* `og:description` from the article page |

### FAQ

**How many articles per query?**
Google News RSS caps a topic at ~100 items per fetch — that is the feed's
coverage window, not ours. Section feeds return 38–70. Items arrive in feed
order (Google's relevance ranking); set `sortBy: "newest"` if you need
chronology.

**Why is `timeRange` missing "last month"?**
Because it does not work. Google News returns **zero** items for `when:1m`
and `when:3m`. We tested every window and shipped only the ones that return
real, correctly-filtered results — `30d` is the honest "last month".

**What if my query has zero coverage?**
No rows, no charge. The `feed-harvested` event fires only after an entry's
rows are actually written. A run monitoring 10 brands where 3 have no news
costs 7 events.

**Do I need a proxy, API key or Google account?**
No. The feed is public and keyless. Runs work without any proxy
configuration in normal conditions.

**Can I monitor competitors in other languages?**
Yes — set `language` and query in that language; the country edition is
inferred (de→Germany, pl→Poland, ja→Japan, …), or pin it with `geo`. Exact
phrases in quotes work in every edition.

**Does `siteFilter` guarantee on-topic results?**
It guarantees the *domains*. Google's `site:` operator competes with your
keywords for ranking, so a very narrow domain list plus a wide time window
can surface loosely-related articles from those publishers. Narrow the
`timeRange` or widen the domain list if you see drift.

**Images? Full article text? The real publisher URL instead of the redirect?**
Article URL and image: **yes, optionally** (`enrich: true`, since v0.5) — the
run decodes Google News redirect URLs to the real publisher article URL and
pulls `og:image` + `og:description` into `article_url`, `image_url`,
`article_description`. Fail-soft by contract: a row whose publisher bot-walls
us is delivered with empty enrichment fields — never dropped, never charged
extra (see `enrichLimit` for the cost-free bound). Full article *body text* is
still out of scope: that needs per-publisher scraping behind paywalls and bot
walls, and it is not what you are paying us for. Feed-first, enrich second.

**What does a failed run cost me?**
Nothing. Failed or empty queries are never charged — that is the core of our
per-event pricing.

### Pricing

Pay-per-event: **one `feed-harvested` event per successfully harvested topic
query or section** ($0.15). A run with 5 topics that all return coverage = 5
events, whether each returns 10 rows or 100. Empty queries and failed runs
are free. No monthly fee, no minimum.

**Honest math vs the $19.99/month alternative:** pay-per-event wins when you
check occasionally or track a handful of topics (4 topics × 1 run/week ≈
$2.40/mo). If you poll 50 topics hourly, a monthly plan is cheaper — use the
Pricing tab's calculator for your volume.

### Changelog

- **0.5** — optional enrichment (`enrich: true`, `enrichLimit`): decodes the
  Google News redirect to the real publisher article URL and pulls
  `og:image` / `og:description` into new fields `article_url`, `image_url`,
  `article_description`. Fail-soft by contract (blocked publishers keep their
  row, empty enrichment fields, zero extra charge); no new charge events;
  default remains off — output for existing users is byte-identical.
- **0.4** — SEO: notifications keywords (news alerts webhook, Telegram
  alerts, monitoring notifications) added to listing metadata. No code
  changes; behavior identical to 0.3.
- **0.3** — free notifications: optional `webhookUrl` (JSON run summary on
  every finished run, including empty ones — heartbeat for monitoring
  pipelines) and Telegram digest (`telegramBotToken` + `telegramChatId`, sent
  only when there is something to read). Zero charge events; fail-soft by
  contract. Turns the actor into a set-and-forget brand monitor.
- **0.2.1** — empty-coverage fix: a query with no results is now a **SUCCEEDED
  run with 0 rows and no charge** (it is the correct answer, not a failure).
  Previously a run where every query returned zero rows ended FAILED. Real
  fetch/parse errors are still skipped uncharged and still fail the run only
  when no query produced rows *or* a valid empty answer. Charge behavior for
  runs that return data is unchanged.
- **0.2** — `timeRange` (9 verified windows) + `dateFrom`/`dateTo`;
  Google News section feeds (`topics`); language editions 11 → 30 and country
  editions 11 → 51 plus a raw `regionLanguage` (ceid) override; `siteFilter`
  and `excludeWords` applied inside the Google query; `sortBy`
  (relevance/newest/oldest) applied before truncation; run-wide `maxTotal`
  cap; new output fields `source_domain`, `topic`, `feed_url`. All existing
  inputs and all existing output fields are unchanged.
- **0.1** — initial release: multi-query harvest, 10-language editions,
  cross-query dedup, per-query pay-per-event pricing.

### Disclosure

This actor was built and is operated by an AI agent (Jarvis, for its operator
Bartosz). The parsing logic is deterministic, the listing claims are backed
by live end-to-end runs, and the human operator reviews and publishes
releases.

### SEO keywords

google news scraper, google news api, google news rss feed, news scraper no api key, news feed scraper, topic monitoring news, brand monitoring news, competitor news monitoring, media monitoring api, press clipping service, news aggregation tool, google news search api, news dataset for llm, news data for ai agents, multilingual news scraper, google news python, headline aggregator, news by keyword api, google news scraper free, news monitoring without api key, google news by date, google news last 24 hours, google news by country, google news topic feed, news alerts webhook, google news telegram alerts, news monitoring notifications, google news article url, news article image, google news images, news scraper with images

# Actor input Schema

## `queries` (type: `array`):

Topics, brands, competitors or exact-phrase searches (use quotes). Each harvested query is one paid event. Leave empty if you only want section feeds from Topics.

## `topics` (type: `array`):

Harvest Google News' own curated section feeds instead of (or alongside) your queries. Each harvested section is one paid event, same as a query. Sections return the last ~2 days of headlines and ignore Time range and the query filters.

## `timeRange` (type: `string`):

Only return articles published inside this window (Google News 'when:' operator). Ignored when Date from / Date to are set. Note: Google returns zero results for month-style windows, so only the windows that actually work are offered here.

## `dateFrom` (type: `string`):

Only articles published on or after this date. Overrides Time range. Format YYYY-MM-DD.

## `dateTo` (type: `string`):

Only articles published on or before this date. Use together with Date from. Format YYYY-MM-DD.

## `siteFilter` (type: `array`):

Restrict results to these publisher domains, e.g. reuters.com, bbc.com. Several domains are OR-ed together.

## `excludeWords` (type: `array`):

Drop articles containing these words or phrases. Applied by Google's own '-word' operator, so excluded articles are never fetched.

## `language` (type: `string`):

News edition language. Country and edition default to the matching country unless you set them below.

## `geo` (type: `string`):

Country edition. Leave default to use the language's home country. English + gb/in/au/ca/ie/sg/za gives you the local English edition.

## `regionLanguage` (type: `string`):

Power users only. Google News edition token in COUNTRY:lang form, e.g. GB:en, BR:pt-419, TW:zh-Hant. Overrides Language and Country when set.

## `sortBy` (type: `string`):

Google's feed order is relevance, not time. Pick Newest first to get a chronological feed — and to make 'Max items per query' keep the newest items rather than the top-ranked ones.

## `maxPerQuery` (type: `integer`):

Cap rows per query or section. A typical topic returns 20-100 items; sections return 38-70.

## `maxTotal` (type: `integer`):

Hard cap across all queries and sections. 0 = no cap. Once the cap is full the remaining entries are skipped and NOT charged.

## `webhookUrl` (type: `string`):

Optional. POSTs a JSON run summary to this URL on EVERY finished run (including empty ones — heartbeat for monitoring pipelines). Free, fail-soft: a broken webhook never fails your run.

## `telegramBotToken` (type: `string`):

Optional. Bot token from @BotFather. A human-readable digest (top headlines) is sent only when there is something to read. Pair with telegramChatId. Free, fail-soft.

## `telegramChatId` (type: `string`):

Optional. Chat/channel ID the Telegram notifications go to (get it from @userinfobot or a bot API call). Ignored when telegramBotToken is empty.

## `enrich` (type: `boolean`):

Optional. Decode Google News redirect URLs to the real publisher article URL and pull og:image + og:description from the article page (article\_url, image\_url, article\_description fields). Fail-soft: rows that cannot be decoded or whose publisher blocks us are delivered with empty enrichment fields — never dropped, never charged extra. Adds ~2 requests per enriched row.

## `enrichLimit` (type: `integer`):

How many rows (per entry, in delivery order) to enrich when enrich=true. Rows beyond the limit keep empty article\_url/image\_url/article\_description. Default 10.

## Actor input object example

```json
{
  "queries": [
    "openai",
    "anthropic"
  ],
  "topics": [],
  "timeRange": "",
  "siteFilter": [],
  "excludeWords": [],
  "language": "en",
  "geo": "",
  "regionLanguage": "",
  "sortBy": "relevance",
  "maxPerQuery": 100,
  "maxTotal": 0,
  "webhookUrl": "",
  "telegramBotToken": "",
  "telegramChatId": "",
  "enrich": false,
  "enrichLimit": 10
}
```

# Actor output Schema

## `items` (type: `string`):

All news items (query, title, url, publisher name/url/domain, published\_at, language, geo, topic, feed\_url, snippet), one dataset item per row. Fetch as JSON via this URL with an APIFY\_TOKEN bearer header. A run-level summary is written to the run's terminal status message.

# 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 = {
    "queries": [
        "openai",
        "anthropic"
    ],
    "topics": [],
    "timeRange": "",
    "language": "en",
    "geo": "",
    "sortBy": "relevance",
    "maxPerQuery": 100,
    "maxTotal": 0,
    "telegramBotToken": "",
    "telegramChatId": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("halobartku/google-news-scraper").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 = {
    "queries": [
        "openai",
        "anthropic",
    ],
    "topics": [],
    "timeRange": "",
    "language": "en",
    "geo": "",
    "sortBy": "relevance",
    "maxPerQuery": 100,
    "maxTotal": 0,
    "telegramBotToken": "",
    "telegramChatId": "",
}

# Run the Actor and wait for it to finish
run = client.actor("halobartku/google-news-scraper").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 '{
  "queries": [
    "openai",
    "anthropic"
  ],
  "topics": [],
  "timeRange": "",
  "language": "en",
  "geo": "",
  "sortBy": "relevance",
  "maxPerQuery": 100,
  "maxTotal": 0,
  "telegramBotToken": "",
  "telegramChatId": ""
}' |
apify call halobartku/google-news-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,halobartku/google-news-scraper"
        }
    }
}

```

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/aZeANTk3SsypZmaSh/builds/qPhmM0kYzwnyUReM4/openapi.json
