# E-commerce Price & Stock Monitor (`afkdigital/price-stock-monitor`) Actor

Extract prices and stock status from public product URLs, CSV/JSON, or sitemaps. Export title, SKU, GTIN, currency, availability, and confidence. Reuse a monitor ID in scheduled runs to detect price and stock changes.

- **URL**: https://apify.com/afkdigital/price-stock-monitor.md
- **Developed by:** [AFK Digital](https://apify.com/afkdigital) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 product extracteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## E-commerce Price & Stock Monitor

Apify Actor for extracting product fields from publicly accessible product pages. Extraction is deterministic and local to the Actor: JSON-LD/schema.org first, embedded JSON second, platform hints third, then standard product metadata and basic page title fallback. It does not use an LLM.

### Features

- Single URL, URL list, inline CSV, JSON, or sitemap URL input, including sitemap indexes.
- Sitemap discovery prioritizes product-named child sitemaps, filters Shopify product paths, and omits sitemap pages without product fields. A sitemap with no recognizable products can return an empty dataset; use direct product URLs for unusual catalogs.
- Product title, SKU, GTIN, price, compare price where exposed, currency, stock state/text, brand, image, timestamp, source/platform and confidence.
- Optional cross-run monitoring using `monitorId`; re-use the same value in scheduled task runs to receive previous/current price and availability changes.
- Per-domain delay, bounded concurrency, retries, Apify Proxy opt-in, and `robots.txt` honoring.
- Shopify, WooCommerce, Magento, BigCommerce hints plus generic structured-data and metadata parsing.
- Dataset rows are JSON; request failures also produce rows with `error` and timestamp.

### Run locally

Requires Node.js 20+ and npm. Install dependencies with `npm install`, then place input JSON in the Apify local `INPUT` store or run on Apify. `npm test` executes offline parser tests. `npm run benchmark` parses a fixed JSON-LD fixture and excludes network time.

```json
{
  "csv": "url\nhttps://public-shop.example/products/item-a",
  "maxPages": 50,
  "concurrency": 3,
  "delayPerDomainSecs": 2,
  "useProxy": false,
  "normalizeCurrency": true,
  "monitorId": "weekly-price-watch"
}
```

Other accepted shapes:

```json
{ "url": "https://public-shop.example/products/item-a" }
```

```json
{ "urls": ["https://one.example/p/1", "https://two.example/p/2"] }
```

```json
{ "json": "[{\"url\":\"https://public-shop.example/p/1\"}]" }
```

```json
{ "sitemapUrl": "https://public-shop.example/sitemap.xml", "maxPages": 100 }
```

The inline CSV parser expects a header row with a `url` column, or URLs in the first column. JSON accepts an array of objects with `url` or `productUrl` fields. Sitemap mode reads sitemap indexes and product URL sets, caps product URLs at `maxPages`, and limits sitemap documents to 50.

### Dataset fields

Core fields are defined in `.actor/dataset_schema.json`; `.actor/output_schema.json` links the dataset and run summary. A monitoring run adds `previous_price`, `current_price`, `change_percent`, `previous_availability`, `current_availability`, `availability_changed`, and `change_timestamp`. Missing fields are omitted or `null`; `in_stock: null` means the page did not expose a clear availability signal. `extraction_confidence` is a heuristic and is not a calibrated probability.

### Monitoring isolation

Monitoring history is written to an Apify Key-Value Store scoped by the initiating Apify user and a SHA-256-derived `monitorId`; this avoids one customer sharing state with another. Set `monitorId` to the same value on each recurring run. If the runtime does not expose an initiating user ID, the run still succeeds and emits ordinary extraction rows, but it will not persist monitoring history. The first check initializes history and has no previous-value fields.

### Safety and limitations

- Input must be public HTTP(S); localhost, private/reserved IP ranges, and domains resolving to private addresses are blocked before requests are queued. Redirect following is disabled; submit the final public product URL directly.
- The Actor honors robots.txt, uses a configurable per-domain pause, and does not bypass login, CAPTCHA, paywalls, or access controls.
- Use only URLs and data you are authorized to access; comply with each site's terms and applicable law. Stop or reduce runs when a site signals blocking or requests less traffic.
- Browser-rendered pages, anti-bot pages, unusual encodings and heavily customized themes may return incomplete data. This release deliberately does not launch a browser or bypass access restrictions.
- The currency toggle infers an ISO code from common currency symbols only when structured data omits currency. It does not perform foreign-exchange conversion.
- This MVP takes URL input or inline CSV/JSON text; it does not accept arbitrary uploaded files as input.
- Adapters are deterministic hints, not guarantees. Store theme changes can affect extraction. Review confidence and error fields.

### Pricing

This Actor is published on Apify Store with pay-per-event pricing. A successful product page normally triggers one `product-extracted` event ($0.0035) and one `page-processed` event ($0.0010). When monitoring is enabled, a successful recurring comparison also triggers `monitoring-check` ($0.0005). Actor Start is $0.00005 per event, with event count based on memory. Platform usage is included. The Store Pricing tab shows the current rates and details.

Failed requests do not incur `product-extracted` charges. Review the event cost cap before running large lists; optional proxy usage can affect results and resource consumption.

### Support and data retention

Open an issue on this Actor's Apify page with a public example URL, expected field and redacted output. Do not include credentials or private data. Input URLs, results and errors are stored in Apify run storage; monitoring history is stored in a named Apify key-value store keyed by initiating user and monitoring ID. These records persist until deleted or expired under the applicable Apify storage settings.

# Actor input Schema

## `url` (type: `string`):

One public product page.

## `urls` (type: `array`):

List of public product URLs.

## `csv` (type: `string`):

CSV with a url column (or first column).

## `json` (type: `string`):

JSON array/object with url or productUrl fields.

## `sitemapUrl` (type: `string`):

Public XML sitemap URL.

## `maxPages` (type: `integer`):

Maximum product pages processed per run.

## `concurrency` (type: `integer`):

Maximum concurrent requests.

## `delayPerDomainSecs` (type: `integer`):

Minimum delay between requests to one domain.

## `useProxy` (type: `boolean`):

Use Apify Proxy when available.

## `normalizeCurrency` (type: `boolean`):

Infer ISO currency code from symbol when needed.

## `monitorId` (type: `string`):

Optional. Reuse the same value across scheduled runs to compute changes.

## Actor input object example

```json
{
  "url": "https://theme-dawn-demo.myshopify.com/products/thelma-sandal",
  "maxPages": 100,
  "concurrency": 5,
  "delayPerDomainSecs": 2,
  "useProxy": false,
  "normalizeCurrency": true
}
```

# Actor output Schema

## `products` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "url": "https://theme-dawn-demo.myshopify.com/products/thelma-sandal"
};

// Run the Actor and wait for it to finish
const run = await client.actor("afkdigital/price-stock-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "url": "https://theme-dawn-demo.myshopify.com/products/thelma-sandal" }

# Run the Actor and wait for it to finish
run = client.actor("afkdigital/price-stock-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "url": "https://theme-dawn-demo.myshopify.com/products/thelma-sandal"
}' |
apify call afkdigital/price-stock-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,afkdigital/price-stock-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/MPFaSALSNFPlTnsGS/builds/s5WTVh02c2mRT7Fnh/openapi.json
