Price Drop Alerts with a Shopping API: Architecture + Real Gotchas (2026)

By Anurag Pathak · · 10 min read

A price-drop alert is a four-step loop: poll a product query on a schedule, read the numeric extractedPrice from each listing, compare it to your stored baseline, and notify when it falls. The Shopping API supplies everything the loop needs in one call — price, currency, merchant, and a stable product URL to key the alert on.

This guide walks through the pattern with a tested Python script and honest cost math, so you know what a real price-watch system costs before you build it. It also covers the gotchas I hit in August 2026: prices come back null on a share of placements (my first runs returned prices on 0, 0, then 1 of the placements; a re-test returned 6–8 of 10), and one query returns ~10–11 listings no matter the num — both of which change how you write the loop.

TL;DR: Poll /api/shopping for your watchlist, use extractedPrice (a number, not a string) to compare, store a baseline per product, and fire when the current price drops below it. Skip null prices. At the Scale tier a 1,000-keyword daily sweep costs about 42 cents a day.

Table of Contents

The price-drop loop

Every price-watch system, from a hobby script to an enterprise repricer, is the same four steps:

  1. Poll. On a schedule, query each product you watch.
  2. Normalize. Turn the display price into a number you can compare.
  3. Compare. Against the last price you stored for that product.
  4. Alert. When the current price drops below the baseline (or your threshold).

The hard part is never the loop — it is getting a clean, comparable price reliably. That is where the API earns its keep.

Price data the API gives you

Each listing in the Shopping API response includes both a display price string and a numeric extractedPrice. The numeric one is what your comparison logic should use:

{
  "title": "Sony WF-1000XM6",
  "price": "$299.99",        // display string
  "extractedPrice": 299.99,  // number, ready for math
  "currency": "USD",
  "merchant": "Best Buy",
  "url": "https://www.bestbuy.com/product/sony-wf1000xm6-..."
}

Keying alerts on url (the merchant product URL) keeps the same product stable across polls. Keying on the title alone is fragile — two merchants can sell "Nike Vomero 18" at different prices, and that is exactly the difference you want to catch.

Working Python: a price watcher

Here is a complete, runnable watcher. It keeps baselines in a JSON file, polls a watchlist, and prints an alert for every product whose price dropped since the last run. Wire the print to your email, Slack, or push channel.

Replace sk_live_your_key with your key from the dashboard.

import json
import os
import time
import requests

API_KEY = "sk_live_your_key"
BASE_URL = "https://apiserpent.com/api/shopping"
STATE_FILE = "price_baselines.json"

def fetch_prices(query):
    resp = requests.get(
        BASE_URL,
        params={"q": query, "engine": "yahoo", "num": 20},
        headers={"X-API-Key": API_KEY},
        timeout=60,
    )
    resp.raise_for_status()
    out = {}
    for item in resp.json()["results"]["shopping"]:
        price = item.get("extractedPrice")
        if price is None:                 # Yahoo didn't show a price this poll
            continue
        out[item["url"]] = {
            "title": item["title"],
            "price": price,
            "merchant": item.get("merchant"),
        }
    return out

def load_state():
    if os.path.exists(STATE_FILE):
        return json.load(open(STATE_FILE))
    return {}

def save_state(state):
    json.dump(state, open(STATE_FILE, "w"), indent=2)

WATCHLIST = ["nike running shoes", "sony headphones", "iphone 15 case"]

state = load_state()
for query in WATCHLIST:
    for url, listing in fetch_prices(query).items():
        previous = state.get(url, {}).get("price")
        current = listing["price"]
        if previous is not None and current < previous:
            drop = previous - current
            print(f"PRICE DROP ({drop:.2f}) {listing['title']} "
                  f"${current:.2f} was ${previous:.2f} @ {listing['merchant']}")
        state[url] = listing

save_state(state)
print(f"\nWatched {len(state)} products. Baselines saved to {STATE_FILE}.")

Run it once to seed baselines, then again later — any product whose extractedPrice fell prints a PRICE DROP line. The script skips listings with no price instead of treating null as a catastrophic drop to zero.

Cost math for a real watchlist

Here is what a price-watch system actually costs on the Shopping API, billed per 1,000 calls (no page multiplier, no subscription):

TierPer 1,000 calls500 keywords, 1×/day5,000 keywords, 1×/day
Default$0.60$0.30/day$3.00/day
Growth$0.54$0.27/day$2.70/day
Scale$0.42$0.21/day$2.10/day

At the Scale tier a 500-keyword daily sweep is 21 cents a day. Even hourly checks of a 200-product watchlist (4,800 calls/day) come to about $2 a day at Scale. See the pricing page for the current full table.

Honest limits

Next step: pull a key from the dashboard, try the Shopping API in the playground, then adapt the watcher above. Pair it with price monitoring, e-commerce price intelligence, or the cross-engine Google vs Yahoo comparison.

FAQ

How do price drop alerts work with a shopping API?

Poll a query on a schedule, read the numeric extractedPrice, compare it to the baseline you stored for that product URL, and alert when it falls below a threshold.

What is the cheapest way to track price drops?

Shopping calls cost $0.00042 each at the Scale tier ($0.42 per 1,000). A 1,000-keyword catalog checked once a day is about 42 cents a day, so even hourly sweeps of a small watchlist stay affordable.

Why is the price sometimes missing?

The product rail hydrates prices asynchronously, so a small share of listings can lack a displayed price at read time. Skip nulls in your loop and log them; never treat a missing price as a zero.

Can I get historical price data?

No API returns the prices it showed yesterday. You build history by storing baselines as you poll. The API supplies the current price; your store supplies the past.

Does engine=yahoo vs engine=google matter for price alerts?

Both engines return price and extractedPrice. Query the same product on both for a cross-engine view of the lowest offer — see the Google vs Yahoo comparison for the pattern.