# Google News Scraper - Headlines + Original Article URLs (`lanternlab318/google-news`) Actor

Search Google News by keyword and get the public results back: headline, publishing outlet, publish time, and the publisher's own article URL resolved out of Google's redirect link at no extra charge.

- **URL**: https://apify.com/lanternlab318/google-news.md
- **Developed by:** [Lantern Lab](https://apify.com/lanternlab318) (community)
- **Categories:** News, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.99 / 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?

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 Search Scraper

Give it search terms and get Google News' public search results back: the headline, the publishing outlet, the publish time, the Google News link - and `publisherUrl`, the outlet's own article URL resolved out of Google's redirect. No Google login, no cookies, no account of your own. **$2.99 per 1,000 articles, no start fee; resolving the publisher's URL is included.** See [Pricing](#pricing).

Input:

```json
{
  "queries": ["\"electric vehicle\" battery -China"],
  "language": "en-US",
  "country": "US",
  "timeRange": "7d",
  "maxArticlesPerQuery": 100
}
```

Output - one row per article (`recordType: "article"`); per-term status rows and the run summary are free. Sample from a real run; Google's opaque identifiers shortened:

```json
{
  "recordType": "article",
  "articleId": "CBMiWEFVX3lxTE9aV1hGZHI1TnlJeEVUS1By…",
  "title": "マリオの生みの親・宮本茂氏に文化勲章授与。50年のゲーム作りを語る[記者会見] - Nintendo DREAM WEB",
  "sourceName": "Nintendo DREAM WEB",
  "sourceUrl": "https://www.ndw.jp",
  "publishedAt": "2026-10-23T02:36:09.000Z",
  "publishedAtRaw": "Fri, 23 Oct 2026 02:36:09 GMT",
  "googleNewsUrl": "https://news.google.com/rss/articles/CBMiWEFVX3lxTE9aV1hG…?oc=5",
  "publisherUrl": "https://www.ndw.jp/nintendo-interview-261023/",
  "publisherUrlStatus": "ok",
  "query": "Nintendo",
  "language": "ja-JP",
  "country": "JP",
  "timeRange": "any",
  "rank": 1,
  "partial": false,
  "partialReason": null,
  "collectedAt": "2026-10-23T15:12:50.777Z"
}
```

Limits:

- **Public data only.** Keyword search on Google News' public feed; no login, no topics or "Full coverage" pages, no article body text or images - the result and the links, not the article. Google News itself serves about 100 articles per term and offers no way to page past that.
- **Every search term gets a status row saying what happened.** A term Google answered with nothing is an empty result; a term cut short by a limit, refusal or error is `partial` with the reason - the two are never confused.
- **Your limits stop collection and appear in the row**: `maxArticlesPerQuery`, the `maxUpstreamRequests` request ceiling and the maximum charge per run (see [Pricing](#pricing)).

### What it does

- Searches each term separately and labels every row with the term that produced it, so one run can cover many terms.
- Returns the outlet's own article URL in `publisherUrl`. A Google News link points at `news.google.com`, and the identifier behind it is opaque — resolving it takes two more requests per article, which this Actor does for you.
- Deduplicates across the whole run: an article that matches several of your terms is written once and charged once.
- Keeps the source identifier (`articleId`), the platform's own publish time (`publishedAt`, plus `publishedAtRaw` exactly as Google sent it) and our collection time (`collectedAt`) as separate fields.
- Says what happened per search term. A term Google answered with nothing is recorded as an empty result; a term that was cut short by a limit, a refusal or an error is marked `partial: true` with the reason. The two are different records and are never confused.
- Caps itself: `maxUpstreamRequests` is a hard ceiling on requests to Google News for the whole run, derived from your input when you leave it empty, so a run is never unbounded.
- Charges only article rows. Empty results, per-term status rows and the run summary are free.

### What it does not do

- No article body text and no images: it returns the search result and the link to the article, not the article.
- No Google News topics, home page or "Full coverage" sections — keyword search only.
- No other search engine, no translation, no login-only or paywalled content.
- No scheduling or monitoring built in; use Apify schedules.

### Input

| Field | Default | Meaning |
| --- | --- | --- |
| `queries` | required | One search term per line. Google's search syntax works: quotes for a phrase, `OR`, a leading `-` to exclude, `site:bbc.co.uk`. |
| `language` | `en-US` | Google News interface language as a BCP-47 tag: `en-US`, `zh-CN`, `ja-JP`, `de-DE`, `pt-BR`, `zh-TW`. |
| `country` | `US` | Two-letter country code of the edition to search. Pair it with a language of the same edition. |
| `timeRange` | `any` | `any`, `1h`, `1d`, `7d` or `30d`. Only articles published inside the window are returned. |
| `maxArticlesPerQuery` | `100` | Upper bound on rows per search term. Google News itself serves about 100 per term and offers no way to page past that. |
| `resolvePublisherUrl` | `true` | Resolve the outlet's own article URL. Off makes a run much faster and lighter, with `publisherUrl` null. |
| `maxUpstreamRequests` | derived | Hard ceiling on requests to Google News for the whole run. Leave empty and the Actor works one out from your terms and limits. |
| `proxyConfiguration` | Apify Proxy | The proxy the requests go through. The datacenter default is what this Actor is measured on. |

### Output

One row per article, `recordType: "article"`:

| Field | Meaning |
| --- | --- |
| `articleId` | Google News' own identifier for the article. Stable across editions of the same feed; the dedupe key. |
| `title` | The headline as Google News publishes it, including the ` - Outlet` suffix Google appends. |
| `sourceName` | The outlet, as Google News names it. |
| `sourceUrl` | The outlet's home page. |
| `publishedAt` | The platform's publish time, as an ISO 8601 instant. |
| `publishedAtRaw` | The same time exactly as Google sent it, unparsed. |
| `googleNewsUrl` | The Google News redirect link. |
| `publisherUrl` | The outlet's own article URL, or `null` when it could not be resolved. Never guessed. |
| `publisherUrlStatus` | `ok`, or why `publisherUrl` is null: `not_requested`, `no_signature`, `refused`, `request_failed`, `blocked`, `upstream_request_cap`. |
| `query` | The search term that produced this row. |
| `language`, `country`, `timeRange` | The edition and window that were searched. |
| `rank` | Position in that term's results, counting from 1. |
| `partial`, `partialReason` | Always `false` / `null` on an article row: the row exists because the article was collected. |
| `collectedAt` | Our own collection time, separate from `publishedAt`. |

One row per search term, `recordType: "query_status"`, never charged:

| Field | Meaning |
| --- | --- |
| `articlesReturned` / `articlesCharged` | Rows this term wrote and charged. |
| `duplicatesSkipped` | Articles an earlier term in the run had already collected. |
| `feedArticlesAvailable` | How many articles the feed offered, before your own limits. |
| `feedLimitReached` | `true` when Google's own ceiling, not your `maxArticlesPerQuery`, ended the list. |
| `partial` / `partialReason` | `true` with `upstream_request_cap`, `charge_limit_reached`, `blocked`, `upstream_error` or `not_collected` when the term's results are incomplete. |
| `statusText` | The same thing in a sentence, so the dataset explains itself. |

The last row is `recordType: "RUN_SUMMARY"` — articles collected and charged, empty terms, partial terms by reason, publisher URLs resolved by status, requests sent by endpoint, bytes moved, and why collection stopped. The same object is also in the run's key-value store under `RUN_SUMMARY`.

#### Partial is not empty

`articlesReturned: 0` with `partial: false` means Google News has no article for that term. `partial: true` means the term's results are incomplete and there may well be more. A run that collected nothing *and* had partial terms fails rather than reporting an empty success.

### Limits and boundaries

- Only Google News' public search results. No login, no cookies, no account, no personal data.
- Google News serves at most about 100 articles per search term and offers no paging, so `maxArticlesPerQuery` above 100 does not reach more articles; `feedLimitReached` tells you when that ceiling, rather than your setting, ended a list.
- A one-hour `timeRange` on a quiet term can legitimately return nothing. That is an empty result, not a failure.
- `publishedAt` is Google's own value for the article and is not re-derived from the page.

### Pricing

Pay per event, no start fee. Resolving the outlet's own article URL (`publisherUrl`) is included in the article price:

| Event | Price | Charged for |
| --- | --- | --- |
| `result` | $0.00299 ($2.99 per 1,000) | Each article row written, publisher URL included. Per-term status rows, empty results and the run summary are free. |

Set a maximum charge for the run and the Actor stops before going over it.

# Actor input Schema

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

One Google News search term per line. Google's own search syntax works, for example quotes for an exact phrase, OR, a leading minus to exclude a word, or site:bbc.co.uk. Every term is searched separately and its results are labelled with it. An article that matches several of your terms is returned once and charged once.

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

Google News interface language, as a BCP-47 tag: "en-US", "zh-CN", "ja-JP", "de-DE", "pt-BR", "zh-TW". This picks which edition of Google News is searched, so it changes which outlets appear, not just the wording.

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

Two-letter country code of the Google News edition to search, for example "US", "CN", "JP", "GB". Pair it with a language of the same edition ("zh-CN" with "CN", "ja-JP" with "JP").

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

Only return articles Google published within this window. "any" applies no window. Shorter windows return fewer articles: a one-hour window on a quiet term can legitimately return nothing.

## `maxArticlesPerQuery` (type: `integer`):

Upper bound on the rows one search term may produce. Google News itself returns at most about 100 articles for a search term and offers no way to page past that, so a value above 100 does not reach more articles - the row count is then Google's ceiling, not this setting, and the run says so per term.

## `resolvePublisherUrl` (type: `boolean`):

Google News links point at a news.google.com redirect, not at the outlet. With this on, every article also gets publisherUrl: the outlet's own URL, resolved through two extra requests per article. It is included in the price - there is no separate charge for it. Turn it off for a much faster, lighter run that returns only the Google News link, with publisherUrl null.

## `maxUpstreamRequests` (type: `integer`):

Hard ceiling on the HTTP requests this run sends to Google News: search feeds, publisher-URL resolution and retries together. At the ceiling collection stops - the search terms not reached are written with partialReason "upstream_request_cap" and the run then ends normally. Leave empty and the Actor works a ceiling out from your search terms and limits, so a run is never unbounded.

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

The proxy the requests to Google News go through. The default (Apify Proxy, datacenter) is the cheapest and is what this Actor is measured on; Google News serves its public search feeds over it without blocking.

## Actor input object example

```json
{
  "queries": [
    "OpenAI"
  ],
  "language": "en-US",
  "country": "US",
  "timeRange": "any",
  "maxArticlesPerQuery": 100,
  "resolvePublisherUrl": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `runSummary` (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 = {
    "queries": [
        "OpenAI"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lanternlab318/google-news").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"] }

# Run the Actor and wait for it to finish
run = client.actor("lanternlab318/google-news").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"
  ]
}' |
apify call lanternlab318/google-news --silent --output-dataset

```

## MCP server setup

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

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/1VY7QcC3BDPt4RUje/builds/ltt8RU2vHDETeNJxy/openapi.json
