AutomationFlowsWeb Scraping › Track Competitor Pricing Changes with Google Sheets, Openai, and Gmail

Track Competitor Pricing Changes with Google Sheets, Openai, and Gmail

ByYasutaka Toshima @yasutaka on n8n.io

This workflow runs daily, checks competitor webpages listed in Google Sheets, compares the latest page text to the previous snapshot using OpenAI, stores the updated snapshot in Google Sheets, and emails a consolidated HTML change report via Gmail. Runs every day at 9:00 AM on a…

Cron / scheduled trigger★★★★☆ complexityAI-powered16 nodesGoogle SheetsHTTP RequestHtml ExtractOpenAIGmail
Web Scraping Trigger: Cron / scheduled Nodes: 16 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #17227 — we link there as the canonical source.

This workflow follows the Gmail → Google Sheets recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "YFsxtTA9Y4HsGYZS",
  "name": "Competitor Price and Campaign Change Tracker with Google Sheets and Gmail and Open AI",
  "tags": [],
  "nodes": [
    {
      "id": "5f9e9a29-0d50-410e-bcdc-9d1749e904e9",
      "name": "Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -48
      ],
      "parameters": {
        "width": 560,
        "height": 784,
        "content": "## \ud83d\udcca Competitor Price & Campaign Change Tracker\n\nThis workflow monitors competitor websites every day and emails you an AI-generated summary of any pricing, plan, or campaign changes.\n\n## How it works\n1. Runs automatically every day at 9 AM\n2. Reads your list of competitor websites from Google Sheets\n3. Fetches each website's HTML and extracts the page text\n4. Loads the previously saved page text from Google Sheets\n5. Uses OpenAI (gpt-4o-mini) to compare both versions and summarize pricing, plan, and campaign changes\n6. Saves the latest page text back to Google Sheets as the new snapshot\n7. Sends a daily HTML report with all detected changes via Gmail\n\n## Setup steps\n1. Create a Google Sheet with a \"Monitored Sites\" tab (columns: Site ID, Site Name, URL) and a \"Snapshots\" tab (columns: Competitor Site ID, Last Fetched At, Page Text)\n2. Add your Google Sheets ID, sheet tab names, and report email address in the Config node\n3. Connect your Google Sheets, OpenAI, and Gmail credentials\n4. Adjust the schedule in the trigger node if needed\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9585cf9b-bc38-40e9-91be-60b38928d62a",
      "name": "Step 1 - Trigger & Config",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        656,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 752,
        "content": "## Trigger & Configuration\nStarts the workflow on a daily schedule and loads all user-configurable settings.\n\n**Daily 9 AM Trigger**: Runs the workflow every day at 9 AM\n**Config**: Stores your Google Sheets ID, sheet tab names, and report email address"
      },
      "typeVersion": 1
    },
    {
      "id": "0d8ad504-02a5-4678-9a0e-04495517758c",
      "name": "Step 2 - Fetch Content",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1104,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 752,
        "content": "## Fetch Website Content\nReads the list of competitor websites from Google Sheets, then fetches each site's HTML and extracts the visible page text for comparison."
      },
      "typeVersion": 1
    },
    {
      "id": "8a90c7f7-780a-4bc0-8e57-8181b9e88620",
      "name": "Step 3 - AI Detection",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1776,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 752,
        "content": "## AI Change Detection\nLoads the previously saved page text and uses OpenAI to:\n- Compare the previous and current page text\n- Summarize pricing, plan, and campaign changes as bullet points\n- Reply \"No changes detected\" when nothing has changed\n- Format each summary into an HTML table row for the report"
      },
      "typeVersion": 1
    },
    {
      "id": "8ced5718-9fa0-4b91-8e5b-75b155b67774",
      "name": "Step 4 - Save & Report",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2448,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 752,
        "content": "## Save Snapshot & Send Report\nSaves the latest page text to Google Sheets as the new snapshot, aggregates all site summaries into one HTML table, and emails the daily report via Gmail."
      },
      "typeVersion": 1
    },
    {
      "id": "495e82df-64e5-4012-a5d6-968f5edacbff",
      "name": "Daily 9 AM Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        720,
        416
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 9 * * *"
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "777c0a52-9277-4fe3-903c-2ea3636e7846",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "position": [
        944,
        416
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "fbfebfa1-fdec-400d-bfcd-e019f97d33b7",
              "name": "GOOGLE_SHEETS_ID",
              "type": "string",
              "value": "YOUR_GOOGLE_SHEETS_ID_HERE"
            },
            {
              "id": "2492d8c5-aaa4-40f2-89fd-ee1b9df3467c",
              "name": "MONITORED_SITES_SHEET_NAME",
              "type": "string",
              "value": "Monitored Sites"
            },
            {
              "id": "6e3d33f9-b87d-41a9-8890-b6cd83046516",
              "name": "SNAPSHOTS_SHEET_NAME",
              "type": "string",
              "value": "Snapshots"
            },
            {
              "id": "f4a7a24c-1e8a-4789-8b7f-c252be96004a",
              "name": "EMAIL_ADDRESS",
              "type": "string",
              "value": "YOUR_EMAIL_ADDRESS_HERE"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f0f7859f-4b1f-4e06-b554-cd9ac776e6c7",
      "name": "Read Monitored Sites",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1152,
        416
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.MONITORED_SITES_SHEET_NAME }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.GOOGLE_SHEETS_ID }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "4e97c7c2-1edb-410f-b566-5c257813de5e",
      "name": "Fetch Website HTML",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1360,
        416
      ],
      "parameters": {
        "url": "={{ $json['URL'] }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "a4181df0-9fc0-4d5d-bee1-2e9d590cbfff",
      "name": "Extract Page Text",
      "type": "n8n-nodes-base.htmlExtract",
      "position": [
        1584,
        416
      ],
      "parameters": {
        "options": {},
        "extractionValues": {
          "values": [
            {
              "key": "pageText",
              "cssSelector": "body"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "91072338-41f9-4d83-8bf4-7a9d304b77d5",
      "name": "Read Previous Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1808,
        416
      ],
      "parameters": {
        "options": {
          "returnFirstMatch": true
        },
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('Read Monitored Sites').item.json['Site ID'] }}",
              "lookupColumn": "Competitor Site ID"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Config').item.json.SNAPSHOTS_SHEET_NAME }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config').item.json.GOOGLE_SHEETS_ID }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "447f7d64-6d4e-4b31-b2ad-12265ddd1309",
      "name": "Summarize Changes with AI",
      "type": "n8n-nodes-base.openAi",
      "position": [
        2016,
        416
      ],
      "parameters": {
        "prompt": {
          "messages": [
            {
              "role": "system",
              "content": "You are an AI assistant that detects pricing and campaign changes on competitor websites. Compare the previously captured page text with the newly captured page text, and summarize any changes related to pricing, prices, campaigns, or plans as concise bullet points. If nothing has changed, reply with only \"No changes detected\"."
            },
            {
              "content": "=[Monitored Site] {{ $('Read Monitored Sites').item.json['Site Name'] }}\n\n[Previous Page Text]\n{{ ($('Read Previous Data').item.json['Page Text'] ?? '(No previous data - this is the first run)').slice(0, 10000) }}\n\n[Current Page Text]\n{{ $('Extract Page Text').item.json.pageText.slice(0, 10000) }}"
            }
          ]
        },
        "options": {
          "maxTokens": 1000,
          "temperature": 0.3
        },
        "resource": "chat",
        "chatModel": "gpt-4o-mini",
        "requestOptions": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "68c06247-2977-4f25-9683-8c1c0979dec5",
      "name": "Format Results",
      "type": "n8n-nodes-base.set",
      "position": [
        2240,
        416
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "html-row-field",
              "name": "htmlRow",
              "type": "string",
              "value": "=<tr style=\"border-bottom:1px solid #ddd;\"><td style=\"padding:10px 14px;font-weight:bold;min-width:120px;\">{{ $('Read Monitored Sites').item.json['Site Name'] }}</td><td style=\"padding:10px 14px;white-space:pre-wrap;\">{{ $json.message.content }}</td></tr>"
            },
            {
              "id": "site-id-field",
              "name": "siteId",
              "type": "string",
              "value": "={{ $('Read Monitored Sites').item.json['Site ID'] }}"
            },
            {
              "id": "page-text-field",
              "name": "pageText",
              "type": "string",
              "value": "={{ $('Extract Page Text').item.json.pageText.slice(0, 40000) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "74b69576-09c8-41c0-a54d-84f36ce3aef2",
      "name": "Save Current Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2512,
        416
      ],
      "parameters": {
        "columns": {
          "value": {
            "Page Text": "={{ $json.pageText }}",
            "Last Fetched At": "={{ $now.toISO() }}",
            "Competitor Site ID": "={{ $json.siteId }}"
          },
          "schema": [
            {
              "id": "Competitor Site ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Competitor Site ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Last Fetched At",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Last Fetched At",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "Page Text",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Page Text",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Competitor Site ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Config').item.json.SNAPSHOTS_SHEET_NAME }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config').item.json.GOOGLE_SHEETS_ID }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "c2ea9862-0ae4-4327-a92a-b94171176a45",
      "name": "Aggregate Results",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        2496,
        256
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "htmlRow"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "60fb3f0f-33dd-48bc-9b78-cc95b0f6c9d2",
      "name": "Send Email Report",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2720,
        256
      ],
      "parameters": {
        "sendTo": "={{ $('Config').item.json.EMAIL_ADDRESS }}",
        "message": "=<div style=\"font-family:sans-serif;max-width:700px;\"><h2 style=\"color:#333;border-bottom:2px solid #4a90d9;padding-bottom:8px;\">\ud83d\udcca Competitor Site Daily Monitoring Report</h2><p><strong>Checked At:</strong> {{ $now.format('MMMM dd, yyyy HH:mm') }}</p><table style=\"border-collapse:collapse;width:100%;\"><thead><tr style=\"background:#4a90d9;color:white;\"><th style=\"padding:10px 14px;text-align:left;\">Site Name</th><th style=\"padding:10px 14px;text-align:left;\">Changes Detected</th></tr></thead><tbody>{{ $json.htmlRow.join('') }}</tbody></table><p style=\"color:#888;font-size:12px;margin-top:16px;\">This email was sent automatically by n8n.</p></div>",
        "options": {},
        "subject": "={{ '[Competitor Monitoring Report] Daily Check Results - ' + $now.format('MMMM dd, yyyy') }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "29ae0e3a-397e-4785-9467-8806522d3e54",
  "nodeGroups": [],
  "connections": {
    "Config": {
      "main": [
        [
          {
            "node": "Read Monitored Sites",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Results": {
      "main": [
        [
          {
            "node": "Save Current Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Aggregate Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Results": {
      "main": [
        [
          {
            "node": "Send Email Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Page Text": {
      "main": [
        [
          {
            "node": "Read Previous Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily 9 AM Trigger": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Website HTML": {
      "main": [
        [
          {
            "node": "Extract Page Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Previous Data": {
      "main": [
        [
          {
            "node": "Summarize Changes with AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Monitored Sites": {
      "main": [
        [
          {
            "node": "Fetch Website HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Summarize Changes with AI": {
      "main": [
        [
          {
            "node": "Format Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow runs daily, checks competitor webpages listed in Google Sheets, compares the latest page text to the previous snapshot using OpenAI, stores the updated snapshot in Google Sheets, and emails a consolidated HTML change report via Gmail. Runs every day at 9:00 AM on a…

Source: https://n8n.io/workflows/17227/ — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

This template can be used to find the content gaps in your competitors' discourse: identifying the topics they are not yet connecting and giving you an opportunity to fill in this gap with your conten

HTTP Request, Html Extract, Google Docs +3
Web Scraping

Simplify employee performance reviews with AI-powered automation. This workflow transforms raw feedback and evaluation inputs into clear, structured, and professional performance review summaries — sa

OpenAI, Gmail, HTTP Request +3
Web Scraping

Wait Code. Uses manualTrigger, httpRequest, htmlExtract, openAi. Event-driven trigger; 11 nodes.

HTTP Request, Html Extract, OpenAI +1
Web Scraping

Enrich your company lists with OpenAI GPT-3 ↓

HTTP Request, Html Extract, OpenAI +1
Web Scraping

Httprequest Workflow. Uses httpRequest, htmlExtract, gmail. Scheduled trigger; 15 nodes.

HTTP Request, Html Extract, Gmail