Google News RSS feed: URLs that work, and what they leave out
Google News gives you an RSS feed for any search or topic. Below are the URLs that work, then the three places the feed falls short: redirect links, no article body, and duplicate stories.
I tested every URL and script here on 2026-10-08. Google does not document these endpoints as a public API, so re-test before you rely on them.
The Google News RSS URL formats
| Type | URL pattern | Notes |
|---|---|---|
| Top stories | https://news.google.com/rss?hl=en-US&gl=US&ceid=US:en |
The front page for that edition. |
| Search | https://news.google.com/rss/search?q=QUERY&hl=en-US&gl=US&ceid=US:en |
The workhorse. QUERY is URL-encoded. |
| One site | .../rss/search?q=site:reuters.com&... |
site: works inside q. |
| Time window | .../rss/search?q=openai+when:1d&... |
when:1h, when:1d, when:7d and so on. |
| Topic | https://news.google.com/rss/topics/TOPIC_ID?hl=en-US&gl=US&ceid=US:en |
Needs a topic ID, see below. |
| Section | https://news.google.com/rss/headlines/section/topic/TECHNOLOGY?... |
Redirects to a topic ID URL. |
Search feeds
Search feeds are the most useful because you control the query. Spaces become + or %20. Quote phrases with %22. Operators such as site: and when: go inside q.
# "interest rates" from the last 7 days, German edition
curl -s "https://news.google.com/rss/search?q=%22interest+rates%22+when:7d&hl=de&gl=DE&ceid=DE:de"
# Everything from one publisher in the last day
curl -s "https://news.google.com/rss/search?q=site:reuters.com+when:1d&hl=en-US&gl=US&ceid=US:en"
Search feeds returned 100 to 103 items. Top stories returned 38. Narrow queries return fewer: openai+when:1h gave 15. We tested each operator in the feed, with counts, in Google News RSS search operators.
Topic and section feeds
For the standard sections you do not need an ID. /rss/headlines/section/topic/TECHNOLOGY returns a 302 to the matching /rss/topics/<ID> URL, so use curl -L. That feed returned 68 items. For other topics, the ID is the long string in the topic page URL on news.google.com; I first confirmed the /rss/topics/<ID> form only with the ID the redirect returned. We later tested IDs copied from topic pages too, and found that some return a 200 with no items. See Google News topic IDs that still work.
Country and language editions
hl, gl and ceid describe one edition and should agree:
hlis the interface language, such asen-USorde.glis the country, such asUSorDE.ceidiscountry:language, such asUS:enorDE:de.
Leave some out and Google answers with a 302 to a URL with all three filled in. hl=de, gl=DE or ceid=DE:de alone each redirected to the German edition. With none, I got en-US, which may depend on where you request from. Plain curl does not follow redirects, so use curl -L.
When they disagree, ceid won: hl=de&gl=US&ceid=US:en returned the US English edition. Set all three and keep them consistent.
What an item actually contains
Here is one <item> from q=openai+when:1d, trimmed. The link and ID are shortened.
<item>
<title>OpenAI used AI to help write email warning Australian government AI had hacked its websites - The Guardian</title>
<link>https://news.google.com/rss/articles/CBMi3wFBVV95cUxOUkM3NTBLW...</link>
<guid isPermaLink="false">CBMi3wFBVV95cUxOUkM3NTBLW...</guid>
<pubDate>Thu, 08 Oct 2026 13:30:00 GMT</pubDate>
<description><a href="https://news.google.com/rss/articles/CBMi3w...">OpenAI used AI to help write email ...</a>&nbsp;&nbsp;<font color="#6f6f6f">The Guardian</font></description>
<source url="https://www.theguardian.com">The Guardian</source>
</item>

What you get:
- A title with the publisher appended after
-. - A publish date.
- A
sourceelement with the publisher name and its home page. - A
descriptionthat is an HTML fragment. In this item it is the headline link plus the publisher name. For some stories it is an ordered list of related headlines instead: the first item ofq=openaion 2026-10-08 came back that way.
What you do not get: the article text, an author, or the publisher's article URL.
The three problems you hit next
1. Redirect links, not publisher URLs
The link points at news.google.com/rss/articles/<encoded id>, not at the publisher. I fetched four with curl -L. Each took three hops (one through consent.google.com, from my location) and ended on a news.google.com HTML page with a 200. No hop went to the publisher.
curl -s -L -A "Mozilla/5.0" -o /dev/null \
-w "code=%{http_code} hops=%{num_redirects} final=%{url_effective}\n" \
"https://news.google.com/rss/articles/CBMi..."
The IDs do not help either: base64-decoding all four gave opaque bytes, not a URL.
What worked was calling the endpoint the Google News page itself uses. The article page carries two attributes, data-n-a-sg and data-n-a-ts. Send them with the article ID to batchexecute and the response contains the publisher URL. The script at the end of this post does it in about 20 lines of standard-library Python. It resolved every link I tried (4 by hand, 3 more in the pipeline script). It is an undocumented internal call: expect it to break, and rate-limit it, since you call Google once per item.
Dedup, canonical links and scraping all key on the URL, so you need a resolver first.
2. Headline and snippet only
The feed has no body. For an LLM, a search index or a digest, you must fetch each publisher page yourself. News pages are full of JavaScript, navigation, cookie banners and related-article rails, so extracting the article takes a renderer plus a content extractor. Markdown is a good target format for the result.
3. Same story, many outlets
One event produces dozens of items: wire copy on many sites, and rewritten headlines over the same body. Scrape first and dedup later and you pay for every copy. Dedup in layers, cheapest first:
| Check | Catches | Needs the body? |
|---|---|---|
| Exact URL or ID repeat | The same item seen on the previous poll | No |
| Headline clustering | Same story, similar headline | No |
| Content similarity | Same text under a rewritten headline | Yes |
The first two should run before you fetch anything. The third is a clean-up pass after scraping. For counts from real feeds, see why a Google News RSS feed repeats the same story.
Limits and gotchas
- Item count. Feeds are capped, around 100 for searches. A busy query can push stories out between polls, so poll often enough.
- Polling hard. I did not stress-test rate limits. Poll modestly and cache.
- No stability promise. These URLs are not a documented API. Parameters and behaviour can change without notice.
- Is there a Google News API? I know of no official public one for this. You have three options: these feeds, a third-party SERP or news API, or your own pipeline. I have not compared vendors.
Google Alerts as an RSS source
Google Alerts can also deliver to a feed.
- Create an alert and set Deliver to to RSS feed. You get a feed URL per alert instead of an email.
- I did not test Alerts feeds for this post, and options may differ by account.
- Expect the same limits: redirect-style links and no article body.
- It suits narrow, low-volume queries, not broad topics.
- For other tools that can watch a query, see Google Alerts alternatives for news monitoring.
Doing it yourself: minimal pipeline

