# Google News Scraper (`dami_studio/google-news-scraper`) Actor

Google News scraper API by keyword or topic, one row per headline: title, publisher, date, snippet and the publisher's real link, decoded from Google's redirect, with links that would not decode marked. Full text, and a summary on your own OpenAI key, are optional. $3.00 per 1,000 articles.

- **URL**: https://apify.com/dami_studio/google-news-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** News, Integrations, AI
- **Features:** Limited permissions, Multi-tenant, Server interface
- **Stats:** 10 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $2.85 / 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.
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

**Google News scraper for a keyword or a topic feed, one row per article**: headline,
publisher, date, snippet and a link. Optionally the full body text, and a short summary with a
sentiment label if you bring your own OpenAI key.

Here is the part that decides whether you need this. Every link in a Google News feed is a redirect, and the publisher's actual address has to be
decoded out of it. That decoding is best effort. When a link will not decode, the row still arrives
with the Google link in `url` and `urlResolved: false`, so you always know which one you are
holding.

| | |
|---|---|
| **Input** | A keyword search, or one of eight Google News topic feeds |
| **Output** | One row per article: headline, publisher, date, snippet and the publisher's own link |
| **Ceiling** | 100 articles per run |
| **Account needed** | None. An OpenAI key only if you want the summaries |
| **Price** | $3.00 per 1,000 articles on the Free plan, a little less on paid plans. The free plan's $5 a month covers about 1,600 |

| Using an AI agent? | |
|---|---|
| Actor | `dami_studio/google-news-scraper` |
| Set one field | `query`, labelled "Search query" in the Console |
| Cap the bill | `maxTotalChargeUsd` on the run, for example `0.25`: it never charges past that |
| MCP, one link | `https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/google-news-scraper` |
| How often | Every few hours for a fast topic, daily for a digest |

### 🔍 What Google News Scraper does

It reads a Google News feed and writes a row per article, with the original Google link kept beside
the decoded one in case you want it.

**Search or topic.** A `query` takes Google operators, so `tesla OR rivian`, `site:reuters.com` and
`intitle:layoffs` all work. A `topic` instead gives you the edition feed for World, Nation, Business,
Technology, Entertainment, Sports, Science or Health.

**Full text, if you ask.** With `fetchArticleText` on, each resolved article is downloaded and its
body pulled out, up to 20,000 characters. Some pages give no body text at all,
and those rows come back with `articleText: null` and are not charged. The same page gives
`publisherPublishedAt`, the publish time the publisher puts on it.

**Summaries, on your key.** `aiSummary` adds a one or two sentence summary and a sentiment label,
using the OpenAI key you supply. OpenAI bills you for those calls directly. This actor adds nothing
for them.

### 📋 What data you get from each Google News article

| What you get | Field |
|---|---|
| Headline | `title` |
| Publisher name and homepage | `source`, `sourceUrl` |
| When it was published, as Google's feed dates it | `publishedAt` |
| When it was published, as the publisher's page says, with full text on | `publisherPublishedAt` |
| The snippet from the feed | `snippet` |
| The publisher's own link, and whether it decoded | `url`, `urlResolved` |
| The original Google redirect link | `googleUrl` |
| The article body, with full text on | `articleText` |
| A short summary and a sentiment label, with AI summary on | `aiSummary`, `sentiment` |

### ▶️ How to scrape Google News

