Product Price Checker – Any Store URL (price, stock, old → new) avatar

Product Price Checker – Any Store URL (price, stock, old → new)

Pricing

from $3.00 / 1,000 url checkeds

Go to Apify Store
Product Price Checker – Any Store URL (price, stock, old → new)

Product Price Checker – Any Store URL (price, stock, old → new)

Paste product URLs from ANY online store and get price, stock availability and compare-at price, plus old → new change detection on scheduled runs. Plain HTTP (no browser, no proxies), honest per-URL status: ok / no_price / blocked / not_found / error / skipped.

Pricing

from $3.00 / 1,000 url checkeds

Rating

0.0

(0)

Developer

Thongpoo Supong

Thongpoo Supong

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

20 hours ago

Last modified

Categories

Share

Paste product URLs from any online store, get price, currency, compare-at price and stock availability read straight from the page's own structured data (JSON-LD, microdata, OpenGraph) over plain HTTP — no login, no browser, no proxies. The actor remembers every URL and, on scheduled runs, emits only what changed (old → new): a product whose price moved looks like previousPrice: 24.99 → price: 29.99 in a single row, no dataset joins.

Status: M3 (pay-per-event url-checked, default full list, changes mode for scheduled monitoring).

Billing: url-checked $0.003, charged once per URL whose price was actually read (status: ok). Nothing is charged for blocked, not-found, error or no-price URLs. See PRICING.md.

Input

FieldTypeDefaultNotes
productUrlsarray[string]requiredfull product page URLs, e.g. ["https://singer.com/products/singer-smart-heavy-duty-9120-sewing-embroidery-machine", "https://www.nutrimuscle.com/products/native-iso-blend"]
onlyChangesbooleanfalseTurn ON for scheduled monitoring: emit only rows whose price / compareAtPrice / inStock changed since the previous run, plus URLs never seen. OFF (default) emits a row for every URL. The first run of a URL is always emitted (it builds the baseline).
priceChangedOnlybooleanfalsefurther restrict to rows where price or compareAtPrice changed (availability-only moves are skipped)
maxItemsinteger1000hard cap on dataset rows pushed in one run
fallbackToVisiblePricebooleantrueif no structured data, try a conservative regex on the HTML near a price class/id/itemprop; result gets source: "html-heuristic", confidence: "low"

URLs are fetched with ≤ 2 concurrent requests per host, ≥ 400 ms between requests to the same host, 25 s timeout, 3× retries with backoff on network/5xx/429 (Retry-After honoured, capped at 60 s). Response bodies are capped at 2 MB. Big brands that answer 403/429 with a JS challenge are reported blocked and are never bypassed (no header spoofing, no proxies, no browser).

Scheduling recipe

For a daily or weekly price/stock alert, schedule the actor with onlyChanges turned on:

{
"productUrls": [
"https://singer.com/products/singer-smart-heavy-duty-9120-sewing-embroidery-machine",
"https://ugmonk.com/products/analog-starter-kit-black",
"https://www.nutrimuscle.com/products/native-iso-blend"
],
"onlyChanges": true,
"priceChangedOnly": false,
"maxItems": 1000,
"fallbackToVisiblePrice": true
}
  • The first run of each URL is always emitted (isNew: true) — that is the paid baseline. Every run after that emits only the moves.
  • A scheduled run with nothing changed pushes 0 rows but still checks every URL and writes a SUMMARY (you pay only the ok checks).
  • Keep the actor's named key-value store product-url-price-checker-state between runs — that is the snapshot it diffs against. Wiping it resets every tracked URL to "new".
  • Only runs that actually read a price (status: ok) write the snapshot. A blocked / not-found / no-price check never overwrites the stored entry, so a temporary 403 can't be mistaken for a price change, and the previous known price stays on the row for context.
  • The default input (onlyChanges: false) always yields a row per URL, so an unscheduled "run now" or a daily auto-test is never empty.

Output

One dataset row per URL (one row even for no_price / blocked / not_found / error URLs, so you see every URL in the table). Every row carries the previous values, so a change reads old → new without any join:

FieldTypeExample
url / finalUrlstringhttps://singer.com/products/… / final URL after redirects
statusstringok
titlestring|nullSINGER® Smart Heavy Duty 9120™ …
pricenumber|null499.99
currencystring|nullUSD
compareAtPricenumber|null199.99 (only when it exceeds the price)
inStockboolean|nulltrue
sku / gtin / brandstring|nullfrom structured data
imageUrlstring|nullproduct image
sourcestringjsonld | microdata | opengraph | html-heuristic | none
confidencestringhigh | medium | low
platformHintstringshopify | woocommerce | magento | bigcommerce | prestashop | unknown (informational)
isNewbooleantrue on the first run of a URL
priceChanged / compareAtChanged / availabilityChangedbooleanwhat moved vs the previous run
changeTypestringnew | price | compareAt | availability | unchanged | none
previousPrice / previousCompareAtPrice / previousInStock / previousCheckedAt…|nullprevious run's values
checkedAtISO stringwhen the URL was checked
httpStatusint|nullfinal HTTP status

The dataset ships two built-in views: Overview (every field, image column included) and Changes (old → new) with URL, title, changeType and the before/after price, stock and compare-at columns. A SUMMARY key-value record lists one slim status row per URL (url, finalUrl, status, price, source).

Per-URL statuses (never silent empty output)

StatusMeaning
okprice found and read
no_pricepage answered 200 but no price via any enabled source
blocked403 / 429 / CAPTCHA / JS-challenge page — reported, never bypassed
not_found404 / 410
errornetwork or 5xx failure after retries
skippedthe run stopped before this URL: maxItems reached or the pay-per-event charge limit was hit

Only ok rows are charged (one url-checked), and ok rows are charged even if onlyChanges suppresses the push — the price was read. Every other status is free: you get the loud status row in the table instead. If zero URLs are ok the run fails loudly with the status breakdown — never a silent empty dataset that looks like success.

Pair with the store-monitor

For whole-store monitoring (a Shopify storefront and all its variants in one fetch) use Shopify Price & Stock Monitor — this actor takes individual URLs from any platform and is the cheapest way to watch a short list of specific products.

What is NOT collected

  • No personal data: no emails, phone numbers, names+contacts of private individuals, social profiles, customer/user records or reviews are ever read or output (enforced by a unit test).
  • No login content: pages that need authentication or a password are out of scope.
  • No bypassing: CAPTCHA/paywall/login walls, browsers, proxy rotation or header spoofing are never attempted.

Limitations

  • JS-only stores: if a store renders its price entirely in JavaScript with no JSON-LD/microdata/OpenGraph fallback on the raw HTML, the URL comes back no_price (or html-heuristic low-confidence when it matches). This actor deliberately has no browser.
  • Geo-priced stores: prices that depend on your IP's country come back as whatever the plain HTTP response shows; no geo-spoofing is performed.
  • Blocked big brands: Dyson, LEGO, Sonos, Helly Hansen, Gymshark, Bombas and similar answer 403/429 to plain requests — expected, reported blocked, never evaded.
  • First run = baseline: every URL is new once (or again if the state store was wiped).
  • State = ok checks only: the snapshot is written only when a price is read (status: ok). blocked / no_price / not_found runs never change it, so the last known-good price survives temporary outages without faking a change; an unchanged URL whose row is suppressed by onlyChanges still refreshes its stored timestamp so it is never re-reported as "new" after 120 days.