Product Price Monitor & Scraper - WooCommerce, Schema.org avatar

Product Price Monitor & Scraper - WooCommerce, Schema.org

Pricing

$1.00 / 1,000 product (or change record) delivereds

Go to Apify Store
Product Price Monitor & Scraper - WooCommerce, Schema.org

Product Price Monitor & Scraper - WooCommerce, Schema.org

Product prices, stock, GTIN, brand and variants from WooCommerce stores (public Store API) and product pages with schema.org data (JSON-LD, microdata). One normalised format; monitor mode returns only price and stock changes. $0.001 per product.

Pricing

$1.00 / 1,000 product (or change record) delivereds

Rating

0.0

(0)

Developer

HUG0 S

HUG0 S

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Get clean, structured product data (price, currency, stock, GTIN, brand, variants) from shops that publish it, in one consistent format, and track price and stock changes over time.

$0.001 per product delivered, no start fee. Stores and pages that fail, are blocked or have no product data are free. In monitor mode you pay only for changes.

Quick start: example input, output and cost

Track competitor product prices and stock: WooCommerce stores and schema.org product pages, price-drop and restock alerts. robots.txt respected.

Example input

{
"stores": [
"https://woo.com"
],
"productUrls": [
"https://yithemes.com/themes/plugins/yith-woocommerce-wishlist/"
],
"maxProductsPerStore": 5
}

Cost example: 500 products x $0.001 = $0.50; a daily check of 200 products = $6.00/month.

Five ways in, one output

InputHow it is readGood for
stores: WooCommerce shopsThe WooCommerce Store API, the public API the storefront itself uses: 100 products per requestWhole catalogs, fast and exact (prices in minor units, sale prices, stock)
storeApis: Magento / Shopware (opt-in)Magento 2 GraphQL (anonymous catalog) or Shopware 6 Store API (your sales-channel key)Your own Magento/Shopware shop, or a store you have permission for
productUrls: product pages of any shopschema.org Product data the shop publishes for search engines: JSON-LD first, then microdata, then OpenGraph product tagsCompetitor or supplier pages on Shopify, Magento, Shopware, PrestaShop, custom shops
discoverFromSitemap (opt-in) on storesRobots-allowed sitemaps (incl. nested indexes); URLs matching productUrlPattern are fetched as product pagesAuto-finding product URLs when you only have the shop homepage
productFeeds: open product feed URLsGoogle Merchant RSS 2.0 (g: namespace), Atom, or CSV/TSV (gzip OK); robots-checkedShops that publish a comparison-shopping feed; whole catalog in one request

What you get (one item per product)

source (woocommerce-store-api / magento-graphql / shopware-store-api / json-ld / microdata / opengraph / google-merchant-feed), store, productId, productKey, url, title, brand, sku, gtin, mpn, categories, price, priceMinor, currency, regularPrice, onSale, discountPct, priceMin, priceMax, availability, inStock, stockQuantity, condition, imageUrl, images, rating, variantsCount, variants[], attributes, unitPrice, shippingDetails, returnPolicy, priceValidUntil, priceEur / priceMinEur / priceMaxEur / regularPriceEur + fxDate, fxRate, fxStatus, fxNote (optional, convertToEur), descriptionText (optional), quality, priceCheck, matchGroup (optional, matchAcrossStores), changeType, changes, fetchedAt, schemaVersion

Normalisation:

  • Prices: "1.299,00", "1,299.00", "€ 9" and 12.5 all become numbers, plus priceMinor (cents; whole units for JPY and other zero-decimal currencies).
  • Currency: ISO 4217 codes; symbols such as €, £, zł and CHF are mapped. Ambiguous symbols such as $ and kr are flagged in quality.warnings.
  • Availability: in_stock, out_of_stock, preorder, backorder, limited, discontinued or unknown, plus inStock true/false.
  • Variants: from schema.org ProductGroup/hasVariant, multiple offers, or WooCommerce variations. Attributes are included; turn on maxVariationsPerProduct to get each variation's price.
  • Discounts: sale prices from WooCommerce or schema.org StrikethroughPrice give regularPrice, discountPct and onSale.
  • GTIN: the check digit is validated. Images become absolute URLs.

Data-quality block (quality): score 0-100, a missing field list, warnings (invalid GTIN, ambiguous currency, thousands separator guessed...), and filledFrom when a gap was filled from OpenGraph tags. Use minQualityScore to skip and not pay for weak records.

Merchant fields (new in 1.1): when the page publishes them in JSON-LD Offers, you also get unitPrice (from UnitPriceSpecification), shippingDetails (rate, destination, handling/transit times), returnPolicy (category, days, method, fees, country) and priceValidUntil. Variant attributes come from variesBy / additionalProperty and WooCommerce term slugs.

