PR Tools
February 20, 2026
8 min read
Alan Wallace

Build a One-Click Google News Monitor in Under 30 Minutes

A step-by-step guide to building a free, automated Google News monitoring system using Google Sheets and Apps Script. No paid tools required - get daily email digests of news mentions for any keyword or client.

Share:
Build a One-Click Google News Monitor in Under 30 Minutes

Most PR monitoring tools cost $200-$500 per month. For smaller agencies and in-house teams managing a handful of clients, that spend is hard to justify when Google News RSS feeds deliver the same core data for free. Here's how to build a working monitor in under 30 minutes using Google Sheets and Apps Script.

What You'll Build

A Google Sheet that automatically fetches Google News results for any keyword or phrase, logs new articles with title, source, date, and URL, and sends you a daily email digest of new mentions. Once set up, it runs on autopilot with zero ongoing maintenance.

What you need: A Google account. That's it. No API keys, no paid subscriptions, no coding background required.

Step 1 - Set Up Your Google Sheet

  1. Open Google Sheets and create a new spreadsheet. Name it something clear like "News Monitor - [Client Name]".
  2. In Row 1, create these column headers: Date, Title, Source, URL, Keyword.
  3. Create a second tab named "Keywords" and list each keyword or phrase you want to monitor in column A, one per row. Examples: your client's company name, product name, CEO name, key competitors.

Step 2 - Open Apps Script

  1. From your Google Sheet, click Extensions in the top menu, then Apps Script.
  2. A new browser tab opens with the Apps Script editor. Delete any existing code in the editor.

Step 3 - Paste the Script

Copy and paste the following code into the Apps Script editor:

function fetchGoogleNews() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const dataSheet = ss.getSheetByName('Sheet1');
  const keywordSheet = ss.getSheetByName('Keywords');
  
  // Get all keywords from the Keywords tab
  const keywords = keywordSheet.getRange('A:A')
    .getValues()
    .flat()
    .filter(k => k !== '');
  
  // Get existing URLs to avoid duplicates
  const existingData = dataSheet.getDataRange().getValues();
  const existingUrls = existingData.map(row => row[3]).slice(1);
  
  const newRows = [];
  
  keywords.forEach(keyword => {
    const encodedKeyword = encodeURIComponent(keyword);
    const rssUrl = `https://news.google.com/rss/search?q=${encodedKeyword}&hl=en-US&gl=US&ceid=US:en`;
    
    try {
      const response = UrlFetchApp.fetch(rssUrl);
      const xml = response.getContentText();
      const document = XmlService.parse(xml);
      const items = document.getRootElement()
        .getChild('channel')
        .getChildren('item');
      
      items.forEach(item => {
        const title = item.getChildText('title');
        const link = item.getChildText('link');
        const pubDate = item.getChildText('pubDate');
        const source = item.getChild('source') 
          ? item.getChild('source').getText() 
          : 'Unknown';
        
        if (!existingUrls.includes(link)) {
          newRows.push([
            new Date(pubDate),
            title,
            source,
            link,
            keyword
          ]);
          existingUrls.push(link);
        }
      });
    } catch (e) {
      Logger.log(`Error fetching ${keyword}: ${e.message}`);
    }
  });
  
  if (newRows.length > 0) {
    // Sort by date descending
    newRows.sort((a, b) => b[0] - a[0]);
    
    // Append to sheet
    const lastRow = dataSheet.getLastRow();
    dataSheet.getRange(lastRow + 1, 1, newRows.length, 5)
      .setValues(newRows);
    
    // Send email digest
    sendDigest(newRows);
  }
}

function sendDigest(newRows) {
  const email = Session.getActiveUser().getEmail();
  const subject = `News Monitor: ${newRows.length} new article(s) - ${new Date().toDateString()}`;
  
  let body = `<h2>New articles found</h2>`;
  
  newRows.forEach(row => {
    const [date, title, source, url, keyword] = row;
    body += `
      <div style="margin-bottom:16px; padding:12px; border-left:3px solid #1a73e8;">
        <strong><a href="${url}">${title}</a></strong><br>
        <span style="color:#666;">${source} - ${date.toDateString()}</span><br>
        <span style="color:#888; font-size:12px;">Keyword: ${keyword}</span>
      </div>
    `;
  });
  
  MailApp.sendEmail({
    to: email,
    subject: subject,
    htmlBody: body
  });
}

Step 4 - Set Up the Daily Trigger

  1. In the Apps Script editor, click the clock icon in the left sidebar (Triggers).
  2. Click + Add Trigger in the bottom right.
  3. Set: Function to run = fetchGoogleNews, Event source = Time-driven, Type = Day timer, Time = 7am-8am.
  4. Click Save. Authorize the script when prompted (it needs permission to fetch URLs and send email).

Step 5 - Test It

Click the Run button (play icon) in the Apps Script editor to run the function manually. Check your Google Sheet - new articles should appear within 30 seconds. Check your email - you should receive the digest immediately.

Tip: If you get a "quota exceeded" error, you've hit Google's daily URL fetch limit (20,000 requests). Reduce your keyword list or switch to an every-other-day trigger. For most PR use cases, 10-20 keywords running daily is well within limits.

Customizations Worth Making

  • Multiple clients:Create a separate tab for each client's keywords. Copy the script and adjust the sheet name references. Run all monitors from one spreadsheet.
  • Competitor tracking:Add competitor names and product names to your keyword list. You'll see their coverage alongside your client's.
  • Conditional formatting:Highlight rows from tier-1 outlets (WSJ, NYT, TechCrunch, etc.) using a formula that checks if the Source column contains those names.
  • Slack notifications:Replace the email function with a Slack webhook call to push new articles directly to a client channel. Requires a free Slack incoming webhook setup.

Limitations to Know

Google News RSS does not include every article - it surfaces the top results for each keyword, typically 10-20 per query. For comprehensive coverage of a high-volume topic, you'll still want a paid tool like Meltwater, Cision, or Mention. But for most PR monitoring needs - tracking a client's name, product launches, and key competitors - this covers 80% of what you need at zero cost.

The script also won't catch paywalled articles (it logs the URL but you may not be able to read the full text). And Google occasionally changes its RSS feed structure, which can break the XML parsing. If articles stop appearing, check the Apps Script logs for errors.

This is part of PressContact's Shared Resources series - practical tools and templates I actually use in client work. If you build on this or have questions about the setup, reach out directly.