ScreenshotNeo

BlogHow-to

How to Schedule Screenshots of Product Pages for Stock and Price Checks

Build a scheduled screenshot workflow with Playwright and cron, or use a managed capture service. Learn how to keep captures consistent and interpret changes carefully.

By the ScreenshotNeo team4 October 202610 min read

To schedule screenshots of product pages for stock and price checks, write a browser script that captures each page and run it on a recurring schedule. The script controls the browser and saves timestamped images; a scheduler such as cron decides when the script runs. You can also use a managed screenshot service that supports recurring captures. A screenshot or visual difference is evidence of what the page appeared to display, not verified inventory data.

This guide uses Playwright with Python and cron. It also covers full-page and targeted captures, stable rendering, comparison limits, alerts, and a managed alternative: ScreenshotNeo, a screenshot API and MCP server for developers.

1. Choose a capture approach

Approach Good fit when Trade-offs
Playwright plus a recurring workflow You want to control the browser, viewport, capture scope, and where images are stored. You maintain the script, browser runtime, storage, and any comparison or notification logic.
Managed scheduled-capture service You want recurring captures and delivery without maintaining a browser runtime. Check the provider’s current schedule limits, retention, geography, access controls, and notification behavior. These vary by service.

Allscreenshots documents recurring captures, stored results, email or webhook delivery, and optional notifications when a page changes; its documentation lists monitoring product pricing pages as a use case. Review its current terms and limits before relying on it. Allscreenshots documentation

For either approach, first check the target site’s terms and any access restrictions. Choose a cadence that fits the decision you need to make; scheduled screenshots do not provide live inventory data.

2. Set up a Playwright capture script

The following Python script opens each configured URL, waits for the page to load, captures the viewport or full page, and saves images with UTC timestamps. It uses a consistent viewport and browser settings to reduce avoidable variation. It does not log in, bypass bot checks, or infer stock status.

Install dependencies

python -m venv .venv
source .venv/bin/activate
python -m pip install playwright
python -m playwright install chromium

On Windows, activate the environment with .venv\Scripts\activate. Install the browser in the same environment that will run the scheduled job.

Save the capture script

Create capture_products.py:

import asyncio
import os
from datetime import datetime, timezone
from pathlib import Path
from urllib.parse import urlparse

from playwright.async_api import async_playwright

URLS = [
    "https://example.com/product-a",
    "https://example.com/product-b",
]
OUTPUT_DIR = Path(os.environ.get("SCREENSHOT_DIR", "captures"))
FULL_PAGE = True
VIEWPORT = {"width": 1440, "height": 1000}
NAVIGATION_TIMEOUT_MS = 45_000


def safe_name(url: str) -> str:
    parsed = urlparse(url)
    # Keep filenames readable without putting the full URL or query string in them.
    path = parsed.path.strip("/").replace("/", "-") or "page"
    host = parsed.netloc.replace(":", "-")
    return f"{host}-{path}"[:150]


async def main() -> None:
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")

    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        context = await browser.new_context(
            viewport=VIEWPORT,
            color_scheme="light",
            reduced_motion="reduce",
            locale="en-US",
            timezone_id="UTC",
        )
        page = await context.new_page()
        page.set_default_navigation_timeout(NAVIGATION_TIMEOUT_MS)

        for url in URLS:
            name = safe_name(url)
            output = OUTPUT_DIR / f"{name}-{timestamp}.png"
            try:
                response = await page.goto(url, wait_until="domcontentloaded")
                # Give client-side rendering a short chance to populate the page.
                await page.wait_for_timeout(1500)
                await page.screenshot(path=str(output), full_page=FULL_PAGE)
                status = response.status if response else "no response object"
                print(f"saved={output} status={status} url={url}")
            except Exception as exc:
                print(f"capture_failed url={url} error={exc}")

        await context.close()
        await browser.close()


if __name__ == "__main__":
    asyncio.run(main())

Replace the example URLs with pages you are permitted to access. For a first run, use a small list and inspect the images before scheduling. The short fixed wait is a starting point: product pages that render later may need a locator-based wait for the price or availability region. Avoid waiting for every network request to become idle without checking behavior; pages with analytics, ads, or polling connections may never become idle.

Capture only the product details

When the site has a stable selector for the product information, a locator screenshot can exclude unrelated page content. Replace the full-page screenshot line with:

details = page.locator("[data-testid='product-details']")
await details.wait_for(state="visible", timeout=15_000)
await details.screenshot(path=str(output))

Use a selector that actually exists on the target page. Selectors based on generated class names can break when the site changes. A targeted element capture is compact, while a full-page capture preserves surrounding context such as shipping notes or variant selection.

3. Run it on a recurring schedule

Test the script from the directory where it lives, then schedule it with an absolute path. On a Unix-like host, open the crontab with crontab -e and add a line such as this to run every six hours:

0 */6 * * * cd /absolute/path/product-monitor && /absolute/path/product-monitor/.venv/bin/python capture_products.py >> /absolute/path/product-monitor/capture.log 2>&1

Change the interval to suit your use case. Cron uses the machine’s local timezone unless configured otherwise, so verify the host timezone and daylight-saving behavior. Keep the script, virtual environment, output directory, and log path accessible to the account that owns the cron job. For a hosted workflow scheduler, configure the same command and cadence, and make sure the job has browser dependencies and persistent or uploaded storage.

Keep a useful record

  • Include a UTC timestamp in each filename so captures sort consistently across hosts.
  • Keep the original images and the URL list that produced them. If URLs contain sensitive tokens, do not put them in public logs or shared filenames.
  • Choose a retention policy and storage location appropriate for the data and volume. Screenshots can contain personal or account-specific information.
  • Record failures separately from captures so an unavailable page is not mistaken for a product state.