The steps are the same in any language:
- Poll the feed.
- Resolve each redirect to the publisher URL.
- Dedup on metadata.
- Scrape the survivors.
- Convert to Markdown.
- Store, and mark the item as seen.
| Step | Cost | Where it breaks |
|---|---|---|
| Poll | Free | Item cap, rate limits, URL changes |
| Resolve redirect | Cheap, but rate-limited | The undocumented call changes |
| Metadata dedup | Free | Thresholds: too loose merges different stories |
| Scrape | Paid or heavy | JavaScript, paywalls, blocked requests |
| Markdown conversion | Cheap | Boilerplate leaking into the text |
| Store | Cheap | Retention and replay |
Dedup runs before the scrape on purpose. On q=openai+when:1d the feed held 100 items and a token-overlap check at 0.6 left 93. Each dropped item is a scrape you do not pay for. This is the whole of step 3.
import re
def tokens(title):
title = title.rsplit(" - ", 1)[0].lower() # drop the " - Publisher" suffix
return set(re.findall(r"[a-z0-9]+", title))
def similar(a, b, threshold=0.6):
return len(a & b) / max(1, len(a | b)) >= threshold
kept = [] # (title, tokens)
for title in titles: # titles from the feed, oldest first
toks = tokens(title)
if not any(similar(toks, t) for _, t in kept):
kept.append((title, toks))
Tune the threshold on your own topics. Too low and you merge different stories. Too high and rewrites slip through to the scraper.
My full pipeline.py adds the resolver, the scrape and the store. It reads a scraper key from WEBCRAWLERAPI_KEY; any service that returns Markdown for a URL works.
export WEBCRAWLERAPI_KEY=... # your own key
python3 pipeline.py "openai when:1d" 3
That printed 93 unique items, scraping 3 and wrote three Markdown files (19,542, 7,942 and 10,578 characters) from openai.com, mashable.com and theguardian.com. The scraper left some   entities in the Markdown, so budget a clean-up pass.
The resolver, the part the feed docs do not cover:
import json, re, urllib.parse, urllib.request
UA = "Mozilla/5.0"
COOKIE = "CONSENT=YES+cb; SOCS=CAI" # skips the EU consent redirect
def resolve(link):
gid = urllib.parse.urlparse(link).path.rstrip("/").split("/")[-1]
req = urllib.request.Request(f"https://news.google.com/rss/articles/{gid}",
headers={"User-Agent": UA, "Cookie": COOKIE})
html = urllib.request.urlopen(req, timeout=20).read().decode()
sg = re.search(r'data-n-a-sg="([^"]+)"', html).group(1)
ts = re.search(r'data-n-a-ts="([^"]+)"', html).group(1)
inner = ('["garturlreq",[["X","X",["X","X"],null,null,1,1,"US:en",null,1,'
'null,null,null,null,null,0,1],"X","X",1,[1,1,1],1,1,null,0,0,null,0],'
f'"{gid}",{ts},"{sg}"]')
body = urllib.parse.urlencode(
{"f.req": json.dumps([[["Fbv4je", inner, None, "generic"]]])}).encode()
req = urllib.request.Request(
"https://news.google.com/_/DotsSplashUi/data/batchexecute", data=body,
headers={"User-Agent": UA, "Cookie": COOKIE,
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"})
raw = urllib.request.urlopen(req, timeout=20).read().decode()
return json.loads(json.loads(raw.split("\n\n", 1)[1])[0][2])[1]
The same pipeline in n8n
I also built it in n8n: an RSS Read node, two Code nodes (headline dedup, then the resolver above), an HTTP Request node that calls the scraper, and a Set node that shapes title, url and markdown. It ran green end to end and returned 3 articles. The dedup node keeps the first 3 headlines per run to keep the test cheap and does not remember earlier runs, so a production version needs somewhere to store seen IDs.

It uses only built-in n8n nodes. To end that pipeline in Slack, see n8n RSS feed to Slack with full article text.
Skip the plumbing
If you are weighing a paid news API instead of the feed, see Is there a Google News API? and Newscatcher vs NewsData.io.
Feedmarkdown runs this pipeline for you. Today it supports Google News and Google Alerts as sources. Direct site sources and plain RSS are on the roadmap, not live. It resolves redirects to publisher URLs, dedups across sources before scraping, and returns each article as full-text Markdown. You receive it by HMAC-signed webhook (X-Feedmarkdown-Signature) or pull it as JSON Feed 1.1 or Atom. Plans start at $19 a month for 10 sources checked every 6 hours; the rest are in the pricing section. To skip the setup, start on the home page.