Monitoring competitors with Google News RSS
A Google News RSS query for a company name returns a feed that is mostly not company news, and the mix changes from pull to pull. In one test, the same Snowflake query returned 8 company-news headlines out of 100, then 3 out of 78 two minutes later. For competitor monitoring, the fix is to filter after the fetch, not to find a cleverer operator.
We ran 31 requests on 2026-10-22 for three companies: Snowflake, Databricks and MongoDB. We picked them for the test. They are not each other's stated competitors, and we did not check anyone's own list. Our media monitoring post builds a monitor for one company and cuts one kind of noise, the name clash. This post counts the other kinds. We did not test Google Alerts, any paid tool or Feedmarkdown on these queries.
What we watched and how we counted
The endpoint is the one from our Google News RSS feed guide. Here is the first request:
curl -s -A "Mozilla/5.0" \
"https://news.google.com/rss/search?hl=en-US&gl=US&ceid=US:en&q=Snowflake+when%3A7d"
We used a Python equivalent. The requests were 5 seconds apart, one attempt each, from a home connection in the Netherlands, US edition, with no cookies. Every response was 200. No rate limit or consent redirect showed up.
We gave every item one label by rule, then read the doubtful ones. That is not a double-blind count, so read every number below as "about".
- On-topic: the headline names the company and reports its news or a partner or vendor announcement. It does not mean the story matters.
- First-party: the company's own site is the publisher.
- Stock/market pages: quote, chart, options, filing, rating and "stocks moving" pages. We define this by page type, not value. Some of these pages carry real company news.
- Namesake, jobs and social, and other: the name means something else, the item is a job or social post, or the headline is about another party's news.
The same query gave 100 items, then 78

Feed, when:7d, 2026-10-22 |
Items | Publishers | On-topic | First-party | Stock/market pages | Namesake | Other |
|---|---|---|---|---|---|---|---|
Snowflake |
100 | 48 | 8 | 25 | 40 | 24 | 3 |
Snowflake, about 2 min later |
78 | 39 | 3 | 13 | 41 | 16 | 5 |
Databricks |
58 | 37 | 29 | 21 | 3 | 0 | 5 |
MongoDB |
35 | 25 | 5 | 0 | 28 | 0 | 2 |
The two Snowflake pulls share 52 headlines, but only 1 of the on-topic headlines. Treat every per-query count here as a single pull with a swing of that size. A feed also stops at about 100 items, so 100 means "at least".
Databricks was mostly company news and its own posts. MongoDB was 28 of 35 stock/market pages. That is not a noise score. A competitor watcher may want some of those pages, and we did not judge any item.
The noise comes in more kinds than a name clash
| Kind | Snowflake pull (100 items) | What removes it |
|---|---|---|
| Namesake | 24 | Context words or a rule in code. The pillar covers this. |
| Stock/market pages | 40 | A -word list, or a publisher list in code |
| The company's own posts | 25, many in other languages | Your code, if you want only third-party news |
| Other subject | 3 (a lease of a former office, a webinar title) | Only reading them |
| Jobs | 0 | Appear only after -site:, below |
The MongoDB feed also had one law-firm notice, and Databricks had one listicle. We count them and leave it there.
What each operator removed

Each change moved the noise instead of removing it. Quoting the legal name returned 60 items, 0 of them on-topic, and 42 were stock/market pages. Adding finance -words removed those pages, and namesakes took their place: 33 of 51. Adding context words with OR removed the namesakes, but 51 of 95 were stock/market pages. The best on-topic share we saw for Snowflake was 9 of 58, against 8 of 100 for the bare name. No query got above about one in six.
Adding the finance -words to the OR query left 60 items: 5 on-topic and 36 in "other", 20 of them from one publisher with generic infrastructure headlines. These are single pulls, so we would not rank the queries.
What -site: did to the feed
-site: is the operator people reach for to drop a publisher. We saw it do something else. Nine of our feeds with -site: returned exactly 100 items, and 53 to 91 of them were jobs or social pages: Facebook, LinkedIn, Instagram, YouTube, job boards and retail pages. It did remove its target: the company's own posts went from 25 to 0.
That could have been an artefact of how we sent it, so we ran six more requests on 2026-10-22 between 20:08 and 20:09 UTC.
| # | Query | Items | Publishers | Jobs and social |
|---|---|---|---|---|
| T1 | Snowflake -site:snowflake.com when:7d (repeat) |
100 | 48 | 57 |
| T2 | Snowflake -site:example.com when:7d |
100 | 45 | 51 |
| T3 | T1 with : sent unencoded |
100 | 48 | 57 |
| T4 | Snowflake -site:snowflake.com, no when: |
100 | 42 | 67 |
| T5 | Snowflake -intitle:snowflakes when:7d |
100 | 49 | 0 |
| T6 | Databricks -site:example.com when:7d |
100 | 41 | 74 |
What the rows show:
- It repeats. T1 shares 97 of its 100 items with the same query pulled earlier in the run.
- It is not the encoding. T3 returned the same 100 items as T1.
- It does not depend on
when:. T4 widened too, and returned three items older than seven days. - It does not need a real target.
example.comappears nowhere in these feeds, and T2 and T6 still came back full of jobs and social pages. The 9 first-party items in T2 were not removed. - It is not every negative operator. T5 looked like the plain query, with 95 items in common with it.
- On Databricks, the plain query had 58 items. T6 had 100, and only 12 of them were in the plain feed.
So any -site: we sent widened the feed. We do not know why, and we only tested these two names. Snowflake's plain feed was already at 100 items, so there the change shows up in what the items are. Don't use -site: to drop a publisher. Do it after the fetch. Our test of the other operators is in Google News RSS search operators.