Price cross-check (priceCheck, new in 1.1): on single-product pages the JSON-LD price is compared with the microdata and OpenGraph prices on the same page. status is match, mismatch or unverified; mismatches add a quality warning and set likelyCentsError when the two differ by exactly x100 (a common feed bug). Useful for auditing your own shop's structured data.

Monitor mode (price & stock tracking)

Set monitorName (e.g. competitors-daily) and schedule the actor.

  • The first run returns everything as new.
  • Later runs return only price-drop, price-increase, back-in-stock, out-of-stock, changed, new and removed, with changes holding the old and new values and the % change.
  • Removed products are reported only after a complete catalog read, or when a product page answers 404/410.
  • Change thresholds (new in 1.2): minPriceChangePercent (e.g. 2) and/or minPriceChangeAmount (e.g. 0.50, in the product's currency) ignore small price moves, so you are not charged for noise. If both are set, a move must pass both. The baseline stays at the last reported price, so a slow drift (1 % a day) is reported once it adds up. Stock changes are always reported; when a stock change comes with a small price move, changes.price.belowThreshold is true. The same thresholds apply to regularPrice. Defaults (0) report every change, exactly as before. Ignored moves are counted in SUMMARY belowThreshold.
  • Price history (new in 1.2): set keepPriceHistory to true and every returned change is also appended as one compact row (price, old price, % change, stock, time, run ID) to a named dataset product-tracker-<monitorName>-history in your account. Run the monitor on a schedule and you get a full price history for charts or spreadsheets. History rows are free; the dataset counts toward your own Apify storage. Off by default.
  • ALERTS record (new in 1.2): every monitor run writes an ALERTS record to the run's key-value store with back-in-stock products and price drops of at least alertMinDropPercent % (0 = every drop). Point a webhook, Make, n8n or Zapier step at it to get only the changes worth acting on. Free.

Sitemap discovery (new in 1.2)

Set discoverFromSitemap to true and put the shop homepage (or a sitemap URL) under stores. The actor:

  1. Reads Sitemap: lines from robots.txt (or falls back to /sitemap.xml / /sitemap_index.xml).
  2. Walks nested sitemap indexes, robots-checked on every file.
  3. Keeps URLs matching productUrlPattern (default regex product|/p/|/products/) on the same host, up to maxSitemapUrls (default 500).
  4. Fetches those pages for schema.org Product data like productUrls.

Billing: sitemap downloads are free. You pay only for products (or change records) delivered, same as always. Off by default so existing runs are unchanged.

Magento GraphQL & Shopware Store API (new in 1.3)

Put entries under storeApis (opt-in; existing stores WooCommerce path is unchanged):

{
"storeApis": [
{ "url": "https://your-magento.example", "platformApi": "magento-graphql" },
{ "url": "https://your-shopware.example", "platformApi": "shopware-store-api" }
],
"shopwareAccessKey": "YOUR_SALES_CHANNEL_KEY",
"maxProductsPerStore": 100
}
  • Magento 2 / Adobe Commerce GraphQL: anonymous POST /graphql catalog query (no login, no customer/cart mutations). Page size capped at 100. ConfigurableProduct variants included.
  • Shopware 6 Store API: GET /store-api/context + POST /store-api/product. Requires your sales-channel sw-access-key (Admin > Sales Channels > API access) or a key the vendor publishes for a public demo. Prefer the top-level shopwareAccessKey secret field (per-entry key also works); it is never taken from page source or JavaScript.
  • robots.txt: fail-closed for /graphql, /store-api/context and /store-api/product (POST with no query string, so Disallow: /*? does not block them).
  • Auto: platformApi: "auto" (default) probes Magento GraphQL; if shopwareAccessKey is set it uses Shopware first.
  • Monitor mode: productKey is stable (store + platform product id).
  • Billing: API pages are free. You pay only for products (or change records) delivered, same $0.001, no start fee.
  • Compliance: use only on stores you own or have permission for (or clear vendor demos). Identifying User-Agent with publisher contact details, ≤1 req/s/host, no proxies, no logins, no anti-bot bypass, no personal data.

Open product feeds (Google Merchant, new in 1.3)

Put public feed URLs under productFeeds (RSS 2.0 with the Google g: namespace, Atom, or CSV/TSV with Google Merchant column names: id, title, description, link, image_link, price, sale_price, availability, brand, gtin, mpn, condition, item_group_id). Gzip is fine. The actor:

  1. Checks robots.txt for every feed URL (404 = allow; 401/403/network fail = disallow), same fail-closed rule as pages.
  2. Parses items into the same normalised product records (sale_price becomes price with regularPrice; availability in_stock / out_of_stock / preorder / backorder; item_group_id groups variants).
  3. Caps at maxFeedItems (default 1000) per feed.
  4. Works with monitor mode (productKey is stable across runs).

Billing: feed downloads are free. You pay only for products (or change records) delivered. Descriptions redact e-mails and phone numbers. XML is parsed with defusedxml (XXE/DTD blocked).

EUR prices with ECB reference rates (optional)

Set convertToEur to true to add priceEur, priceMinEur, priceMaxEur and regularPriceEur to every product, converted with the latest European Central Bank euro foreign exchange reference rates (about 30 currencies, published each working day around 16:00 CET). One free fetch per run, no extra charge. Each converted item carries fxDate, fxRate (units of the shop currency per 1 EUR), fxStatus and fxNote. Currencies without an ECB rate get fxStatus: "no-ecb-rate"; nothing is guessed. If the ECB file can't be reached, products are still delivered without EUR fields and the problem is listed in ERRORS.

Source: European Central Bank. The EUR fields are computed by this actor (price divided by the reference rate, rounded to 2 decimals). The reference rates are published for information only, are not meant for transactions, and are available free of charge at ecb.europa.eu.

Compare the same product across stores (optional, new in 1.4)

Set matchAcrossStores to true when you read several shops in one run (any mix of stores, storeApis, productUrls, sitemap discovery and productFeeds). After all products are read, the actor groups offers of the same item from different stores and writes a free MATCHES record to the run's key-value store.

  • Strong identifiers only: a valid GTIN (GTIN-8/12/13/14 normalised to GTIN-14 with the check digit verified, so a UPC-12 and an EAN-13 of the same item match), or, for products without a valid GTIN, the exact brand + MPN (case and spaces ignored; placeholders such as N/A are ignored). Titles are never compared, so there are no fuzzy false matches. GTINs with a wrong check digit are not matched.
  • A group needs at least 2 different stores (same-shop duplicates do not count).
  • Each product gets matchGroup (e.g. gtin:00036000291452 or mpn:bosch|gsr 18v-55, stable across runs) or null when no other store sells it.
  • MATCHES: {groupCount, matchedProducts, productsConsidered, productsWithIdentifier, note, groups: [{matchGroup, matchedBy, gtin, brand, mpn, title, storeCount, offerCount, currencies, priceComparable, priceBasis, lowestPrice, highestPrice, priceSpreadPct, cheapest, cheapestInStock, offers[]}]}, sorted by the biggest price spread first.
  • Prices are compared honestly: in EUR when convertToEur gave every offer an ECB EUR price, otherwise only when all offers share one currency. Mixed currencies without EUR conversion are listed but not compared (priceComparable: false with a note).
  • Works in monitor mode too: change records carry matchGroup, and MATCHES covers the current offers of the whole monitor (changed and unchanged).
  • Billing unchanged: matching and MATCHES are free; you pay only for products (or change records) delivered. MATCHES lists only products you were charged for (now or as an earlier monitor baseline), so the maximum cost per run is respected. With matching on, products are saved at the end of the run (after all shops are read) instead of shop by shop. Off by default: output is exactly as before.

Own-shop price position (optional, new in 1.4)

Add your own shop's host to ownStoreHosts (for example ["myshop.example"]; a full URL works too, www. and paths are ignored, a port is kept) together with matchAcrossStores: true, and read your shop plus the competitors you are allowed to monitor in the same run. The actor then writes a free PRICE_POSITION record: for every matched product your shop sells, where you are the cheapest or the most expensive, and by how much.

  • One row per match group in which your shop has an offer: matchGroup, matchedBy, gtin, brand, mpn, title, position, priceComparable, priceBasis, currencies, ownPrice, own, ownOfferCount, ownInStock, competitorStoreCount, competitorOfferCount, pricedCompetitorStoreCount, rank, tiedCompetitors, cheapestCompetitor, cheapestInStockCompetitor, highestCompetitor, deltaToCheapestAmount, deltaToCheapestPct, deltaToHighestAmount, deltaToHighestPct, competitors[] (plus note when prices cannot be compared).
  • position is one of cheapest, tied-cheapest, mid, most-expensive, only-offer (no competitor price) or no-comparable-price (your offer has no price, or mixed currencies without convertToEur). deltaToCheapestAmount = your price minus the cheapest competitor's price, so a negative value is how far below the next competitor you are. rank 1 = cheapest store.
  • Top level: ownStoreHosts, ownProducts, ownProductsMatched, ownProductsUnmatched, rowCount, positions (count per position), fxDate, note, rows (sorted with the most over-priced products first) and schemaVersion. Plain JSON with stable field names, so a webhook on run success can fetch PRICE_POSITION from the run's key-value store and act on it.
  • Same matching and price rules as MATCHES (GTIN or brand + MPN only; EUR when convertToEur gave every offer an ECB EUR price, otherwise only within one currency). Several own hosts (e.g. your .nl and .com shop) count as one seller; your lowest own offer is used.
  • Needs matchAcrossStores: true (otherwise the run stops with a free input error). If no delivered product comes from your host(s), a warning is added to ERRORS. Works in monitor mode (the report covers the monitor's current offers).
  • Free; billing unchanged. Only products you were charged for are used, so the maximum cost per run is respected. SUMMARY gains pricePositionRows, ownCheapest (cheapest + tied-cheapest) and ownMostExpensive when on. Off by default: output is exactly as before.

Export a Google Merchant Center feed for your own shop (optional, new in 1.4)

Set merchantFeedExport to true (and includeDescription to true, because Google requires a description) to turn the products of a run into a ready-to-upload Google Merchant Center product feed. Meant for your own shop: read it with stores, storeApis, productUrls, sitemap discovery or productFeeds, then upload or schedule-fetch the file in Merchant Center.

  • MERCHANT_FEED (key-value store, tab-separated text): Google attribute names id, item_group_id, title, description, link, image_link, additional_image_link, availability, price, sale_price, brand, gtin, mpn, identifier_exists, condition, product_type, color, size. Prices are written as 49.90 EUR; a discount becomes price = regular price plus sale_price. Variants become one row each with item_group_id, color and size.
  • MERCHANT_FEED_ISSUES (key-value store, JSON): every product or variant Google would reject (missing description, image, link, price or a mappable availability, duplicate id, unknown variant price), with the reason, plus warnings (GTIN with a wrong check digit left out, no GTIN and no brand + MPN so identifier_exists = no, titles over 150 characters). Rejected rows are not put in the feed.
  • Nothing is guessed: no invented descriptions, GTINs, conditions or variant prices.
  • Free; billing unchanged. The feed covers only products you were charged for in this run (or, in monitor mode, the whole current catalogue of the monitor), so the maximum cost per run is respected. Off by default: output is exactly as before. SUMMARY gains merchantFeedRows / merchantFeedRejected when on.

Limits

  • Only shops that publish product data openly (WooCommerce/Magento/Shopware public APIs, schema.org markup, open feeds). Amazon, eBay and other marketplaces are not supported.
  • Prices are what the shop shows to an anonymous visitor; customer-group or logged-in prices are not visible.

Responsible use (public product data only)

  • Only public product data from shops that allow automated access. robots.txt is checked for every URL (RFC 9309, fail-closed). Disallowed stores and pages are skipped and listed in ERRORS, free of charge.
  • About 1 request per second per site, with an honest User-Agent (ProductFeed/1.0; digivibetech.com/support). No proxies, no cookies, no logins. HTTP 401/403/429 blocks are reported and never bypassed.
  • No personal data: reviews, review authors and Person nodes are dropped (only the aggregate rating is kept). Descriptions are optional, with e-mails and phone numbers redacted.
  • Check each shop's terms of use before monitoring it; using the data is your responsibility. Google Shopping, Amazon and other sites that forbid automated access are not supported.

Pricing

EventPrice
Product (or change record) delivered$0.001

Example: a daily monitor of 2,000 products where 3% change each day costs about $0.06 per day.

Output

  • Dataset: one item per product or change, with an "overview" table view.
  • ERRORS (key-value store): every input that failed, with the reason (not charged).
  • SUMMARY (key-value store): counts of stores, pages, feeds / feedItems, products seen, delivered, unchanged, below-threshold (monitor mode), history rows, alerts, sitemaps / sitemapUrls (discovery), matchGroups / matchedProducts (with matchAcrossStores), pricePositionRows / ownCheapest / ownMostExpensive (with ownStoreHosts), filtered and billed.
  • MATCHES (key-value store, only with matchAcrossStores): cross-store match groups with lowest/highest price, cheapest store and price spread (free).
  • PRICE_POSITION (key-value store, only with ownStoreHosts): your own shop's price position per matched product: cheapest / most expensive and by how much (free).
  • MERCHANT_FEED / MERCHANT_FEED_ISSUES (key-value store, only with merchantFeedExport): Google Merchant Center TSV feed and the list of rejected products with reasons (free).
  • ALERTS (key-value store, monitor mode): {monitorName, alertMinDropPercent, count, alerts: [{alert: "price-drop" | "back-in-stock", productKey, title, url, store, oldPrice, price, pctChange, currency, availability}]}.

Roadmap

Requests are welcome via Issues.

Covered by tests (unit + end-to-end with a mock store).