Get icon
Script icon
If icon
SlackIncomingWebhook icon
Set icon
Schedule icon

Nightly Competitor Pricing Monitor with Puppeteer

Orchestrate a nightly Puppeteer scrape of competitor pricing pages with Kestra. Diff against a KV Store snapshot and alert Slack only when a price actually changes.

Categories
BusinessData

Most SaaS and e-commerce pricing pages render their price tables with client-side JavaScript, so a plain HTTP GET and a regex or HTML parser sees an empty shell instead of a number. This blueprint drives an actual headless Chrome browser with Puppeteer, a Node.js library with no equivalent in Kestra's built-in HTTP or scripting tasks, to load each competitor's pricing page the way a real visitor would, wait for the price element to render, then read it straight off the DOM. It stores the scraped snapshot in Kestra's KV Store, diffs it against the previous run, and only pings Slack when a price actually moved, so the channel stays quiet on the nights nothing changed.

How it works

  1. read_previous_snapshot (io.kestra.plugin.core.kv.Get) reads the competitor_pricing_snapshot key. errorOnMissing: false lets the first run fall through to an empty list.
  2. scrape_pricing_pages (io.kestra.plugin.scripts.node.Script) runs on the ghcr.io/puppeteer/puppeteer container image, which ships Chrome preinstalled so Puppeteer never has to download a browser at runtime. beforeCommands installs the puppeteer and @kestra-io/libs npm packages, inputFiles stages the competitors input as competitors.json, and the inline script launches one browser instance, visits each competitor page in turn with page.goto and page.waitForSelector, reads the price with page.$eval, and reports the whole array back to Kestra with Kestra.outputs({ pricesJson: ... }).
  3. detect_price_changes (io.kestra.plugin.scripts.node.Script) stages both the previous and current snapshots as inputFiles, compares them by competitor name, and outputs a changeCount and a human-readable changesSummary.
  4. alert_on_price_change (io.kestra.plugin.core.flow.If) only fires notify_price_change (io.kestra.plugin.slack.notifications.SlackIncomingWebhook) when changeCount is greater than zero.
  5. update_snapshot (io.kestra.plugin.core.kv.Set) writes today's prices back to the KV Store as the baseline for tomorrow's diff.
  6. A disabled nightly Schedule trigger runs at 6 AM, and an errors block posts a separate Slack alert if the scrape or diff step fails, which usually means a competitor changed their page layout and a selector needs updating.

What you get

  • A pricing intelligence feed that reads pages exactly as a browser would, including client-rendered React, Vue, or Angular pricing tables.
  • Change-only alerting, so the team is not paged every night a competitor's price stayed flat.
  • A rolling KV-backed history of the last observed price per competitor, with no external database required to get started.
  • A clear failure signal in Slack when a selector breaks, instead of a silent scrape that quietly returns nothing.

Who it's for

  • Growth, pricing, and competitive intelligence teams who currently check competitor pages by hand.
  • Data teams asked to add "just one more scrape" to an existing pipeline without standing up a scraping service.
  • Anyone who has outgrown a cron job and a scratch script running on someone's laptop.

Why orchestrate this with Kestra

A scheduled script can call Puppeteer, but it typically has no durable state between runs, no structured retry when a single competitor's page times out, and no alerting path that distinguishes "a price changed" from "the scrape itself broke". Kestra adds the KV Store for the snapshot, a Schedule trigger you can pause without touching code, an If task that keeps notifications quiet unless something actually changed, and an errors block that alerts on scrape failures separately from price-change alerts, so the two Slack messages never get confused.

Prerequisites

  • A Kestra worker able to pull the ghcr.io/puppeteer/puppeteer Docker image (or an equivalent image with Chrome and its runtime dependencies preinstalled).
  • A Slack incoming webhook for change and failure notifications.
  • CSS selectors for each competitor's rendered price element, found via your browser's DevTools inspector.

Secrets

  • SLACK_WEBHOOK_URL: Slack incoming webhook URL used for both price-change and failure alerts.

Quick start

  1. Add the SLACK_WEBHOOK_URL secret to your Kestra namespace.
  2. Update the competitors input with your real competitor URLs and the CSS selector for each one's price element.
  3. Run the flow once manually to confirm each selector resolves and seed the initial KV snapshot.
  4. Enable the nightly trigger and let it run; watch the KV Store's competitor_pricing_snapshot key advance each night.

How to extend

  • Add a io.kestra.plugin.jdbc.postgresql.Query task after update_snapshot to log every scrape into a history table for trend charts, not just the latest snapshot.
  • Capture a page.screenshot() per competitor as visual proof alongside the extracted price, and store it via outputFiles.
  • Swap the Schedule trigger for a Flow trigger so the scrape runs on demand right before a pricing committee meeting.
  • Add a second selector per competitor for plan tier or currency, and branch the alert message accordingly.
  • Replace Puppeteer with Playwright if your team already standardizes on it; the task wiring stays the same.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.