Use one feed per company
An OR feed looks like a shortcut for a competitor set. We ran Snowflake OR Databricks OR MongoDB. It returned 100 items, and the headlines named Snowflake 60 times, Databricks 34 and MongoDB 7. MongoDB alone returned 35 items in the same window. The cap hides the quieter names.
A list of event words (acquires, raises, launches, partnership) gave Databricks 24 items, 13 on-topic. The plain feed had 29 of 58. These are different sets and too small to compare as percentages. Snowflake Databricks returned 18 items, 2 of them on-topic, so a head-to-head feed is thin.
One story under ten headlines
The plain Databricks pull had one story, a New York office expansion, in 10 headlines from 10 publishers, one of them Databricks itself. We ran the stage-1 headline check from our dedup post on the feed. It flagged 2 duplicates in 58 items, and they were other stories. Headline matching only catches near-identical wording, and a press release carried by many outlets still counts as many items. If you run several queries per company, dedupe by guid first.
A filter that works after the fetch
We moved the rules into code and ran them on the saved feeds. Save a feed with the curl above, then run the script with the company name. The third argument is an optional context regex for ambiguous names. The deny-list is ours and needs upkeep.
import re, sys
import xml.etree.ElementTree as ET
# usage: python3 filter.py feed.xml Snowflake '\b(data|cloud|AI)\b' (third argument optional)
# Rules are ours and need upkeep. Publishers are matched as substrings.
DENY_SOURCES = re.compile(
r"Facebook|Instagram|YouTube|Reddit|LinkedIn|Pinterest|Dice|Indeed|Glassdoor|"
r"TradingView|MarketBeat|Seeking Alpha|Investing\.com|Yahoo.?Finance|Moomoo|Zacks|"
r"Benzinga|TipRanks|Barchart|Trefis|GuruFocus|TradingKey|Quiver|Binance|CoinMarketCap|"
r"CoinGecko|Insider Monkey|Simply Wall|Stock Titan|Kalkine|Traders Union|TIKR|"
r"marketscreener|24/7 Wall|Motley Fool|NewsBreak", re.I)
MARKET_TITLE = re.compile(
r"\b(stocks?|shares|options chain|price target|bStocks|Form 4|Form 144)\b|\((SNOW|MDB)\)", re.I)
def tokens(title):
return set(re.findall(r"[a-z0-9]+", title.lower()))
def jaccard(a, b):
return len(a & b) / len(a | b) if a | b else 0.0
def main(path, company, context=None):
items = []
for it in ET.parse(path).iter("item"):
src = it.find("source")
items.append({"title": it.findtext("title") or "",
"source": src.text if src is not None else "",
"guid": it.findtext("guid") or ""})
kept, seen_guid, seen_tok = [], set(), []
for it in items:
t = it["title"]
if it["source"].lower() == company.lower():
continue # the company's own site
if DENY_SOURCES.search(it["source"]) or MARKET_TITLE.search(t):
continue # social, job and market pages
if company.lower() not in t.lower():
continue # the headline must name the company
if context and not re.search(context, t, re.I):
continue # ambiguous name: need a context word
if it["guid"] in seen_guid:
continue
tok = tokens(t)
if any(jaccard(tok, s) >= 0.6 for s in seen_tok):
continue # near-identical headline already kept
seen_guid.add(it["guid"]); seen_tok.append(tok); kept.append(it)
print(f"{len(items)} items in, {len(kept)} kept")
for it in kept:
print("-", it["title"])
if __name__ == "__main__":
main(*sys.argv[1:4])
python3 filter.py feed.xml Snowflake '\b(data|cloud|AI|Cortex|database)\b'
python3 filter.py feed.xml Databricks
| Saved feed (2026-10-22) | Items in | Kept | On-topic kept | On-topic dropped |
|---|---|---|---|---|
Snowflake, context regex |
100 | 8 | 5 | 3 of 8 |
Snowflake, context regex, 2 min later |
78 | 5 | 3 | 0 of 3 |
Snowflake, no context regex |
100 | 34 | 7 | 1 of 8 |
Databricks |
58 | 31 | 28 | 1 of 29 |
MongoDB |
35 | 3 | 3 | 2 of 5 |
It keeps most of the on-topic items, and the drops are the price. Without the context regex, 21 namesakes got through. For MongoDB it dropped two on-topic items. One came from a publisher on the deny-list and one matched a market word in its headline, and a rule list tuned on one name will need retuning for the next.
What we did not test
- Other days, other IPs, other editions or non-English feeds.
- Recall against the world. We only compared feeds with each other.
- Google Alerts on these companies. See setting up Google Alerts and the Alerts RSS feed.
- Paid monitoring tools, sentiment, or how soon a story reaches the feed. The same method applied to regulators, with a measured coverage delay for SEC releases, is in regulatory news monitoring with RSS and a webhook.
Where Feedmarkdown fits
A Feedmarkdown Google News source takes a query string and passes it to Google. We have not run these queries through it, so we promise nothing about what comes back, and it does not filter job posts, market pages or namesakes. It does fetch and resolve publisher URLs, drop duplicates across sources before scraping, and return full-text Markdown by HMAC-signed webhook or as a JSON Feed 1.1 or Atom pull. Starter is $19 a month for 10 sources checked every 6 hours, Pro is $49 for 50 sources checked hourly, and Agency is $149 for 200 sources checked every 15 minutes. See the home page and the pricing section.