Monitors are live: get a webhook when any Shopify store changes a price, sells out, or starts a promo

[ BLOG ]

All posts

Monitoring ·

How to Know When a Competitor Starts a Sale on Shopify

On Shopify, a sale shows up as a compare-at price above the current price. How to read that signal, tell a real discount from an inflated "was" price, and get an alert when a sale starts or ends.

On Shopify, a product is on sale when its compare-at price is higher than its price. That's the field that makes a theme show a crossed-out "was" price and a sale badge. So the way to know when a competitor starts a sale is to watch the compare-at prices on their products and get told when one rises above the price, and again when it drops back. You can check this by hand on a few products, or set up alerts that cover every product in up to 100 stores.

How sales work on Shopify

Each product variant on Shopify has a price and an optional compare-at price. When the compare-at price is set and higher than the price, the storefront shows the product as discounted. A $60 jacket with a compare-at price of $80 shows as "$60, was $80".

That gives you two ways a sale can start:

  • A real price drop. The merchant lowers the price and sets the old price as the compare-at price. The customer pays less than last week.
  • A raised compare-at price. The merchant leaves the price where it was and sets a higher compare-at price. The product looks discounted, but the customer pays the same as before.

The second pattern is common, and it matters if you're deciding whether to respond. A competitor who suddenly shows "30% off" without changing what anyone pays isn't undercutting you.

Discount codes and automatic discounts applied in the cart are a third mechanism. They don't change the product's price or compare-at price, so they don't show up on product pages. You'll find those in ads, emails and banners instead.

What you need

  • The list of competitor stores you want to watch.
  • For manual checks, a browser.
  • For alerts, an HTTPS endpoint that can receive webhooks and a ShopLens API key.

Steps

  1. Pick the products that matter: your competitor's best sellers and the products that compete with yours.
  2. Record each product's current price and compare-at price.
  3. Check them again on a schedule, or set up a monitor that does it for you.
  4. When a sale starts, compare the new price with the price before the sale.
  5. Classify it as a real price drop or a raised compare-at price.
  6. Log the end of the sale so you know how long it ran.

Step 1: choose the products

You can't usefully watch every product by hand. Start with the competitor's best sellers (see how to find the best-selling products of a Shopify store) and the products that go head to head with yours. With alerts, you can watch whole stores instead.

Step 2: record price and compare-at price

Every Shopify product has a public JSON view. Add .js to a product URL, for example example-rival.com/products/linen-shirt.js, and the response lists each variant with price and compare_at_price. In this endpoint both values are in the currency's smallest unit, so 6000 means $60.00. The format is described in Shopify's Ajax API docs.

bash
curl -s https://example-rival.com/products/linen-shirt.js \
  | jq '.variants[] | {title, price, compare_at_price}'

Save the values in a spreadsheet with the date.

Step 3: check on a schedule, or get alerts

Running that request every morning for 40 products works, but it breaks when product handles change and it doesn't scale to whole catalogs. A monitor does the same check for you, about once a day, and sends a webhook only when something changes.

The two topics for sales are product.sale_started, which fires when one or more variants go on sale in a market, and product.sale_ended, which fires when they stop being on sale.

bash
curl -s -X POST https://data.shoplens.dev/api/v1/monitors \
  -H "Authorization: Bearer $SHOPLENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Competitor sales",
    "webhookUrl": "https://example-app.com/webhooks/shoplens",
    "topics": ["product.sale_started", "product.sale_ended"],
    "markets": ["US", "GB"],
    "shops": ["example-rival.com", "example-other.com"]
  }'

Every store in shops must already be known to ShopLens; look new ones up first with GET /shops/{domain}. Sales are checked per market because a store can discount in one country and not another, and leaving markets empty checks each store's primary market only. A monitor costs 1 credit a month while active. The complete setup, including receiving and storing events, is in how to track competitor prices on Shopify.

Step 4: compare with the price before the sale

A product.sale_started event lists the variants now on sale as they are now, and each one carries previous with its price and compare-at price before the change.

json
{
  "eventType": "product.sale_started",
  "shop": { "domain": "example-rival.com" },
  "market": "US",
  "data": {
    "product": { "title": "Linen Shirt", "handle": "linen-shirt" },
    "variants": [
      {
        "title": "M / White",
        "price": "39.00",
        "compareAtPrice": "45.00",
        "currency": "USD",
        "previous": { "price": "45.00", "compareAtPrice": null }
      }
    ]
  }
}

The example is made up. Here the price went from 45.00 to 39.00 and the old price became the compare-at price, so this is a real discount.

Step 5: classify the sale

Apply one rule to each variant:

  • If price is lower than previous.price, it's a real price drop. The size of the cut is previous.price minus price.
  • If price equals previous.price and compareAtPrice went up, it's a raised compare-at price. Nothing changed for the buyer.
typescript
type Variant = { price: string; compareAtPrice: string | null; previous?: { price?: string } };

function classify(variant: Variant) {
  const before = Number(variant.previous?.price ?? variant.price);
  const now = Number(variant.price);
  if (now < before) return { kind: "price_drop", cut: before - now };
  return { kind: "raised_compare_at", cut: 0 };
}

Step 6: log when the sale ends

product.sale_ended fires when the variants stop being on sale. Store the date next to the start, and after a few months you'll know each competitor's habits: how often they run sales, how deep they go and how long sales last. That's more useful for planning than any single alert.

Reading sale patterns across a season

After a few months of logged sales, sort them by competitor and by month. Most brands follow a calendar: a spring sale, a summer clearance, Black Friday and an end-of-year sale, plus smaller promotions on individual collections. Knowing when a competitor usually discounts lets you plan your own promotions instead of reacting to theirs, and tells you when a sale outside the usual calendar means something has changed, such as excess stock or a slow season.

Common mistakes

  • Treating every sale badge as a threat. Check whether the price actually dropped.
  • Watching one market. Brands often discount in one country first. Add the markets you sell in.
  • Missing cart discounts. Code-based promotions don't change product prices. Watch the competitor's ads for those, as in [how to see the ads a Shopify store is running](/blog/see-shopify-store-ads).
  • Reacting to every alert. Decide in advance which cuts on which products are worth a response.

How to check it worked

Open the product page of the first alert you receive. You should see the crossed-out compare-at price and the sale price from the event. If the page shows no sale, check the market: the event may be for a country where you don't see the store's prices from your location.

[ GET STARTED ]

Start building on Shopify data

Get an API key and make your first store lookup in a few minutes.