---
title: "Monitor Competitor Prices & Get AI Alerts with n8n"
description: "Build an n8n price monitoring workflow that checks competitor prices on a schedule, uses AI to interpret changes, and sends plain-English alerts. No API key needed."
canonical: https://agentroost.app/en/blog/competitor-price-monitor-ai-alerts-n8n
date: 2026-06-07T04:00:00Z
---

[Canonical URL](https://agentroost.app/en/blog/competitor-price-monitor-ai-alerts-n8n)

## The Problem with Manual Price Checking

Checking three competitor URLs every morning sounds manageable. Then you add a fourth product line, a second market, and a weekend shift. Within a month you're either paying for an expensive SaaS repricing tool or silently losing margin because you missed a price drop on a Tuesday.

An n8n workflow fixes this for pennies. You define the URLs once, tell n8n when to check them, and let an AI node do the interpretation — not just "price changed" but "Competitor B dropped their Pro plan 18% the day before Black Friday; consider matching."

This guide walks through the exact nodes, a real config, and how to run it on your own always-on n8n instance that already has AI credits included.

---

## What You Will Build

A scheduled workflow that:

1. Pulls competitor pricing pages on a fixed schedule (every hour, or daily at 08:00).
2. Extracts the relevant price from each page with an HTTP Request + HTML Extract node.
3. Stores the latest known price in a Google Sheet so you have a comparison baseline.
4. Passes the old price, new price, and product name to an AI/LLM node.
5. The AI node writes a plain-English summary of what changed and whether it looks significant.
6. Sends the summary to Slack, email, or a Telegram chat — only when something actually changed.

---

## The Node-by-Node Build

### 1. Schedule Trigger

Add a **Schedule Trigger** node. Set it to `Every Hour` (or `Every Day` at `08:00 UTC`). This is the entry point — n8n wakes up on its own, no cron server needed.

```
Trigger mode: Interval
Every: 1
Unit: Hours
```

### 2. Code Node — Define Targets

Use a **Code** node (JavaScript) to return an array of competitor URLs and product identifiers. Keeping targets here (not in a spreadsheet) makes the workflow readable.

```js
return [
  { competitor: "AcmePro", url: "https://acmepro.io/pricing", selector: ".price-hero .amount" },
  { competitor: "BoltSaaS", url: "https://boltsaas.com/plans", selector: "#monthly-price" },
  { competitor: "ZenTool",  url: "https://zentool.co/pricing", selector: "[data-price]" },
];
```

### 3. Split In Batches

Drop a **Split In Batches** node after the Code node with `Batch Size: 1`. This lets the rest of the workflow run once per competitor — clean and easy to debug.

### 4. HTTP Request — Fetch the Page

Add an **HTTP Request** node:

- **Method**: GET
- **URL**: `{{ $json.url }}`
- **Response Format**: Text

No auth needed for public pricing pages. If a site blocks the default User-Agent, add a header: `User-Agent: Mozilla/5.0 (compatible; PriceBot/1.0)`.

### 5. HTML Extract — Pull the Price

Add an **HTML Extract** node:

- **Source Data**: `{{ $json.data }}` (the raw HTML from step 4)
- **Extraction Values**:
  - **Key**: `price`
  - **CSS Selector**: `{{ $('Split In Batches').item.json.selector }}`
  - **Return Value**: Text

This gives you `{ price: "$49" }` (or whatever text is in the element).

> **Tip:** Always clean the extracted string with a **Set** node: `{{ $json.price.replace(/[^0-9.]/g, '') }}`. Stripping currency symbols lets you do numeric comparisons later.

### 6. Google Sheets — Read Last Known Price

Add a **Google Sheets** node in **Read** mode. Keep one sheet with columns: `competitor`, `lastPrice`, `lastChecked`.

- **Operation**: Read Rows
- **Filter**: Column `competitor` equals `{{ $('Split In Batches').item.json.competitor }}`

This returns the stored baseline. If no row exists yet (first run), the output will be empty — handle that with an **IF** node downstream (treat "no row" as "price is new").

### 7. IF Node — Did the Price Change?

Add an **IF** node with one condition:

```
{{ $('HTML Extract').item.json.priceClean }} != {{ $('Google Sheets').item.json.lastPrice }}
```

- **True branch** → send to AI node + alert.
- **False branch** → Stop (no noise, no alert).

### 8. AI / LLM Node — Interpret the Change

This is where the magic happens. Add an **AI** node (the built-in LLM node in n8n) and set the **System Prompt**:

```
You are a pricing analyst. You receive a competitor name, their old price, and their new price.
Write a 2–3 sentence plain-English summary: what changed, the percentage difference, and one
actionable observation (e.g., "this may signal a promotional push" or "within normal A/B range").
Be direct. No fluff.
```

**User message**:

```
Competitor: {{ $('Split In Batches').item.json.competitor }}
Old price: ${{ $('Google Sheets').item.json.lastPrice }}
New price: ${{ $('HTML Extract').item.json.priceClean }}
```

On AgentRoost, this node works immediately — no OpenAI key to paste, no billing page to configure. The AI credits are bundled into your plan.

### 9. Google Sheets — Write Updated Price

Add another **Google Sheets** node in **Update** mode to overwrite the row for this competitor with the new price and a timestamp. Now next hour's run will use today's price as the baseline.

### 10. Slack (or Email / Telegram)

Add a **Slack** node or **Send Email** node:

- **Channel / To**: your team channel or your inbox
- **Message**: `{{ $('AI').item.json.text }}`

If you prefer Telegram, use the **Telegram** node with your bot token and chat ID. The message body is the AI node's output — already human-readable, no template to write.

---

## Handling Edge Cases

**Site changed its HTML structure** — The selector stops matching and `price` comes back empty. Add a second IF branch: `if priceClean is empty → send "Selector broken for [competitor]" alert` so you notice before silently losing coverage.

**Price temporarily missing (429 / maintenance page)** — Wrap the HTTP Request in an **Error Trigger** or use the node's built-in retry settings (`Max Tries: 3`, `Wait Between Tries: 60s`). Only write to Sheets if the request succeeded.

**Multiple price tiers** — Run the Code node with an array that includes a `tier` field (`{ competitor: "AcmePro", tier: "Pro", selector: ".pro .amount", url: "..." }`). The IF node compares per tier, and the AI node gets the tier in its prompt.

---

## How to Run This on AgentRoost

You need an always-on instance — not a laptop you close at night, not a cron job that lapses when your VPS restarts. This is where self-hosting collapses for most people: the maintenance overhead.

On AgentRoost you get **your own n8n instance**, running 24/7, at `https://<your-id>.agentroost.app` — your login, your workflows, your data. The instance never sleeps. And the AI node in step 8 already has credits loaded; you pick from 350+ LLM models and switch anytime.

**Setup takes about 2 minutes:**

1. Sign up at [agentroost.app/en/agents/n8n](/en/agents/n8n)
2. Pick the **n8n** framework, name your instance
3. Your private n8n editor opens at `https://<your-id>.agentroost.app`
4. Import or build the workflow above — the AI node will have credits ready

No Docker. No SSL certificate. No API key to paste for the LLM. All-in from **$19.99/month** with a 14-day money-back guarantee.

[Compare plans and get started →](/en/pricing)

---

## Tips for a Production-Grade Monitor

- **Run every hour during business hours, daily at night.** Use the Schedule Trigger's advanced cron field: `0 8-18 * * 1-5` (weekdays, 08:00–18:00 UTC hourly).
- **Archive history, not just last price.** Add an extra Google Sheets **Append** node to a second "history" sheet so you can chart price trends over weeks.
- **Throttle the batch.** If you have 20+ competitors, add a **Wait** node (2–5 seconds between requests) to avoid looking like a bot storm.
- **Test selectors with a Webhook trigger first.** Create a manual Webhook node that returns a hardcoded HTML snippet to iterate on your CSS selectors without hitting live sites repeatedly.