4. Compare observations carefully

A pixel or image difference can flag a changed page, but it cannot establish that stock or price changed. Fonts, browser versions, operating systems, hardware, power source, rendering settings, and headless mode can all affect output. Playwright recommends keeping the rendering environment consistent when using visual comparisons. Playwright visual comparisons

For lower-noise comparisons, keep the same browser version, operating system, viewport, locale, timezone, color scheme, and capture scope. Wait for the relevant content to settle. If animation causes noise, prefer reduced motion or disable animation in a controlled way. Handle predictable consent dialogs and other overlays explicitly; do not assume every page has the same layout or state. Playwright page assertions and overlay guidance

When the goal is an exact price or availability history, pair the screenshot with a permitted structured check of the displayed text. Store the observed text alongside the image and timestamp, and retain the image as context. Follow the site’s terms and use an available, permitted interface. A visual alert should say that the page appeared to change and link to the observation, rather than claiming confirmed inventory.

5. Improve the capture for your target pages

Pick the right scope

  • Viewport: captures only the currently visible area. Use it when the product panel is already in view and you need a small artifact.
  • Full page: captures the scrollable page. Use it when context below the fold matters, while accounting for page length and lazy-loaded content.
  • Element: captures a selected region such as a product details panel. Use it when a reliable selector is available and surrounding content is unnecessary.

Playwright supports viewport, full-page, and locator screenshots. For long pages, inspect whether lazy-loaded images or content appear before the capture; scrolling or waiting may be needed for the page to populate them. Playwright screenshots

Make dynamic pages repeatable

  1. Wait for a meaningful element, such as the product title or price, rather than relying only on a fixed delay.
  2. Use the same region and page state each time, including variant, region, currency, and signed-in state where applicable.
  3. Handle consent dialogs or predictable overlays explicitly in the normal capture flow. A dialog can obscure the product details or change what the page displays.
  4. Keep browser and rendering configuration stable, and update it deliberately when dependencies change.

Some pages personalize content by location, cookies, account state, or selected options. A capture represents only the state used for that run; it may differ from what another visitor sees.

6. Performance, reliability, and cost

  • Runtime: each page requires browser navigation and rendering. Full-page captures can take longer and create larger files than a viewport or element capture. Set a reasonable navigation timeout and handle failures per URL so one broken page does not stop the rest.
  • Reliability: a successful browser navigation does not guarantee that the intended product data loaded. Check the expected element and inspect a sample of saved images. Log timestamps, status codes when available, and errors.
  • Scheduling: the scheduler only starts jobs; it does not guarantee the target site is available at that moment. Avoid overlapping runs if one run might still be capturing when the next starts.
  • Storage: estimate image volume from the number of URLs, cadence, file size, and retention period. Remove or archive old captures according to your retention needs.
  • Cost: self-managed runs use your compute and storage. Hosted schedulers and managed capture providers may impose their own limits or charges; check current terms and pricing directly. This research does not establish a universal cost or ideal schedule.

7. Troubleshooting

Symptom Likely cause Fix
Browser executable missing Playwright’s Chromium browser was not installed in the environment used by the job. Run python -m playwright install chromium with the same Python environment and user account that runs the script.
Works manually but not in cron Cron has a different working directory, PATH, environment, or permissions. Use absolute paths, set needed environment variables explicitly, and redirect output to a writable log.
Screenshot is blank or incomplete Client-side content had not rendered, navigation failed, or the relevant area was below the fold or lazy-loaded. Wait for a meaningful locator, check the response and logs, and scroll or otherwise trigger lazy content before capturing if appropriate.
Price panel is missing The selector changed, the product variant is unavailable, or the page rendered a different state. Inspect the current page, update the selector, and record a failed observation rather than treating missing text as out of stock.
Many visual changes with no product change Rendering configuration, animations, rotating content, or personalized elements differ between runs. Keep browser and viewport settings consistent, reduce motion, target a stable element, and inspect flagged images before alerting.
Navigation times out The page is slow, has long-running requests, or blocks automated browsing. Use a suitable navigation wait condition, wait separately for the needed element, review site rules, and do not try to bypass access controls.
Overlapping jobs or missing files A run exceeds its interval, storage is unavailable, or the scheduler account cannot write to the output directory. Prevent concurrent runs, use persistent storage, and verify directory permissions and available capacity.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request can return a PNG, JPEG, WebP, or PDF; use your scheduler to call the API on a recurring cadence. See the ScreenshotNeo API documentation for parameters and setup.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

For a product page, replace the target URL with the page you are allowed to capture. Add your scheduler around this request and save each result with a timestamp. ScreenshotNeo accepts the parameter names used by other screenshot APIs, which can make switching easier. Its other relevant options include full-page capture, element selection, custom viewport and device presets, wait conditions, cookies and headers, caching with a chosen TTL, async jobs with signed webhooks, and bulk capture for up to 100 URLs per call. Every plan includes every feature.

Cookie and consent banners are accepted and more than 60 known consent platforms, newsletter popups, and chat widgets are removed before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Responses identify the page verdict and billing status in headers. An 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; yearly billing gives two months free. Sign up for 1,000 free screenshots a month with no card.

FAQ

Does a screenshot prove that a product is in stock?

No. It records what the page displayed for that browser state and time. For a reliable stock record, separately record the displayed availability text through an allowed method and preserve the screenshot as context.

How often should I capture a product page?

There is no universal interval. Choose one based on how quickly a change matters and what access the site permits. A scheduled capture can miss changes between runs.

Should I use full-page screenshots?

Use them when information below the fold matters. For a compact price history, a stable product details element can be easier to compare.

Can I monitor pages that require an account?

Only if you are authorized and can store the required session data securely. Account-specific pages may show personalized prices or availability, so label observations with the state used.