# Product Price & Stock Audit + JSON-LD Extractor (`nefissiez/product-offer-extractor`) Actor

Audit catalog prices and stock against public Product JSON-LD. Match SKU/GTIN, flag differences and unknowns, export flat CSV with source evidence. Or extract offers as JSON. $0.004 per parsed page.

- **URL**: https://apify.com/nefissiez/product-offer-extractor.md
- **Developed by:** [uras aydinlioglu](https://apify.com/nefissiez) (community)
- **Categories:** E-commerce, SEO tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 product page parseds

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?

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

## Product Price & Stock Audit + JSON-LD Extractor

Compare your catalog's expected price and availability with the Product JSON-LD published on a public product page. Get **MATCH**, **MISMATCH**, or **UNVERIFIABLE** for each catalog row, with SKU/GTIN, expected and observed values, currency, timestamp and an exact source pointer.

For ecommerce teams and feed agencies checking a catalog before or after a price update. This checks published structured data, not the rendered page, checkout or Google Merchant Center approval.

### Try before running a batch

[Try the free comparison demo and download input examples](https://nefissiez-tools.uras-aydinlioglu.chatgpt.site/#demo). The demo compares editable example data in your browser; it does not crawl websites or send your data to us.

### Audit mode

Provide `catalog` with 1–100 rows spanning at most 30 unique URLs. Each row needs a `sku` or `gtin` and at least one expected value. Use strings for identifiers to preserve leading zeros.

```json
{
  "catalog": [{
    "id": "campaign-row-1",
    "url": "https://scrapeme.live/shop/Bulbasaur/",
    "sku": "4391",
    "expectedPrice": "63.00",
    "currency": "GBP",
    "expectedAvailability": "in_stock"
  }]
}
```

This public demo-store input is a starting example; the page can change. Replace it with your own catalog values. `expectedPrice` accepts plain nonnegative decimals with up to six decimal places, without currency symbols or thousands separators. `currency` is required with a price. Stock values: `in_stock`, `out_of_stock`, `preorder`, `backorder`. Prices compare exactly; currencies are never converted. Use the currently effective sale price when applicable.

### What makes a result trustworthy?

- SKU matching is exact and case-sensitive. GTIN matching preserves digits and normalizes leading-zero padding. When both identifiers are supplied, both must match.
- Multiple matching products, multiple offers, aggregate price ranges, truncated extraction, missing identifiers and missing fields are reported explicitly. The Actor never guesses which variant or cheapest offer you meant.
- `MATCH` means every requested field matched. `MISMATCH` means at least one definite difference; another field can still be unknown. Check the individual `priceStatus` and `availabilityStatus`.
- Each verifiable offer includes the source script index and JSON Pointer. Network failures are separate from product differences.

### Results and CSV

The default dataset contains one row per unique page, with normalized `products` and an `audits` array. The run's key-value store contains `AUDIT_REPORT` (flat JSON), `AUDIT.csv` (flat spreadsheet-ready report), and `SUMMARY` (counts by verdict and any spending-limit stop). Open them from the run's Output tab. A spending cap can stop the run before every input is processed; inspect `SUMMARY.stopReason`.

CSV columns include `id`, `sku`, `verdict`, `expectedPrice`, `observedPrice`, `priceDelta`, both currencies, expected/observed availability, reasons, timestamp and source evidence. Leading formula characters are escaped for safe spreadsheet import.

### Extraction mode — existing inputs still work

```json
{"urls":["https://scrapeme.live/shop/Bulbasaur/"],"maxItems":50}
```

Returns Product names, brand, SKU/GTIN, offers, images and source evidence. `maxItems` limits products per page (1–50). Both modes can be combined; repeated URLs are fetched once within a run.

### Pricing

**$0.004 per successfully parsed page with at least one Product record** ($4 per 1,000 such pages). One page can contain multiple catalog rows or products, for the same page charge. An audit may be `UNVERIFIABLE` and still incur this page charge if Product data was extracted. Empty pages, blocked requests and fetch failures have no Actor event charge. Apify platform charges and the price shown for your plan in Console apply. Set a maximum charge before running.

### Repeat and automate

Save the input as an Apify Task, then create an Apify Schedule for a recurring audit. Refresh the expected values when your catalog changes; a saved input is a snapshot and does not sync automatically. Read the output through the Apify API or download CSV. [Download a Python API runner and JSON examples](https://nefissiez-tools.uras-aydinlioglu.chatgpt.site/#start).

### Coverage and limits

Reads public, server-delivered HTML and Product JSON-LD only. No JavaScript rendering, login, proxy bypass, Microdata, checkout verification, geolocation or inventory quantities. A successful comparison does not guarantee Merchant Center approval or agreement with the visible page. Source JSON-LD can itself be stale. Public IPv4 HTTP(S) only; robots.txt is respected. No LLM API key or external AI service is needed.

### Help and evidence

Use this Actor's **Issues** tab to report an unsupported public page. Include a reproducible URL and redacted input/output; never post API tokens or private catalog data. We do not promise a response-time SLA.

Google documents the underlying issue: [mismatched product price](https://support.google.com/merchants/answer/12159029?hl=en). This Actor is independent of Google and Apify.

# Actor input Schema

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

Optional in audit mode. 1–30 public product URLs for extraction. Provide URLs, catalog rows, or both.

## `maxItems` (type: `integer`):

Stop after this many normalized Product records.

## `catalog` (type: `array`):

1–100 objects across at most 30 URLs. Each needs url, sku or gtin, and expectedPrice + currency or expectedAvailability. See README for an example. Leave empty by omitting this field in extraction mode.

## Actor input object example

```json
{
  "maxItems": 50,
  "catalog": [
    {
      "id": "demo-product",
      "url": "https://scrapeme.live/shop/Bulbasaur/",
      "sku": "4391",
      "expectedPrice": "63.00",
      "currency": "GBP",
      "expectedAvailability": "in_stock"
    }
  ]
}
```

# Actor output Schema

## `results` (type: `string`):

No description

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

No description

## `audit` (type: `string`):

No description

## `csv` (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 = {
    "catalog": [
        {
            "id": "demo-product",
            "url": "https://scrapeme.live/shop/Bulbasaur/",
            "sku": "4391",
            "expectedPrice": "63.00",
            "currency": "GBP",
            "expectedAvailability": "in_stock"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nefissiez/product-offer-extractor").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 = { "catalog": [{
            "id": "demo-product",
            "url": "https://scrapeme.live/shop/Bulbasaur/",
            "sku": "4391",
            "expectedPrice": "63.00",
            "currency": "GBP",
            "expectedAvailability": "in_stock",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("nefissiez/product-offer-extractor").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 '{
  "catalog": [
    {
      "id": "demo-product",
      "url": "https://scrapeme.live/shop/Bulbasaur/",
      "sku": "4391",
      "expectedPrice": "63.00",
      "currency": "GBP",
      "expectedAvailability": "in_stock"
    }
  ]
}' |
apify call nefissiez/product-offer-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nefissiez/product-offer-extractor"
        }
    }
}

```

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/pH7MWTqf2yWzuFPQt/builds/BNjY6E67ELrkHfUtk/openapi.json
