Web Scraping

Build a Price Monitor with AlterLab + Supabase

A step-by-step guide to building a real-time price monitoring system using AlterLab web scraping and Supabase. Covers Edge Functions, pg_cron scheduling, structured extraction, and alert notifications, with full Python and TypeScript code.

Yash Dubey
Yash Dubey
10 min read
5 views

AlterLab handles this automaticallyscrape any URL with one API call. No infrastructure required.

Try it free

Price monitoring is one of the most common reasons people reach for a web scraping API. Track competitor prices, watch for deals, or keep your own pricing dashboard current. This tutorial builds a complete price monitor from scratch using AlterLab for scraping and Supabase for storage and scheduling.

~15 minSetup Time
$0/moInfrastructure Cost
hourlyCheck Frequency

What We're Building

A price monitor that:

  • Scrapes product pages on a schedule using Supabase pg_cron + Edge Functions
  • Extracts structured price data (price, currency, in-stock status) using AlterLab's JSON Schema extraction
  • Stores price history in Supabase Postgres
  • Sends a Slack alert when prices drop below a threshold

All serverless. No VMs, no containers to manage.

Prerequisites

  • A Supabase project (free tier works)
  • An AlterLab account with API key (get one here)
  • Basic familiarity with TypeScript or Python

Step 1: Set Up the Database

Open the SQL editor in your Supabase dashboard and create two tables:

SQL
-- Tracked products
create table products (
  id         uuid primary key default gen_random_uuid(),
  name       text not null,
  url        text unique not null,
  created_at timestamptz default now()
);

-- Price history
create table price_history (
  id           uuid primary key default gen_random_uuid(),
  product_id   uuid references products(id) on delete cascade,
  price        numeric(10, 2),
  currency     text default 'USD',
  in_stock     boolean,
  checked_at   timestamptz default now()
);

-- Fast lookups
create index on price_history (product_id, checked_at desc);
create index on products (url);

Seed some products to track:

SQL
insert into products (name, url) values
  ('Widget Pro', 'https://shop.example.com/widget-pro'),
  ('Gadget X',   'https://shop.example.com/gadget-x');

Step 2: Add Your AlterLab API Key

In your Supabase project: Settings → Edge Functions → Secrets

Code
ALTERLAB_API_KEY=sk_live_your_key_here
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...  (optional)
PRICE_ALERT_THRESHOLD=50.00  (alert when price drops below this)

Step 3: Create the Price Monitor Edge Function

Create supabase/functions/price-monitor/index.ts:

TYPESCRIPT
import { createClient } from "jsr:@supabase/supabase-js@2";

const supabase = createClient(
  Deno.env.get("SUPABASE_URL")!,
  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
);

// JSON Schema for structured price extraction
const PRICE_SCHEMA = {
  type: "object",
  properties: {
    price:    { type: "number",  description: "Current price as a number, e.g. 29.99" },
    currency: { type: "string",  description: "ISO currency code, e.g. USD" },
    in_stock: { type: "boolean", description: "Whether the product is currently in stock" },
    name:     { type: "string",  description: "Product name or title" },
  },
  required: ["price"],
};

async function scrapePrice(url: string) {
  const res = await fetch("https://api.alterlab.io/api/v1/scrape", {
    method: "POST",
    headers: {
      "X-API-Key":    Deno.env.get("ALTERLAB_API_KEY")!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url,
      extraction_schema: PRICE_SCHEMA,
    }),
  });

  if (!res.ok) {
    throw new Error(`AlterLab error ${res.status}: ${await res.text()}`);
  }

  const data = await res.json();
  return data.extraction as {
    price: number;
    currency?: string;
    in_stock?: boolean;
    name?: string;
  };
}

async function sendSlackAlert(product: string, price: number, url: string) {
  const webhook = Deno.env.get("SLACK_WEBHOOK_URL");
  if (!webhook) return;

  await fetch(webhook, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      text: `Price alert: *${product}* dropped to *$${price}*\n${url}`,
    }),
  });
}

