# Product Price & Availability Evidence API (JSON-LD) (`soilair/public-product-offer-price-evidence-api`) Actor

Extract declared product offers, decimal prices, currencies and availability from public JSON-LD HTML, with hashes and explicit gaps. No browser or checkout assumptions.

- **URL**: https://apify.com/soilair/public-product-offer-price-evidence-api.md
- **Developed by:** [Salih Can Kurnaz](https://apify.com/soilair) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 offer observations

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

## Product Price & Availability Evidence API (JSON-LD)

Turn public product-page JSON-LD into selected, exportable offer rows: product name/SKU/brand, direct decimal price, declared currency, availability, and source block/pointer. Suitable for inexpensive price-declaration monitoring across storefront platforms. Unlike a general SEO metadata extractor, output has one row per inline offer and keeps AggregateOffer low/high ranges separate.

### Quick start

```json
{"urls":["https://scrapeme.live/shop/Bulbasaur/","https://scrapeme.live/shop/Ivysaur/"],"maxItemsPerPage":20}
```

Example sources are a public scraping demo, not customers or sales. Download the dataset as JSON, CSV or Excel. POST input to https://api.apify.com/v2/actors/soilair~public-product-offer-price-evidence-api/runs with your Apify authorization header, poll the returned run, and read dataset plus OUTPUT/SNAPSHOT. Never put API tokens inside a source URL.

### What is returned

Recognizes lexical Product and inline Offer/AggregateOffer types, including schema.org HTTP/HTTPS type URLs and type arrays, anywhere within static JSON-LD object/list graphs. ProductGroup-only nodes are not expanded unless they contain explicit Product nodes. No JSON-LD context expansion, compact-prefix resolution, remote contexts, @id reference resolution or linked document fetching. Inline offers without a recognized explicit type are skipped with a coverage warning.

Offer declaredPrice is a nonnegative, plain decimal string from the direct price field. AggregateOffer yields declaredLowPrice/declaredHighPrice separately; it is not converted to a single purchasable price. Decimal commas, exponent notation, nonfinite values, negatives, booleans and unsupported structures yield null with row warnings. Currency is the original uppercase three-letter declaration, syntactically checked, not verified against a currency registry. A row with missing/unsupported price or currency is still a billable offer observation with warnings. priceSpecification is not used as a fallback; tax, delivery, unit pricing, discounts, exchange rates and price validity are not computed. URL and availability fields are source strings, not fetched, authenticated or independently verified.

These are publisher declarations, not checkout prices, measured stock quantities or product authenticity. Stale or inaccurate structured data can differ from the rendered page and checkout. No browser, login, captcha workaround, cart action, screenshots, availability guarantee or schema/rich-result validation. UTF-8 HTML decoding only; non-UTF-8 pages may show replacement characters. Related/recommended Product nodes may also appear; this is not a main-product classifier.

### Coverage and changes

Up to five distinct normalized pages, 100 offer rows per page (default20), 50 JSON-LD blocks, 5000 traversed nodes per block and depth24. The HTTP decoded-body cap is 2 MiB per response. Invalid/duplicate-key/nonfinite/oversized blocks, unsupported offer references, missing inline offers and row caps are reported as incomplete coverage. A zero-offer page can be a successful zero-result observation; this does not prove absence of products. Always read OUTPUT.errors, sources, warnings and complete. complete describes only the declared bounded extraction, not a complete store catalog or verification of prices.

Pass SNAPSHOT.previousHashes as previousHashes on the next run. UNCHANGED/CHANGED compare selected declaration fields. Keys bind final source URL plus block/JSON pointer/offer position; markup reordering or redirects can create new identities. FIRST_SEEN means absent from your provided baseline, not a newly listed product. No global product matching, missing-row deletion inference, hidden state or automatic schedule. Rows include observedAt; no full description, reviews, seller profile, cookies or raw HTML is stored.

### Pricing and requests

Publication price: $0.0015 per emitted offer observation ($1.50 per1000), plus $0.005 Actor start at256MB. Higher memory can multiply startup events. Platform usage is included in this event model. Failed pages emit no result event; startup can still apply. Missing-price offer rows can be charged. Budget-limited output reports PARTIAL_BUDGET and complete=false. Check effective Store pricing before running.

Public HTTP(S) ports80/443 only, no credentials, exclusively public DNS addresses, connection IP pinning, TLS hostname verification, at most five redirects, approximately95-second fetch budget. Fetches robots.txt and uses Python RobotFileParser; parser semantics may differ from actual crawlers. Disallowed/unavailable robots policy and source errors are not bypassed. No proxies or paid upstream. Only supplied pages, their HTTP redirects and corresponding robots files are fetched; product/offer URLs, images and remote contexts are never followed. Websites see the request and User-Agent. Input/results stay in your Apify storage under your retention settings; no additional tracking or customer-data export.

Developed with AI assistance. Fixture tests and public-demo/cloud checks qualify the stated behavior, not universal store compatibility. Support: share a run ID and the expected field, without credentials/private data.

References: https://schema.org/Product · https://schema.org/Offer · https://schema.org/AggregateOffer

# Actor input Schema

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

1..5 public HTML URLs; duplicate normalized URLs are requested once. No credentials/private sources.

## `maxItemsPerPage` (type: `integer`):

1..100 inline offer observations per page. A cap reports incomplete coverage.

## `previousHashes` (type: `object`):

Up to 2000 positional itemKey to SHA256 mappings; no deletion inference.

## Actor input object example

```json
{
  "urls": [
    "https://scrapeme.live/shop/Bulbasaur/",
    "https://scrapeme.live/shop/Ivysaur/"
  ],
  "maxItemsPerPage": 20,
  "previousHashes": {}
}
```

# Actor output Schema

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

No description

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

No description

## `snapshot` (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 = {
    "urls": [
        "https://scrapeme.live/shop/Bulbasaur/",
        "https://scrapeme.live/shop/Ivysaur/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("soilair/public-product-offer-price-evidence-api").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 = { "urls": [
        "https://scrapeme.live/shop/Bulbasaur/",
        "https://scrapeme.live/shop/Ivysaur/",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("soilair/public-product-offer-price-evidence-api").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 '{
  "urls": [
    "https://scrapeme.live/shop/Bulbasaur/",
    "https://scrapeme.live/shop/Ivysaur/"
  ]
}' |
apify call soilair/public-product-offer-price-evidence-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,soilair/public-product-offer-price-evidence-api"
        }
    }
}
```

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/uIDe3A74fYKCo05GN/builds/NvYKHOzUmocGQYVLN/openapi.json
