How to Monitor a Shopify Product Page with Urlwatch
Track changes to a Shopify product’s public page with urlwatch: extract the fields you care about, preview the result, schedule checks, and reduce noisy alerts.
Use urlwatch to fetch a Shopify product’s public page, filter its output to the product fields you care about, and compare each run with the saved previous result. Start with a regular URL job; switch to a browser job only if the needed content is rendered by JavaScript. Preview the filter before scheduling, then run urlwatch periodically to receive diffs when the selected content changes.
This watches what a visitor can access on the public storefront. It does not grant access to a Shopify store’s private admin data.
1. Install urlwatch and initialize it
Follow the official urlwatch installation instructions for your operating system and Python environment. Then run urlwatch once to initialize its local state and configuration. Use urlwatch --edit to open or create its job list, urls.yaml.
urlwatch
urlwatch --edit
Keep the state directory available between runs. Urlwatch compares new results with saved history; running it from a temporary environment that loses its state means it cannot maintain the expected change history.
2. Find the product fields in the page HTML
Open the product page in a browser and inspect its markup. Identify the element or elements containing the exact values to track, such as title, price, availability, or description. Do not copy a selector from another store and assume it applies to yours: Shopify themes and customizations vary, and a page may contain duplicate desktop/mobile or hidden elements.
Also check whether the values appear in the HTML returned directly by the web server. You can inspect the response with a command-line request:
curl -L 'https://your-store.example/products/your-product' -o product.html
Replace the example URL with the public product page. Search the saved file for the visible title or price. If the relevant content is present, a regular urlwatch URL job is usually the simplest option. If it is missing because JavaScript fills it in after page load, use the browser job described below.
3. Create a URL job and filter its output
This example selects the page’s product title, price, availability, and description using illustrative CSS selectors. Replace every selector with one you verified in your store’s markup. A CSS selector filter is chained with html2text so the stored comparison is readable text rather than a large HTML fragment.
name: "Shopify: Example product"
url: "https://your-store.example/products/your-product"
filter:
- css:
selector: "h1.product__title, .price, .product__inventory, .product__description"
- html2text
- strip
Save the job, then list configured jobs and preview the filter result:
urlwatch --list
urlwatch --test-filter 1
Use the job’s listed index in place of 1, or test it by URL if supported by your installed version:
urlwatch --test-filter 'https://your-store.example/products/your-product'
The preview should contain the values you intend to watch and omit navigation, recommendations, rotating promotions, and other unstable page content. Refine the selectors until the preview is useful. The filter reference documents CSS and XPath filters, filter chaining, and options such as maxitems for limiting duplicate matches: urlwatch filters.
Track only one field or limit duplicate matches
To watch only a price, use the verified price selector alone. If a selector matches multiple copies of an element, set maxitems: 1 when the first match is the correct one:
name: "Shopify: Example product price"
url: "https://your-store.example/products/your-product"
filter:
- css:
selector: ".price"
maxitems: 1
- html2text
- strip
You can also use an XPath filter, or chain filters such as html2text, grep, and strip. The right filter depends on the actual HTML and the output you want to compare. Check available features in your installed release with urlwatch --features.
4. Use a browser job when the product content needs JavaScript
A regular URL job reads the document returned by the server. If the desired product values only appear after JavaScript runs, configure a browser job with navigate. Browser jobs require urlwatch’s optional Playwright dependency and installed browser binaries. They use substantially more resources than regular URL jobs, so use one only when the ordinary response is insufficient.
name: "Shopify: JavaScript-rendered product"
navigate: "https://your-store.example/products/your-product"
wait_until: "domcontentloaded"
wait_for: ".product__title"
filter:
- css:
selector: "h1.product__title, .price, .product__inventory"
- html2text
- strip
Install the optional Playwright package and browser using the instructions for your urlwatch version and Playwright environment. The browser job supports a wait_until value of load, domcontentloaded, networkidle, or commit; wait_for accepts a CSS or XPath selector. The documented default timeout for wait_for is 30 seconds. See the urlwatch job reference for the current options and installation requirements.
Choose a wait condition that matches the page. Waiting for networkidle may be unsuitable for pages with persistent network activity. Waiting for a specific product selector can be more targeted, but the selector must exist on that page.
5. Schedule checks and configure notifications
Urlwatch checks at the cadence at which you invoke it. The urlwatch quick start recommends running no more frequently than every 30 minutes. On a Unix-like system, edit your crontab with crontab -e and add a schedule such as:
*/30 * * * * urlwatch
Make sure the scheduled command uses the same account, urlwatch installation, configuration, and state directory as your manual run. If the executable is not on cron’s PATH, use its full path. On Windows, use Task Scheduler to run urlwatch on the interval you choose.
Urlwatch can report changes in the terminal, by email, or through supported third-party notification methods. Configure the reporter in urlwatch’s configuration using the handbook instructions for your chosen channel. A scheduled run only creates an alert where a reporter is configured and the job detects a change.
6. Interpret Shopify changes carefully
A change in visible page text means the selected storefront output changed; it does not necessarily mean a merchant intentionally edited the product copy or price. Themes can change markup, promotions can rotate, and availability text can reflect stock or sales-channel behavior.
Shopify’s documentation for product JSON describes admin data fields, including updated_at and variant inventory_quantity. The product timestamp can change when inventory changes automatically after a purchase, so it is not by itself proof of a title, description, or price edit. Shopify also notes that inventory quantity may be negative in some circumstances. These admin fields are not evidence that an unauthenticated public JSON endpoint exists for every product. See Shopify’s admin JSON documentation.
Or skip the browser setup
ScreenshotNeo captures a visual snapshot of a product page with one API request. It is useful for keeping visual snapshots or reviewing what the storefront looked like; urlwatch remains the tool in this guide for scheduled content diffs and alerts. ScreenshotNeo can render JavaScript pages without you installing and maintaining a local browser.
Before the capture, ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://your-store.example/products/your-product \
-o product.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://your-store.example/products/your-product",
},
timeout=90,
)
r.raise_for_status()
with open("product.webp", "wb") as image:
image.write(r.content)
import { writeFile } from 'node:fs/promises';
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://your-store.example/products/your-product',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await writeFile('product.webp', Buffer.from(await res.arrayBuffer()));
Start with 1,000 free screenshots a month with no card.
Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| The filter preview is empty | The selector does not match the response HTML, or the value is added by JavaScript. | Inspect the fetched HTML and verify the selector. If JavaScript supplies the content, switch to a browser job and wait for the relevant selector. |
| The diff includes menus, recommendations, or promotions | The filter selects too much of the page. | Choose narrower selectors for the product fields and rerun --test-filter. |
| The same field appears more than once | The theme includes duplicate or hidden markup. | Inspect all matches; narrow the selector or use CSS/XPath maxitems where appropriate. |
| A filter change causes a surprising diff | Old content was stored using the previous filter; urlwatch does not refilter that historical content. | Review the preview and understand that the next diff may compare output produced by different filter settings. Avoid treating that one transition as a product edit. |
| The browser job reports a missing Playwright dependency or browser | The optional package or browser binary is not installed in the environment running urlwatch. | Install the optional Playwright dependency and run the documented browser installation command for that environment. |
| The browser job times out waiting for content | The selector is wrong, loads later than expected, or the page is blocked or unavailable. | Confirm the selector in the rendered page, choose an appropriate wait condition, and check the page manually. Do not suppress errors until you know the cause. |
| The job works manually but not on schedule | The scheduler uses another account, PATH, configuration, or state directory. | Use the same environment and persistent state as the successful manual run; inspect scheduler logs and use an absolute executable path if needed. |
| Alerts are too frequent or too noisy | The schedule is too aggressive or the selected content changes for irrelevant reasons. | Follow the recommended minimum 30-minute cadence, narrow the filter, and preview its output before relying on alerts. |
Performance, reliability, and cost
- Request load: Each scheduled run fetches the product page. The quick start recommends no more than one check every 30 minutes. Avoid unnecessary repeat checks.
- Browser resources: Browser jobs use Playwright and a browser installation, and consume substantially more resources than URL jobs. Use them only when page rendering requires JavaScript.
- Alert reliability: A diff is only as meaningful as the filtered output and persistent history behind it. Preview filters, preserve urlwatch state, and treat transient retrieval failures separately from product changes.
- Cost: Urlwatch is open-source software; this workflow’s direct cost depends on where you run it and any notification service you configure. A machine must be available at scheduled times. Hosting it on a server is optional, not required if an existing computer can run the schedule.
- Monitoring scope: Public page monitoring observes the storefront response available to the monitor. It does not guarantee access to private inventory, admin-only data, or a specific customer’s localized storefront experience.
FAQ
Will urlwatch tell me when the Shopify store changes its product price?
It can report a changed price when the price is present in the fetched or rendered page and your filter selects it. Validate the filtered output first.
Can I monitor several products?
Yes. Add a separate named job for each product page in urls.yaml. Keep each filter specific to that product page’s markup.
Does checking a product page reveal private Shopify inventory?
No. This workflow monitors public page content. Admin JSON documentation describes admin data and does not establish public access to those resources.
Can ScreenshotNeo notify me when a product changes?
The API described here returns screenshots; use urlwatch for scheduled text comparison and alerts. ScreenshotNeo can provide visual captures without local browser setup.


