← Back to Cookbook

Price Monitor

Monitors competitor pricing pages and alerts on changes.

firecrawlaistream

Source

/**
 * Monitors competitor pricing pages and alerts on changes.
 */

schedule check_prices {
  label: "Check Prices"
  cron: "0 6 * * *"
  timezone: "America/New_York"
}

graph monitor_prices {
  label: "Monitor Competitor Prices"

  persistence {
    enabled: true
    condition: @ts { return true }
    name: "price_history"
  }

  root {
    type: code
    label: "Start"
    code: @ts {
      return { checked_at: new Date().toISOString() }
    }
    outputSchema: @json {
      {
        "type": "object",
        "properties": {
          "checked_at": { "type": "string" }
        }
      }
    }
  }

  node scrape_competitor {
    type: firecrawl
    label: "Scrape pricing page"
    url: @ts { return "https://competitor.example.com/pricing" }
    selector: ".pricing-table"
    onlyMainContent: true
    formats: ["markdown"]
  }

  node extract_prices {
    type: ai
    label: "Extract pricing data"
    kind: object
    model: "google/gemini-2.5-flash"
    prompt: @ts {
      return "Extract all pricing tiers and their prices from this pricing page content. Return structured data.\n\n" + JSON.stringify(context.nodes.scrape_competitor.output)
    }
    schema: @json {
      {
        "type": "object",
        "required": ["tiers"],
        "properties": {
          "tiers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string" },
                "monthly_price": { "type": "number" },
                "annual_price": { "type": "number" },
                "features": { "type": "array", "items": { "type": "string" } }
              }
            }
          }
        }
      }
    }
  }

  node check_history {
    type: stream
    label: "Get previous prices"
    stream: "price_history"
    query: @sql {
      SELECT "root.checked_at" AS checked_at
      FROM {{table}}
      ORDER BY created_at DESC
      LIMIT 1
    }
  }

  node store_result {
    type: code
    label: "Store prices"
    code: @ts {
      return {
        checked_at: context.nodes.root.output.checked_at,
        tiers: context.nodes.extract_prices.output.tiers,
        tier_count: context.nodes.extract_prices.output.tiers.length
      }
    }
  }

  flow {
    root -> scrape_competitor
    scrape_competitor -> extract_prices
    root -> check_history
    extract_prices -> store_result
  }
}

trigger on_price_check {
  schedule:check_prices -> monitor_prices
  enabled: true
}

Flow

Trigger → graph

Graph nodes