Google News Scraper
Give it search terms, Google News topics, top stories or a city, in any language and country, and it returns one JSON row per article: headline, publisher, the publisher's real article URL, publish time, snippet, thumbnail, and the story cluster with other outlets covering the same story. $3.60 per 1,000 articles; you pay only for rows you get.
$3.60 per 1,000 articles on the Free plan, down to $0.90 on higher plans
Why this actor
- Real publisher URLs, decoded from Google's
news.google.com/rss/articles/...links, on by default and included in the price. - Optional main article text (
includeArticleText, $6.00 per 1,000) where the publisher serves it openly. Paywalled and blocked pages are flagged, never worked around. - Monitor mode skips articles you already have.
- Errors come back as free rows that say why.
What you get
| Field | What it holds |
|---|---|
articleId, title | Google's article id (stable, use it to dedupe) and the headline without the " - Publisher" suffix. |
publisher, publisherDomain, publisherUrl | The outlet: name, domain (reuters.com) and home page. |
url, urlDecoded | The publisher's own article URL, decoded from Google's redirect link. |
googleNewsUrl | Google's link to the article. |
publishedAt | Publish time, ISO 8601 UTC. |
snippet, thumbnailUrl | The article's own summary and lead image, read from its page. |
storyId, isStoryLead, relatedCount, relatedCoverage | Story cluster: the same story across outlets, with the other outlets' articles listed. |
textStatus, text, wordCount, isPaywalled, pageLanguage | Full text and how reading it went (with includeArticleText). |
source, sourceType, position, edition | Which query, section, location or URL the article came from, its rank there, and the edition (US:en). |
scrapedAt | When the row was read. |
Sample row (from the README example; long text shortened, lists cut to two items):
{
"rowType": "article",
"platform": "google-news",
"articleId": "CBMiggFBVV95cUxPYXI4MDQ5UDdXZDNuUUpwYkVwY3RYM05lZzhqcFhlb0NfeDVacEFfYkNQWkRuNDZPM05pRndYcjZ2QXBxeHA1ZnpaSVZjNEhXRnZvdkhTZTR3aFppbVlxUXJpeHRhSXRvV0R1VFRuUmhNe...",
"title": "Southwest Airlines beats estimates as fuel costs ease",
"publisher": "CNBC",
"publisherDomain": "cnbc.com",
"publisherUrl": "https://www.cnbc.com",
"url": "https://www.cnbc.com/2026/10/21/southwest-airlines-luv-3q-2026-earnings.html",
"urlDecoded": true,
"googleNewsUrl": "https://news.google.com/rss/articles/CBMiggFBVV95cUxPYXI4MDQ5UDdX...?oc=5",
"publishedAt": "2026-10-21T11:02:00.000Z",
"snippet": "Southwest Airlines reported third-quarter results that topped expectations ...",
"thumbnailUrl": "https://image.cnbcfm.com/api/v1/image/108444221-1792627087371.jpg",
"storyId": "3f9a1c0d52e8b7a4",
"isStoryLead": true,
"relatedCount": 4,
"relatedCoverage": [
{
"articleId": "CBMie0FVX3lxTE5aVW9GbzJYU2tyRUZaZXhuYVZLUzNKaURYMF96UUxGelZE",
"title": "Southwest earnings top forecasts",
"publisher": "Reuters",
"googleNewsUrl": "https://news.google.com/rss/articles/CBMie0FVX3lxTE5aVW9GbzJYU2tyRUZaZXhuYVZLUzNKaURYMF96UUxGelZE?oc=5"
}
],
"isPaywalled": false,
"textStatus": "extracted",
"text": "Southwest Airlines on Wednesday reported third-quarter earnings ...",
"wordCount": 812,
"pageLanguage": "en",
"source": "BUSINESS",
"sourceType": "section",
"position": 1,
"edition": "US:en",
"scrapedAt": "2026-10-22T08:00:00.000Z"
}
Price
Live Apify price on 2026-10-31, per 1,000 rows, by Apify plan. You pay only for rows delivered; error and summary rows marked free in the README cost nothing. Plus $0.001 per GB of memory when a run starts.
| Per 1,000 | Free | Bronze | Silver | Gold | Platinum | Diamond |
|---|---|---|---|---|---|---|
| Article (main) | $3.60 | $3.60 | $3.38 | $0.90 | $0.90 | $0.90 |
| Article with full text | $6.00 | $5.25 | $4.45 | $2.50 | $2.50 | $2.50 |
Use cases
- News monitoring and alerts: every new article on your brand, competitors or topics, hourly, in any language.
- PR and media tracking: which outlets covered a story (
storyId,relatedCoverage) and when. - Market and investment research: company and sector news with real URLs and full text for analysis.
- AI agents and RAG: "latest news on X" with clean text and sources, one row per story.
- Dataset building: headlines by topic and country over time.
Run it from code or an AI agent
Actor ID arcadium/google-news. Set APIFY_TOKEN to your Apify API token. The input below is the actor’s prefilled example; see the guide for every field.
curl
curl -X POST "https://api.apify.com/v2/acts/arcadium~google-news/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"queries":["artificial intelligence"],"language":"en","country":"US","maxArticlesPerQuery":20}'JavaScript
// npm install apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('arcadium/google-news').call({
"queries": [
"artificial intelligence"
],
"language": "en",
"country": "US",
"maxArticlesPerQuery": 20
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);Python
# pip install apify-client
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("arcadium/google-news").call(run_input={
"queries": ["artificial intelligence"],
"language": "en",
"country": "US",
"maxArticlesPerQuery": 20,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)MCP
{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=arcadium/google-news",
"headers": {
"Authorization": "Bearer <APIFY_TOKEN>"
}
}
}
}LangChain
# pip install langchain-apify (reads APIFY_API_TOKEN from the environment)
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("arcadium/google-news")
rows = tool.invoke({"run_input": {
"queries": ["artificial intelligence"],
"language": "en",
"country": "US",
"maxArticlesPerQuery": 20,
}})
# or give `tool` to an agent: create_react_agent(llm, [tool])LlamaIndex
# pip install llama-index-readers-apify
import os
from llama_index.core import Document, VectorStoreIndex
from llama_index.readers.apify import ApifyActor
reader = ApifyActor(os.environ["APIFY_TOKEN"])
documents = reader.load_data(
actor_id="arcadium/google-news",
run_input={
"queries": ["artificial intelligence"],
"language": "en",
"country": "US",
"maxArticlesPerQuery": 20,
},
dataset_mapping_function=lambda item: Document(text=str(item.get("text") or ""), metadata={"url": item.get("url")}),
)
index = VectorStoreIndex.from_documents(documents)CrewAI
# pip install crewai crewai-tools langchain-apify (reads APIFY_API_TOKEN from the environment)
from crewai import Agent
from crewai_tools import ApifyActorsTool
tool = ApifyActorsTool(actor_name="arcadium/google-news")
researcher = Agent(
role="Research analyst",
goal="Answer with data collected by the tool",
backstory="Uses public web data.",
tools=[tool],
)n8n
{
"nodes": [
{
"name": "Run arcadium/google-news",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
0,
0
],
"parameters": {
"method": "POST",
"url": "https://api.apify.com/v2/acts/arcadium~google-news/run-sync-get-dataset-items",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"queries\":[\"artificial intelligence\"],\"language\":\"en\",\"country\":\"US\",\"maxArticlesPerQuery\":20}",
"options": {
"timeout": 300000
}
}
}
],
"connections": {}
}Google Sheets
// Extensions > Apps Script. Store your token once: Project Settings > Script properties > APIFY_TOKEN.
function importLastRun() {
const token = PropertiesService.getScriptProperties().getProperty('APIFY_TOKEN');
const url = 'https://api.apify.com/v2/acts/arcadium~google-news/runs/last/dataset/items?status=SUCCEEDED&clean=true&format=json';
const res = UrlFetchApp.fetch(url, { headers: { Authorization: 'Bearer ' + token } });
const rows = JSON.parse(res.getContentText());
if (!rows.length) return;
const keys = Object.keys(rows[0]);
const values = rows.map((r) => keys.map((k) => (typeof r[k] === 'object' ? JSON.stringify(r[k]) : r[k])));
const sheet = SpreadsheetApp.getActiveSheet();
sheet.clearContents();
sheet.getRange(1, 1, 1, keys.length).setValues([keys]);
sheet.getRange(2, 1, values.length, keys.length).setValues(values);
}Limitations
- About 100 articles per query per Google News page; bigger requests are split into one-day windows, so very broad searches over long ranges are sampled per day.
- Snippets, thumbnails and text come from the publisher's page. Some large sites refuse automated visits (for example nytimes.com and reuters.com); their rows keep the headline and decoded URL, and
textStatussaysblocked. - Paywalled articles get no text, by design.
- Related coverage lists other outlets' headlines and Google links; their publisher URLs are not decoded. Turn
dedupeStoriesoff to get those articles as rows of their own (with decoded URLs) when the feed lists them. - Google News ranking and clusters are Google's; publish times are as Google reports them.
FAQ
Why are some URLs null? decodeUrls was off, or Google did not resolve that link in this run. googleNewsUrl always works in a browser.
Why is the text empty for some articles? Look at textStatus: paywalled (the publisher marks it as subscriber content), blocked (the site refused the visit), too_short (no article text on the page, e.g. a video or live page).
How do I get only new articles every hour? Set onlyNew: true and a stateKey, then schedule the actor. Each article comes once.
Something looks wrong in the output. Who do I tell? Open an issue on the actor page with the run link. We read them all.
Related actors
Google Trends Scraper
Google Trends interest over time, regions, related queries and topics, $2.70 per 1,000 rows; apify/google-trends-scraper input works as is.
$2.70 per 1,000 result rows on the Free plan, down to $0.27 on higher plans