Monitor Google News
Set up a brand or keyword news alert pipeline that collects and deduplicates Google News articles in real time using the Autom API.
Overview
This playbook builds a news monitoring pipeline: query Google News for a brand name, product, or topic and collect all article titles, sources, and publication dates. Run it on a cron schedule to detect press coverage as soon as it appears.
Prerequisites
- An Autom API key — get one at app.autom.dev
- Install dependencies for your language:
pip install requestsNo extra dependencies — uses the native fetch API (Node 18+).
curl extension enabled (on by default in most PHP installs).
No extra dependencies — uses the net/http standard library (Go 1.18+).
No extra dependencies — uses java.net.http (Java 11+).
No extra dependencies — uses System.Net.Http (.NET 6+).
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"Steps
Fetch latest news articles
Call GET /v1/google/news with the keyword you want to monitor.
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.autom.dev/v1/google/news",
headers={"x-api-key": API_KEY},
params={"query": "OpenAI", "gl": "us", "hl": "en"},
)
data = response.json()const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({ q: "OpenAI", gl: "us", hl: "en" });
const response = await fetch(`https://api.autom.dev/v1/google/news?${params}`, {
headers: { "x-api-key": API_KEY },
});
const data = await response.json();<?php
$apiKey = "YOUR_API_KEY";
$params = http_build_query(["query" => "OpenAI", "gl" => "us", "hl" => "en"]);
$ch = curl_init("https://api.autom.dev/v1/google/news?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);package main
import (
"encoding/json"
"io"
"net/http"
"net/url"
)
func main() {
params := url.Values{"query": {"OpenAI"}, "gl": {"us"}, "hl": {"en"}}
req, _ := http.NewRequest("GET", "https://api.autom.dev/v1/google/news?"+params.Encode(), nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]any
json.Unmarshal(body, &data)
// use data below
}import java.net.URI;
import java.net.http.*;
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.autom.dev/v1/google/news?q=OpenAI&gl=us&hl=en"))
.header("x-api-key", "YOUR_API_KEY")
.GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var body = await client.GetStringAsync(
"https://api.autom.dev/v1/google/news?q=OpenAI&gl=us&hl=en");
Console.WriteLine(body);#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let data = reqwest::Client::new()
.get("https://api.autom.dev/v1/google/news")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("query", "OpenAI"), ("gl", "us"), ("hl", "en")])
.send().await?
.json::<serde_json::Value>().await?;
println!("{:#?}", data);
Ok(())
}Extract and display articles
Each item in organic_results has title, link, source, date, and snippet.
for article in data.get("organic_results", []):
print(f"[{article['date']}] {article['title']}")
print(f" Source : {article['source']}")
print(f" URL : {article['link']}\n")for (const article of data.organic_results ?? []) {
console.log(`[${article.date}] ${article.title}`);
console.log(` Source : ${article.source}`);
console.log(` URL : ${article.link}\n`);
}foreach ($data["organic_results"] ?? [] as $article) {
echo "[{$article['date']}] {$article['title']}\n";
echo " Source : {$article['source']}\n";
echo " URL : {$article['link']}\n\n";
}results := data["organic_results"].([]any)
for _, r := range results {
a := r.(map[string]any)
fmt.Printf("[%s] %s\n Source : %s\n URL : %s\n\n",
a["date"], a["title"], a["source"], a["link"])
}import org.json.*;
var json = new JSONObject(response.body());
var results = json.getJSONArray("organic_results");
for (int i = 0; i < results.length(); i++) {
var a = results.getJSONObject(i);
System.out.printf("[%s] %s%n Source : %s%n URL : %s%n%n",
a.getString("date"), a.getString("title"),
a.getString("source"), a.getString("link"));
}using System.Text.Json;
var json = JsonDocument.Parse(body);
var results = json.RootElement.GetProperty("organic_results").EnumerateArray();
foreach (var a in results)
{
Console.WriteLine($"[{a.GetProperty("date")}] {a.GetProperty("title")}");
Console.WriteLine($" Source : {a.GetProperty("source")}");
Console.WriteLine($" URL : {a.GetProperty("link")}\n");
}if let Some(articles) = data["organic_results"].as_array() {
for a in articles {
println!("[{}] {}", a["date"].as_str().unwrap_or(""), a["title"].as_str().unwrap_or(""));
println!(" Source : {}", a["source"].as_str().unwrap_or(""));
println!(" URL : {}\n", a["link"].as_str().unwrap_or(""));
}
}Build a monitoring pipeline with deduplication
Store seen article URLs so repeated runs don't produce duplicate alerts.
import json, requests
from pathlib import Path
API_KEY = "YOUR_API_KEY"
KEYWORDS = ["OpenAI", "Anthropic", "Mistral AI"]
SEEN_FILE = Path("seen_articles.json")
def load_seen() -> set:
return set(json.loads(SEEN_FILE.read_text())) if SEEN_FILE.exists() else set()
def save_seen(seen: set) -> None:
SEEN_FILE.write_text(json.dumps(list(seen)))
def fetch_news(query: str) -> list:
r = requests.get("https://api.autom.dev/v1/google/news",
headers={"x-api-key": API_KEY}, params={"query": query, "gl": "us", "hl": "en"})
return r.json().get("organic_results", [])
seen = load_seen()
new_articles = []
for keyword in KEYWORDS:
for article in fetch_news(keyword):
if article["link"] not in seen:
seen.add(article["link"])
new_articles.append({**article, "keyword": keyword})
save_seen(seen)
print(f"Found {len(new_articles)} new article(s):")
for a in new_articles:
print(f" [{a['keyword']}] {a['title']} — {a['source']}")import { readFileSync, writeFileSync, existsSync } from "fs";
const API_KEY = "YOUR_API_KEY";
const KEYWORDS = ["OpenAI", "Anthropic", "Mistral AI"];
const SEEN_FILE = "seen_articles.json";
function loadSeen(): Set<string> {
return existsSync(SEEN_FILE)
? new Set(JSON.parse(readFileSync(SEEN_FILE, "utf-8")))
: new Set();
}
async function fetchNews(query: string): Promise<any[]> {
const params = new URLSearchParams({ q: query, gl: "us", hl: "en" });
const res = await fetch(`https://api.autom.dev/v1/google/news?${params}`, {
headers: { "x-api-key": API_KEY },
});
return (await res.json()).organic_results ?? [];
}
const seen = loadSeen();
const newArticles: any[] = [];
for (const keyword of KEYWORDS) {
for (const article of await fetchNews(keyword)) {
if (!seen.has(article.link)) {
seen.add(article.link);
newArticles.push({ ...article, keyword });
}
}
}
writeFileSync(SEEN_FILE, JSON.stringify([...seen]));
console.log(`Found ${newArticles.length} new article(s):`);
for (const a of newArticles) console.log(` [${a.keyword}] ${a.title} — ${a.source}`);<?php
$apiKey = "YOUR_API_KEY";
$keywords = ["OpenAI", "Anthropic", "Mistral AI"];
$seenFile = "seen_articles.json";
$seen = file_exists($seenFile) ? array_flip(json_decode(file_get_contents($seenFile), true)) : [];
$newArticles = [];
foreach ($keywords as $keyword) {
$params = http_build_query(["query" => $keyword, "gl" => "us", "hl" => "en"]);
$ch = curl_init("https://api.autom.dev/v1/google/news?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($data["organic_results"] ?? [] as $article) {
if (!isset($seen[$article["link"]])) {
$seen[$article["link"]] = true;
$newArticles[] = array_merge($article, ["keyword" => $keyword]);
}
}
}
file_put_contents($seenFile, json_encode(array_keys($seen)));
echo "Found " . count($newArticles) . " new article(s):\n";
foreach ($newArticles as $a) echo " [{$a['keyword']}] {$a['title']} — {$a['source']}\n";package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
)
func fetchNews(apiKey, query string) []map[string]any {
params := url.Values{"query": {query}, "gl": {"us"}, "hl": {"en"}}
req, _ := http.NewRequest("GET", "https://api.autom.dev/v1/google/news?"+params.Encode(), nil)
req.Header.Set("x-api-key", apiKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]any
json.Unmarshal(body, &data)
var out []map[string]any
for _, r := range data["organic_results"].([]any) {
out = append(out, r.(map[string]any))
}
return out
}
func main() {
apiKey := "YOUR_API_KEY"
keywords := []string{"OpenAI", "Anthropic", "Mistral AI"}
seen := map[string]bool{}
if b, err := os.ReadFile("seen_articles.json"); err == nil {
var links []string
json.Unmarshal(b, &links)
for _, l := range links { seen[l] = true }
}
var newCount int
for _, kw := range keywords {
for _, a := range fetchNews(apiKey, kw) {
link := a["link"].(string)
if !seen[link] {
seen[link] = true
fmt.Printf(" [%s] %s — %s\n", kw, a["title"], a["source"])
newCount++
}
}
}
links := make([]string, 0, len(seen))
for l := range seen { links = append(links, l) }
b, _ := json.Marshal(links)
os.WriteFile("seen_articles.json", b, 0644)
fmt.Printf("Found %d new article(s).\n", newCount)
}import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import org.json.*;
public class Main {
static HttpClient client = HttpClient.newHttpClient();
static String API_KEY = "YOUR_API_KEY";
public static void main(String[] args) throws Exception {
var keywords = List.of("OpenAI", "Anthropic", "Mistral AI");
var seenPath = Path.of("seen_articles.json");
var seen = new HashSet<String>();
if (Files.exists(seenPath)) {
var arr = new JSONArray(Files.readString(seenPath));
for (int i = 0; i < arr.length(); i++) seen.add(arr.getString(i));
}
int newCount = 0;
for (var kw : keywords) {
var q = URLEncoder.encode(kw, StandardCharsets.UTF_8);
var url = "https://api.autom.dev/v1/google/news?q=" + q + "&gl=us&hl=en";
var req = HttpRequest.newBuilder().uri(URI.create(url))
.header("x-api-key", API_KEY).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
var results = new JSONObject(resp.body()).getJSONArray("organic_results");
for (int i = 0; i < results.length(); i++) {
var a = results.getJSONObject(i);
var link = a.getString("link");
if (!seen.contains(link)) {
seen.add(link);
System.out.printf(" [%s] %s — %s%n", kw, a.getString("title"), a.getString("source"));
newCount++;
}
}
}
Files.writeString(seenPath, new JSONArray(seen).toString());
System.out.println("Found " + newCount + " new article(s).");
}
}using System.Net.Http;
using System.Text.Json;
var apiKey = "YOUR_API_KEY";
var keywords = new[] { "OpenAI", "Anthropic", "Mistral AI" };
var seenFile = "seen_articles.json";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
var seen = File.Exists(seenFile)
? JsonSerializer.Deserialize<HashSet<string>>(File.ReadAllText(seenFile))!
: new HashSet<string>();
int newCount = 0;
foreach (var keyword in keywords)
{
var url = $"https://api.autom.dev/v1/google/news?q={Uri.EscapeDataString(keyword)}&gl=us&hl=en";
var body = await client.GetStringAsync(url);
var json = JsonDocument.Parse(body).RootElement;
foreach (var a in json.GetProperty("organic_results").EnumerateArray())
{
var link = a.GetProperty("link").GetString()!;
if (seen.Add(link))
{
Console.WriteLine($" [{keyword}] {a.GetProperty("title")} — {a.GetProperty("source")}");
newCount++;
}
}
}
File.WriteAllText(seenFile, JsonSerializer.Serialize(seen));
Console.WriteLine($"Found {newCount} new article(s).");use reqwest::Client;
use serde_json::Value;
use std::{collections::HashSet, fs, path::Path};
async fn fetch_news(client: &Client, api_key: &str, query: &str) -> Vec<Value> {
let data = client
.get("https://api.autom.dev/v1/google/news")
.header("x-api-key", api_key)
.query(&[("query", query), ("gl", "us"), ("hl", "en")])
.send().await.unwrap().json::<Value>().await.unwrap();
data["organic_results"].as_array().cloned().unwrap_or_default()
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let api_key = "YOUR_API_KEY";
let keywords = ["OpenAI", "Anthropic", "Mistral AI"];
let seen_path = Path::new("seen_articles.json");
let mut seen: HashSet<String> = if seen_path.exists() {
serde_json::from_str::<Vec<String>>(&fs::read_to_string(seen_path).unwrap())
.unwrap().into_iter().collect()
} else { HashSet::new() };
let mut new_count = 0;
for keyword in &keywords {
for article in fetch_news(&client, api_key, keyword).await {
let link = article["link"].as_str().unwrap_or("").to_string();
if seen.insert(link) {
println!(" [{}] {} — {}", keyword, article["title"].as_str().unwrap_or(""), article["source"].as_str().unwrap_or(""));
new_count += 1;
}
}
}
let seen_vec: Vec<&String> = seen.iter().collect();
fs::write(seen_path, serde_json::to_string(&seen_vec).unwrap()).unwrap();
println!("Found {new_count} new article(s).");
Ok(())
}Schedule this script with a cron job (e.g. every hour) or a task scheduler to receive continuous coverage monitoring. Combine multiple keywords in one run to minimize credit usage.
Scrape Google Search Results
Build a SERP monitoring script that tracks keyword rankings and extracts organic results using the Autom API.
Collect Google Images
Download and catalog image URLs for any search query using the Autom API — useful for dataset building, visual research, or competitor asset audits.