Deno.serve(async () => {
  // Load all tracked products
  const { data: products, error } = await supabase
    .from("products")
    .select("*");

  if (error) {
    return new Response(JSON.stringify({ error: error.message }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }

  const threshold = parseFloat(Deno.env.get("PRICE_ALERT_THRESHOLD") ?? "0");
  const results = [];

  for (const product of products ?? []) {
    try {
      const extracted = await scrapePrice(product.url);

      // Store in price_history
      await supabase.from("price_history").insert({
        product_id: product.id,
        price:      extracted.price,
        currency:   extracted.currency ?? "USD",
        in_stock:   extracted.in_stock ?? true,
        checked_at: new Date().toISOString(),
      });

      // Alert if price dropped below threshold
      if (threshold > 0 && extracted.price < threshold) {
        await sendSlackAlert(product.name, extracted.price, product.url);
      }

      results.push({ name: product.name, price: extracted.price, ok: true });
    } catch (err) {
      results.push({ name: product.name, error: String(err), ok: false });
    }
  }

  return new Response(JSON.stringify({ checked: results.length, results }), {
    headers: { "Content-Type": "application/json" },
  });
});

Deploy it:

Bash
supabase functions deploy price-monitor --no-verify-jwt
1

Create tables

products and price_history in the Supabase SQL editor

2

Add secrets

ALTERLAB_API_KEY and optional SLACK_WEBHOOK_URL

3

Deploy Edge Function

supabase functions deploy price-monitor

4

Schedule with pg_cron

Check prices every hour automatically

Step 4: Schedule with pg_cron

Enable pg_cron and pg_net extensions in your Supabase dashboard under Database → Extensions, then run:

SQL
-- Check prices every hour
select cron.schedule(
  'price-monitor-hourly',
  '0 * * * *',
  $$
  select net.http_post(
    url     := current_setting('app.supabase_url')
                || '/functions/v1/price-monitor',
    headers := jsonb_build_object(
      'Authorization', 'Bearer '
                         || current_setting('app.service_role_key'),
      'Content-Type',   'application/json'
    ),
    body := '{}'::jsonb
  );
  $$
);

That's it. Supabase will now call your Edge Function every hour, scraping all tracked products and recording their prices.

Querying Price History

Check price trends directly in SQL:

SQL
-- Latest price for each product
select p.name, ph.price, ph.currency, ph.in_stock, ph.checked_at
from price_history ph
join products p on p.id = ph.product_id
where ph.checked_at = (
  select max(checked_at)
  from price_history ph2
  where ph2.product_id = ph.product_id
);

-- Price history for a specific product (last 7 days)
select ph.price, ph.checked_at
from price_history ph
join products p on p.id = ph.product_id
where p.name = 'Widget Pro'
  and ph.checked_at > now() - interval '7 days'
order by ph.checked_at desc;

-- Biggest price drops in the last 24 hours
with ranked as (
  select
    p.name,
    ph.price,
    lag(ph.price) over (partition by ph.product_id order by ph.checked_at) as prev_price,
    ph.checked_at
  from price_history ph
  join products p on p.id = ph.product_id
  where ph.checked_at > now() - interval '24 hours'
)
select name, prev_price, price,
       round((price - prev_price) / prev_price * 100, 1) as pct_change
from ranked
where prev_price is not null
order by pct_change asc
limit 10;

Python Alternative

If you prefer a Python script over a Deno Edge Function, use the AlterLab Python SDK with the Supabase Python client:

Python
from alterlab import AlterLabSync
from supabase import create_client
from datetime import datetime, UTC

scraper = AlterLabSync(api_key="sk_live_...")
supabase = create_client(
    "https://your-project.supabase.co",
    "your-service-role-key",
)

PRICE_SCHEMA = {
    "type": "object",
    "properties": {
        "price":    {"type": "number",  "description": "Current price"},
        "currency": {"type": "string",  "description": "ISO currency code"},
        "in_stock": {"type": "boolean", "description": "In stock status"},
    },
    "required": ["price"],
}

def check_prices():
    products = supabase.table("products").select("*").execute().data

    with scraper:
        for product in products:
            result = scraper.scrape(
                product["url"],
                extraction_schema=PRICE_SCHEMA,
            )
            extracted = result.get("extraction") or {}

            supabase.table("price_history").insert({
                "product_id": product["id"],
                "price":      extracted.get("price"),
                "currency":   extracted.get("currency", "USD"),
                "in_stock":   extracted.get("in_stock", True),
                "checked_at": datetime.now(UTC).isoformat(),
            }).execute()

            print(f"{product['name']}: ${extracted.get('price')}")

if __name__ == "__main__":
    check_prices()

Run this on a schedule with your own cron or a simple serverless function.

Cost Estimate

For 10 products checked hourly:

ItemRateDaily cost
AlterLab scrapes (Tier 1, static)$0.0002/req$0.048/day
AlterLab scrapes (Tier 4, JS-heavy)$0.004/req$0.96/day
Supabase Edge Function invocationsFree tier$0
Supabase Postgres storageFree tier$0

For most e-commerce sites with basic JavaScript, Tier 1 or Tier 2 is sufficient. AlterLab automatically escalates tiers when a page needs more than a plain HTTP request to render correctly.

Next Steps

  • Add a Supabase Realtime subscription to push live price updates to a dashboard
  • Use pgvector to store product descriptions and find semantically similar products
  • Add email alerts via Supabase Edge Functions + Resend
  • Check out the AlterLab Supabase integration docs for more patterns including lead enrichment and content aggregation
Share

Was this article helpful?

Frequently Asked Questions

For 10 products checked hourly, AlterLab costs about $0.05-$0.96/day depending on whether the sites use JavaScript rendering. Supabase is free for small projects. Total cost is typically under $5/month.
Yes. AlterLab automatically escalates through its tier system, from a basic HTTP request up to full browser rendering, when a page needs more than a plain HTTP request to render its content. You do not need to configure this manually.