# JSON-LD Product Price & Stock Monitor (`sarvin/jsonld-product-price-stock-monitor`) Actor

Check public product pages for price and stock changes using schema.org JSON-LD. You bring the URLs. No marketplace search, no login bypass.

- **URL**: https://apify.com/sarvin/jsonld-product-price-stock-monitor.md
- **Developed by:** [Sarvin Behyad](https://apify.com/sarvin) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## JSON-LD Product Price & Stock Monitor

Check public product pages for **price** and **stock** changes by reading the `schema.org` Product JSON-LD already on the page.

This is not a marketplace searcher and not an Amazon/Walmart scraper. You supply the product URLs. The Actor reads public structured data only. It does not log in, does not bypass bot checks, and does not open hidden inventory APIs.

### Who pays for this

- Brands watching their own and competitor **public** product pages
- Agencies running weekly price/stock checks on a known URL list
- Agents that need a small, typed offer record (`price`, `currency`, `availability`, `changeType`)

Repeat usage is the point: schedule the same URL list. The first successful check stores a snapshot. Later runs emit `price_up`, `price_down`, `back_in_stock`, `out_of_stock`, or `unchanged`.

### Input

Minimum: one or more public product-page URLs.

```json
{
  "startUrls": [
    { "url": "https://www.glossier.com/products/boy-brow" }
  ]
}
```

Agents can also send `productUrls` as a plain string list.

| Field | Default | Meaning |
| --- | --- | --- |
| `detectChanges` | `true` | Compare against the last snapshot |
| `snapshotStoreName` | `jsonld-product-snapshots` | Named store for scheduled runs |
| `respectRobotsTxt` | `true` | Skip paths the site disallows |
| `maxConcurrency` | `3` | Keep this low |

### Output

One dataset row per extracted Product offer.

```json
{
  "url": "https://www.glossier.com/products/boy-brow",
  "status": "ok",
  "name": "Boy Brow",
  "price": 22,
  "currency": "USD",
  "availability": "InStock",
  "changeType": "price_down",
  "previousPrice": 24,
  "error": null
}
```

`status` values:

- `ok` — Product JSON-LD with a usable price or availability
- `incomplete_offer` — Product JSON-LD without price and availability
- `no_product_schema` — page has no Product JSON-LD (common on category/configurator pages)
- `http_error` — non-2xx or network failure
- `robots_blocked` — path disallowed by robots.txt
- `parse_error` — HTML fetched but JSON-LD could not be parsed

`changeType` values: `new`, `unchanged`, `price_up`, `price_down`, `back_in_stock`, `out_of_stock`, `offer_changed`.

### What this is not

- Not an Amazon, Walmart, eBay, or Google Shopping crawler
- Not a search scraper. You must already know the product URL
- Not a login or cart tool
- Not a CSS/Playwright fallback. If the shop hides offer data from JSON-LD, the row is `no_product_schema` or `incomplete_offer`

That honesty is the product. Competing “any store” Actors often claim Amazon coverage they cannot keep reliable.

### Pricing

Pay per event:

| Event | What it bills | Suggested price |
| --- | --- | --- |
| `page_checked` | Every URL fetched or blocked after work | $0.001 |
| `product_record` | Every structured product row written | $0.002 |
| `change_detected` | Every row that is not `unchanged` | $0.003 |
| `apify-actor-start` | Platform start (keep default) | $0.00005 |

A 100-URL daily monitor with no changes costs about **$0.30** in events (`100 × $0.001 + 100 × $0.002`). A day with 10 real changes adds about **$0.03**.

Competitor dump parsers charge $2–$5 per 1,000 rows and do not emit change events. This Actor is cheaper on a check and charges extra only when something moved.

Platform cost for Cheerio HTTP is typically well under $0.0005 per URL. Event prices above that leave a positive margin after Apify’s 20% store cut.

### Schedule

1. Save your URL list as the Actor input
2. Keep `snapshotStoreName` the same
3. Run daily or hourly from Apify Schedules
4. Filter the dataset on `changeType != unchanged`

### Limits

- Public HTML only
- JSON-LD Product / Offer / AggregateOffer only
- Some shops put Product schema on category or builder pages without a real offer
- Sites that block anonymous HTML fetch will return `http_error`
- robots.txt is respected when enabled

### Agent / MCP

Required input is only URLs. Output is a flat typed row. No browser, no cookies, no extra steps.

# Actor input Schema

## `startUrls` (type: `array`):

Public product detail pages that already expose schema.org Product JSON-LD. Category, search, and login pages will return no\_product\_schema.

## `productUrls` (type: `array`):

Alternative to startUrls for agents: a simple list of strings.

## `detectChanges` (type: `boolean`):

Compare each URL against the last successful snapshot in a named key-value store. First successful check is marked new.

## `snapshotStoreName` (type: `string`):

Named key-value store used for previous price/stock snapshots. Reuse the same name on scheduled runs.

## `respectRobotsTxt` (type: `boolean`):

Skip URLs disallowed by the site robots.txt. Does not bypass access controls.

## `maxConcurrency` (type: `integer`):

How many pages to fetch at once. Keep low to avoid overloading shops.

## `maxRequestRetries` (type: `integer`):

How many times to retry a failed page fetch.

## `requestTimeoutSecs` (type: `integer`):

How long to wait for each page before failing that URL.

## `userAgent` (type: `string`):

Override the default honest User-Agent. Do not use this to impersonate a logged-in shopper.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.glossier.com/products/boy-brow"
    },
    {
      "url": "https://www.glossier.com/products/balm-dotcom"
    }
  ],
  "detectChanges": true,
  "snapshotStoreName": "jsonld-product-snapshots",
  "respectRobotsTxt": true,
  "maxConcurrency": 3,
  "maxRequestRetries": 3,
  "requestTimeoutSecs": 30
}
```

# Actor output Schema

## `results` (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 = {
    "startUrls": [
        {
            "url": "https://www.glossier.com/products/boy-brow"
        },
        {
            "url": "https://www.glossier.com/products/balm-dotcom"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sarvin/jsonld-product-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 = { "startUrls": [
        { "url": "https://www.glossier.com/products/boy-brow" },
        { "url": "https://www.glossier.com/products/balm-dotcom" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("sarvin/jsonld-product-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 '{
  "startUrls": [
    {
      "url": "https://www.glossier.com/products/boy-brow"
    },
    {
      "url": "https://www.glossier.com/products/balm-dotcom"
    }
  ]
}' |
apify call sarvin/jsonld-product-price-stock-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sarvin/jsonld-product-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/0ua6KlcoiTNzxMKdF/builds/Mx7yicRfcQRFVeUEN/openapi.json