1. Open [Google News Scraper](https://apify.com/dami_studio/google-news-scraper) and click **Try for
   free**.
2. Put keywords into **Search query**, or leave it empty and pick a **Topic** instead.
3. Set **Max articles**, and **Freshness** if you only want recent pieces.
4. Tick **Fetch full article text** only if you need bodies, then click **Start**.
5. Download the dataset as JSON, CSV or Excel, or read it from the Apify API.

### 💰 How much does it cost to scrape Google News?

**$3.00 per 1,000 articles**, which is $0.003 each on the Free plan. Paid plans pay a little less: $2.95 on Starter, $2.90 on Scale and $2.85 on Business.
On the free plan, the $5 Apify gives you each month covers about 1,600 articles.

Resolving links, fetching article text and the AI summary all cost nothing extra here. The OpenAI
calls behind `aiSummary` are billed to you by OpenAI, on your own key.

What is not charged: a row where you asked for the body or the summary and it could not be had,
every diagnostic row, and a search that finds nothing.

### 📥 What you give it

```json
{
  "query": "openai funding",
  "freshness": "7d",
  "language": "en-US",
  "country": "US",
  "maxItems": 30,
  "resolveUrls": true
}
```

| Field | Default | What it is |
|---|---|---|
| `query` | none, the box starts at `artificial intelligence` | Keywords, with Google operators. Leave empty if you are using a topic. |
| `topic` | none | A topic feed instead of a search: World, Nation, Business, Technology, Entertainment, Sports, Science, Health. |
| `freshness` | any time | `1h`, `1d`, `7d`, `30d` or `1y`. It narrows a `query` search. A topic feed returns whatever the feed holds. |
| `language` | `en-US` | Google's `hl`. `en-GB`, `fr`, `de`, `es-419` and the rest. |
| `country` | `US` | Google's `gl`. `GB`, `CA`, `DE`, `IN` and so on. |
| `maxItems` | `50` | Articles to return, up to 100. |
| `resolveUrls` | on | Decode the Google redirect into the publisher's own link. |
| `fetchArticleText` | off | Download each article and pull out the body. Needs `resolveUrls` on. |
| `aiSummary` | off | Summary and sentiment per article. Needs your own key in `openaiApiKey`. |
| `openaiApiKey` | none | Read only when `aiSummary` is on. Stored as a secret. |
| `notionConnector` | none | Optional. Write each article into your Notion when the run finishes. |
| `notionParentId` | none | Optional. The Notion data source to write into. |
| `proxyConfiguration` | off | Optional, and a normal run does not want it. |

Send neither a `query` nor a `topic` and you get one uncharged `BAD_INPUT` row telling you which is
missing. Same if `aiSummary` is on without a key.

### 📤 What you get back

![Real run output](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/google-news-scraper-output.png)

A real row from a run on 16 September 2026, with the long Google link cut short:

```json
{
  "ok": true,
  "title": "Kapolri Dorong Pekerja Tingkatkan Kompetensi dan Penguasaan Teknologi - Tribratanews Polda Metro Jaya",
  "publishedAt": "2026-09-16T07:04:36.000Z",
  "source": "Tribratanews Polda Metro Jaya",
  "sourceUrl": "https://tribratanews.metro.polri.go.id",
  "snippet": "Kapolri Dorong Pekerja Tingkatkan Kompetensi dan Penguasaan Teknologi Tribratanews Polda Metro Jaya",
  "url": "https://tribratanews.metro.polri.go.id/kapolri-dorong-pekerja-tingkatkan-kompetensi-dan-penguasaan-teknologi/",
  "urlResolved": true,
  "articleText": null,
  "googleUrl": "https://news.google.com/rss/articles/CBMirgFBVV95cUxNbUp3aDlPMFNNNG5Qa3A3QlV0WFVKVjJHLU5GWExSal9qdEcz..."
}
```

| Field | How to read it |
|---|---|
| `url` | The publisher's own link once decoded, or the Google link when it would not decode. |
| `urlResolved` | `true` means `url` is the publisher's address. `false` means it is still the Google redirect. Check it per row. |
| `source`, `sourceUrl` | Publisher name and homepage, as Google's feed gives them. `null` when the feed leaves them out. |
| `publishedAt` | The time Google's feed gives the article. For the time the publisher gives, turn on full text and read `publisherPublishedAt`. |
| `articleText` | Only with `fetchArticleText` on, and only when the body could be pulled out. Capped at 20,000 characters. |
| `publisherPublishedAt` | Only with `fetchArticleText` on: the publish time on the article's own page, in UTC. `null` when the page shows none or could not be read. |
| `aiSummary`, `sentiment` | Only with `aiSummary` on and the call came back. When the call fails, they stay empty and the row is marked `ok: false` with `NO_SUMMARY`. |

### 🧾 Reading the output

| Row | How to spot it | Charged |
|---|---|---|
| A complete article | `ok: true` | yes |
| An article without the text you asked for | `ok: false`, `charged: false`, the article's own fields, and an `errorCode` with the reason in `error` | no |
| An article without the summary you asked for | `ok: false`, `charged: false`, the article's own fields, and `errorCode: "NO_SUMMARY"` with the reason in `error` | no |
| A diagnostic | `ok: false`, an `errorCode`, and no `title` | no |

There is no sample row on this one. An empty input goes straight to `BAD_INPUT`.

| Code | What it means |
|---|---|
| `BAD_INPUT` | No `query` and no `topic`, or `aiSummary` on with no key. |
| `NO_RESULTS` | The feed came back empty. Widen `freshness`, or try the keyword without operators. |
| `NOT_FOUND` | That feed address does not exist. Usually a bad `language` or `country` pairing. |
| `NOT_SERVED` | This article was not served this run. Re-run it later. Runs before 13 October 2026 wrote `BLOCKED` here, and runs from 13 to 17 October wrote `NETWORK`. |
| `RATE_LIMITED` | Too many requests in a short window. Wait and re-run. |
| `SERVER_ERROR` | Google answered with an error of its own. |
| `NETWORK` | Google was unreachable or the response never finished. |
| `TIME_LIMIT` | The run got close to its time limit and stopped on its own. What it delivered before that is charged as usual; articles it had not finished reading are left out and cost nothing. Give the run more time, or ask for fewer articles. |
| `UNEXPECTED_ERROR` | The run stopped early on an unexpected error. The results before this row are kept. |

A run that ends on one of these rows finishes green, `TIME_LIMIT` included: when the time limit stopped the run before any article came back, you get the `TIME_LIMIT` row and an amber status message.

An article row without its text carries one of these:

| Code | What happened to that article's page |
|---|---|
| `NOT_SERVED` | It was not served this run. Re-run it later. |
| `RATE_LIMITED` | It could not be read this run. Try again in a few minutes. |
| `NOT_FOUND` | The publisher's site says it does not exist. |
| `SERVER_ERROR` | The publisher's site answered with an error of its own. |
| `NETWORK` | It did not finish loading. |
| `NO_TEXT` | It came back without body text to read. |
| `LINK_NOT_DECODED` | Its Google link did not decode, so it was never fetched. |
| `BAD_INPUT` | It was never fetched: full text needs `resolveUrls` on. |

An article row that has everything but its summary carries `NO_SUMMARY`. Its `error` says which of three
things happened: OpenAI refused the key in `openaiApiKey`, the key's rate or credit limits turned the call
away, or the summary did not come back this run.

The Overview table hides `urlResolved`, `ok`, `articleText` and `aiSummary`, which are exactly the
fields you want to check. Switch the dataset to JSON to see them.

### 💡 What people use it for

- Watching a brand, a competitor's product name or a ticker across publishers, on a daily schedule.
- Building a reading list for one topic feed in one country, with real links you can open.
- Pulling headline, date and publisher into a sheet for a weekly roundup, skipping the body entirely.
- Feeding resolved article URLs into your own pipeline, since `googleUrl` is useless to most tools.

### 🚧 What it does not do

- **About 100 articles per feed.** Split a bigger job by keyword, by
  `site:`, or by freshness window.
- **Full text is a gamble per article.** Paywalled pages and pages that load their text in the browser come back with
  no body text. Those rows are flagged and not charged, so do not budget on getting the
  body for every article.
- **Some links never decode.** You get the Google link and `urlResolved: false` rather than a
  dropped row.
- **`freshness` narrows a search, not a topic feed.** Pick a topic and you get what that feed holds.
- **No archive.** There is no way to ask for last March.
- **The Notion connector receives every row**, including the incomplete ones. Filter on `ok` if you
  only want complete articles in there.
- **One query or one topic per run.** Use a schedule or several runs for a list of keywords.
- It is not an official Google tool. Dami's Studio is independent and is not affiliated with or endorsed by Google, or by any other company named on this page.

### 🧭 Which Google scraper do you need?

| If you want | Use |
|---|---|
| The web results page for a keyword: organic, ads, People Also Ask | [Google Search Results Scraper](https://apify.com/dami_studio/google-search-results-scraper) |
| What a search box suggests as people type | [Keyword Autocomplete Scraper](https://apify.com/dami_studio/keyword-autocomplete-scraper) |
| Interest in a keyword over time and by region | [Google Trends Scraper](https://apify.com/dami_studio/google-trends-scraper) |
| News articles with the publisher's real link | This one |
| Image results and the pages they sit on | [Google Images Scraper](https://apify.com/dami_studio/google-images-scraper) |
| The ads a brand runs on Google | [Google Ads Transparency Scraper](https://apify.com/dami_studio/google-ads-transparency-scraper) |
| World news in 65+ languages, with each outlet's country | [GDELT News Scraper](https://apify.com/dami_studio/gdelt-news-scraper) |

### ❓ Questions people ask

#### Do I need a Google API key to scrape Google News?

No. Nothing to apply for, nothing to log into.

#### Do I need an OpenAI key?

Only for `aiSummary`. Leave that off and the actor never touches OpenAI.

#### Why is `url` sometimes still a news.google.com address?

Because that particular link would not decode. `urlResolved` tells you, per row, and `googleUrl`
always holds the original.

#### Why did I get fewer articles than `maxItems`?

The feed ran out. A narrow keyword with a tight `freshness` window often returns a handful.

#### Can I call it from code or connect it to an AI assistant?

The [API tab](https://apify.com/dami_studio/google-news-scraper/api/python) has code for Python, JavaScript and the
CLI. MCP clients like Claude and ChatGPT use
`https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/google-news-scraper`; coding assistants can load [our
skill file](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/apify-search-app-stores-SKILL.md). All
three bill your Apify account at the same price.

```python
## pip install apify-client
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("dami_studio/google-news-scraper").call(
    run_input={
        "query": "artificial intelligence",
        "maxItems": 20,
    },
    max_total_charge_usd=0.25,  # the most this run may spend
)
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row.get("ok") is False:  # free note rows, not results
        continue
    print(row.get("publishedAt"), (row.get("title") or "")[:80], row.get("url"))
```

#### Is scraping Google News legal?

These are public news feeds, and the rows point back at the publishers. Apify's write-up on
[the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/) is a good starting
point, and we are not lawyers.

### 🆘 If something breaks

Open the **Issues** tab on the actor page. Send the query or topic and the run ID. The `errorCode` on
the diagnostic row usually names the problem on its own.

# Actor input Schema

## `query` (type: `string`):

Keywords to search Google News for (e.g. "electric vehicles", or advanced: "tesla OR rivian site:reuters.com"). Leave empty if using a Topic instead.

## `topic` (type: `string`):

Use a Google News topic feed instead of a search query.

## `freshness` (type: `string`):

Only return articles newer than this (applied to query searches).

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

Interface/article language code, e.g. en-US, en-GB, fr, de, es-419.

## `country` (type: `string`):

Edition country code, e.g. US, GB, CA, DE, FR, IN.

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

Maximum number of articles to return (Google News feeds cap around 100 per query).

## `resolveUrls` (type: `boolean`):

Decode Google's redirect links into the actual article URL (e.g. reuters.com/...). Best-effort; falls back to the Google link with urlResolved=false if a link can't be decoded. Check urlResolved per row to know whether url is the real publisher link or the Google redirect.

## `fetchArticleText` (type: `boolean`):

Download each resolved article and extract its main body text. Slower; requires Resolve URLs. Some sites give no extractable body, those rows come back with articleText=null and are flagged ok:false and NOT charged, so you only pay for articles whose text was actually fetched.

## `aiSummary` (type: `boolean`):

Generate a 1-2 sentence summary and a sentiment label per article using OpenAI. Needs an OpenAI API key. Best-effort: if the OpenAI call fails for an article, the aiSummary and sentiment fields are simply omitted/null for that row (the article itself is still returned).

## `notionConnector` (type: `string`):

Optional. Write each article as a page into your Notion when the run finishes. Authorize a Notion connector once in Settings → API & Integrations → MCP connectors, then pick it here. Leave empty to skip (default), results are always saved to the dataset regardless.

## `notionParentId` (type: `string`):

Optional. The Notion data source ID of the database to write into (only used if a Notion connector is set). Leave empty to create the pages privately in your workspace instead.

## `openaiApiKey` (type: `string`):

Only needed when AI summary is on. Kept private.

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

Optional, and not needed for a normal run.

## Actor input object example

```json
{
  "query": "artificial intelligence",
  "topic": "",
  "freshness": "",
  "language": "en-US",
  "country": "US",
  "maxItems": 50,
  "resolveUrls": true,
  "fetchArticleText": false,
  "aiSummary": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Scraped rows are stored in the default dataset (one row per result). Blocked/empty/error runs return a single uncharged diagnostic row instead.

# 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 = {
    "query": "artificial intelligence"
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/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 = { "query": "artificial intelligence" }

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/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 '{
  "query": "artificial intelligence"
}' |
apify call dami_studio/google-news-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/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/xqBHGof3zCJ6U4XVC/builds/0pxAW96V2uc2KcUre/openapi.json
