How to Screenshot Product Prices on Infinite-Scroll Pages
Load the target product before capturing it. Learn when to use a viewport, product-card, or full-page screenshot, with runnable Playwright code.
To screenshot a product price on an infinite-scroll page, first scroll until the target product and its price have loaded, wait for them to settle, then capture the product card or the visible viewport. A full-page screenshot can show the loaded page, but it cannot include products that the page has not loaded yet. Verify the saved image and keep the page URL and capture time with it for context.
1. Choose what to capture
Decide what the screenshot needs to show before you scroll. A single product card is usually easiest to read; a viewport includes nearby listing context; a full-page image can show more of the loaded listing but may be very tall.
| Capture | Useful when | Limit |
|---|---|---|
| Product element | You need a legible record of one product and its price. | It may omit the surrounding listing and page identity. |
| Viewport | The target is visible and nearby context matters. | Anything outside the current viewport is omitted. |
| Full page | You want surrounding context from the page content available at capture time. | It does not force an infinite feed to load every product, and the result may be unwieldy. |
Playwright describes a full-page screenshot as a capture of the full scrollable page and also supports screenshots of a specific element. Neither mode replaces the step of triggering the site’s scroll-based loading first. Playwright screenshot documentation
2. Capture it manually in Chrome DevTools
- Open the listing and locate the product you want to record.
- Scroll the page until the product and displayed price are visible. If the listing itself has a scroll panel, move the pointer over that panel and scroll it.
- Wait for the product text, price, and image to finish loading. Check that a loading indicator has disappeared if the site shows one.
- For a readable record of the visible price, take a viewport screenshot. For a product card, capture or crop the card if your workflow supports it.
- To capture content beyond the viewport, open Chrome DevTools, enable Device Mode, then choose More options → Capture a full size screenshot. Chrome documents this option for including page content outside the viewport. Chrome DevTools Device Mode
- Open the saved image and confirm it shows the correct product and price. Record the listing URL and capture time in a filename, note, or adjacent record.
A full-size capture is still bounded by the content available to the page at capture time. If the target has not loaded, scroll farther and capture again.
3. Automate the capture with Playwright
The following Node.js script opens a listing, scrolls the document in increments to trigger lazy loading, waits for a target product selector, then saves both a card screenshot and a full-page screenshot. Replace the URL and CSS selectors with values from the site. The card selector should identify one product card, not every card on the page.
npm install playwright
npx playwright install chromium
// save as capture-price.mjs
import { chromium } from 'playwright';
const url = 'https://example.com/products';
const cardSelector = '[data-product-id="target-product"]';
const priceSelector = '.price';
const maxScrolls = 40;
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
try {
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
let found = false;
for (let i = 0; i < maxScrolls; i++) {
const card = page.locator(cardSelector).first();
if (await card.count()) {
await card.scrollIntoViewIfNeeded();
try {
await card.locator(priceSelector).waitFor({ state: 'visible', timeout: 3000 });
found = true;
break;
} catch {
// The card may exist before its price is rendered; continue waiting/scrolling.
}
}
await page.evaluate(() => window.scrollBy(0, Math.max(500, window.innerHeight * 0.8)));
await page.waitForTimeout(700);
}
if (!found) {
throw new Error(`Target card or visible price not found after ${maxScrolls} scrolls`);
}
const target = page.locator(cardSelector).first();
await target.scrollIntoViewIfNeeded();
await page.waitForTimeout(500);
await target.screenshot({ path: 'product-price.png' });
await page.screenshot({ path: 'loaded-page.png', fullPage: true });
console.log(JSON.stringify({ url: page.url(), capturedAt: new Date().toISOString() }, null, 2));
} finally {
await browser.close();
}
Run it with node capture-price.mjs. The script intentionally has a finite scroll limit: an infinite feed has no natural end. Adjust maxScrolls, the scroll step, and the wait to fit the site. If the target is in a nested scroll panel, scroll that panel instead of calling window.scrollBy.
Adapt the script to the page
- Find stable selectors: prefer a product ID, data attribute, or semantic selector over a generated class that changes on each load.
- Wait for the actual price: a card can appear before its price or variant selection finishes rendering. Waiting for the price element is more reliable than assuming a fixed delay is always enough.
- Scroll the correct container: some storefronts keep the document still while a catalog panel scrolls. Identify the panel and scroll it, or use
locator.evaluate(el => el.scrollBy(0, 700))on that panel. - Stop at a useful boundary: target card, known product count, or maximum scroll count. Do not treat full-page mode as a way to enumerate an unbounded feed.
- Capture context when needed: save a viewport or page screenshot in addition to the focused card if the product’s listing context matters.
4. Verify and preserve the record
- Check that the product identity and complete displayed price are readable, including currency and any unit or sale-price context shown on the page.
- Check for a variant selector, location, membership, or other visible selection that may affect the displayed price. Capture the state that corresponds to the price you intend to record.
- Save the source URL and capture timestamp alongside the image. For example, use a filename such as
product-123-2026-10-04T120000Z.png. - Keep the original image if you make a crop or annotation for easier reading.
This is practical record-keeping guidance, not a guarantee that a screenshot is independently authenticated or legally sufficient evidence. The browser capture sources describe how to capture page content; they do not establish evidentiary status.
5. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF; its options include full-page capture and selecting one element with a CSS selector. See the ScreenshotNeo API documentation for parameters. For a listing page, use a selector when the target card is addressable and the site has loaded it by capture time.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', new Uint8Array(await res.arrayBuffer()));
Replace the example URL with the listing URL. ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Infinite-scroll content still needs to be available to the capture operation, so check that the target product is included in the output.
Sign up for 1,000 free screenshots a month, with no card.
6. Troubleshooting
| Problem | Likely cause | Fix |
|---|---|---|
| The target product is missing. | The page was captured before scrolling triggered the product load, or the wrong scroll container was used. | Scroll farther, target the listing panel if it has its own scrollbar, wait for the product, then capture again. |
| The card appears but the price is blank. | The price renders later, depends on a selected variant, or has not become visible. | Wait for the price selector, choose the relevant variant if required, and confirm the displayed price before capture. |
| The full-page image stops early. | Only currently available document content was captured, or the site virtualizes/removes off-screen cards. | Load the desired range first. Capture the target card or take multiple finite captures if the page does not retain prior items. |
| The screenshot is extremely tall and hard to read. | Full-page mode includes a long loaded listing. | Capture the product element or viewport instead; keep a separate context capture if useful. |
| Playwright times out during navigation. | The page may keep network connections open or load third-party resources indefinitely. | Use domcontentloaded as in the example, then wait for the specific product and price rather than requiring every network request to finish. |
| Playwright reports that the selector is not visible. | The selector is incorrect, matches a hidden template, or the product is not loaded yet. | Inspect the page markup, choose a selector scoped to the actual product card, and scroll until it appears. |
| The product moves or the price changes between runs. | Listing order, promotions, inventory, or personalized content can change. | Identify the target by a stable product attribute, capture promptly after the price is visible, and record the URL and time. |
7. Performance, reliability, and cost
Infinite-scroll automation takes longer as the target is farther down the feed because the browser must trigger intermediate loads. Keep the scroll loop bounded and stop as soon as the target price is visible. A short wait after each scroll is simple, but waiting on a meaningful product or loading-state selector is usually more robust than relying on one fixed delay for every site.
For reliability, use stable selectors, allow enough time for the target price to render, and inspect the saved image. Pages can virtualize content by removing cards that move far off screen; if so, capture the target as soon as it appears rather than expecting a later full-page capture to preserve it. Browser behavior and site implementations differ, so verify the output for the specific page.
With a local Playwright workflow, account for the browser installation and the time and compute needed to load the page and its assets. A screenshot service can avoid maintaining browser capture infrastructure; ScreenshotNeo’s stated plans are Free for 1,000 shots/month, Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. The service’s billing rule is that only clean shots are billed; its response identifies page verdict and billed status in headers. These plan and billing details do not change the need to confirm that a dynamically loaded target appears in the image.
FAQ
Does a full-page screenshot automatically load every product?
No. Scroll-triggered products must be loaded first. Full-page capture covers the content available to that capture operation.
Should I save the entire listing or just the card?
Save the card for readability. Add a viewport or page capture when the surrounding listing context is useful.
What if the page uses an inner scrolling panel?
Scroll the panel itself until the target card and price appear. Scrolling the browser window may not trigger the listing’s loader.
Can a screenshot prove what a price was?
It records what was displayed in the captured image. Keep the URL and time as context, but do not assume the image alone is independently authenticated.


