# Shopify Feed vs Page Audit - HTML Snapshots (`ysond/my-actor`) Actor

Compare feed price, stock and variant identity with saved Shopify HTML JSON-LD. Explicit country checks and exportable evidence. No crawling or store access.

- **URL**: https://apify.com/ysond/my-actor.md
- **Developed by:** [Cleyson Dias](https://apify.com/ysond) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 results

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

## Shopify Feed vs Page Audit — HTML Snapshots

Compare product-feed expectations with saved Shopify HTML snapshots: variant identity, price, currency and availability, with an explicit target-country check.

**This is a snapshot auditor, not a live Shopify scraper.** Supply the HTML source in the input. Providing only a store URL is not sufficient. No browser, proxy, JavaScript execution, login, Google Merchant Center connection or LLM is used for the audit.

### Who this is for

Developers and catalog/Shopping teams that already have an authorized feed and saved HTML, and want a repeatable static QA step before reviewing a catalog change. The output is a review queue, not an automatic feed repair. If you only have a store URL, or need visible-price/checkout validation, this MVP is not a suitable substitute for a live auditor.

### What you get

- One dataset item per processed feed row, with `match`, `mismatch`, `unknown` or `invalid_input`.
- Evidence and field-level reasons for differences, instead of automatic store edits.
- `REPORT.json`: complete comparison and snapshot fingerprints.
- `REPORT.csv`: CSV with spreadsheet-safe identifiers.
- `REPORT.html`: self-contained report in Portuguese, without scripts or external resources.
- `OUTPUT`: run totals, omitted-row count and budget-limit information.

### Quick start in Apify Console

Use the prefilled synthetic example or paste `INPUT.example.json` in the JSON input editor. It contains three **test scenarios, not three independently captured products**:

1. `reference`: expected `match`.
2. `wrong-price`: expected `mismatch` on price.
3. `other-country`: expected `unknown` because the snapshot declares US while the target is CA.

For your own authorized data, replace the example rows with your feed fields and saved page source. The program does not fetch `link`, `html`, images or other resources.

### Input fields

| Field | Required | Meaning |
|---|---|---|
| `products` | Yes | Array of 1–100 product snapshots |
| `products[].id` | Yes | Unique, nonempty feed-row identifier, max 256 characters |
| `products[].sku` | Yes | Exact SKU identifying the Product in JSON-LD, max 256 characters |
| `products[].link` | Yes | HTTP(S) destination URL to compare, not to visit |
| `products[].price` | Yes | Decimal text plus currency, e.g. `19.90 USD`; no decimal comma or exponent |
| `products[].availability` | Yes | `in_stock`, `out_of_stock`, `preorder` or `backorder` |
| `products[].target_country` | Yes | Two uppercase letters, e.g. `US`; format validation, not ISO-table membership |
| `products[].html` | Yes | Authorized saved HTML source, 1 byte to 1 MiB UTF-8, not a URL |
| `products[].variant_id` | No | Numeric variant identifier supplied as a string |
| `products[].product_form_id` | No | Exact ID of the main product form; recommended when the page includes other product controls |
| `notice` | No | Provenance/context note, max 1000 characters; not a certification |

The serialized input is limited to 8 MiB by the Actor. Platform input limits may be lower. Do not include `page_path`, arbitrary local paths, cookies, proxy settings or unsupported fields. Duplicate IDs and structural/size errors reject the run. Invalid product metadata that reaches the auditor is reported as `invalid_input` instead of approving it.

### How comparison works

The parser requires one Product matching the SKU. If there are multiple Offers, exactly one must have the expected SKU. It never chooses the first or cheapest offer as a substitute for identity.

When supplied, variant ID, variant URL and static main-form controls are cross-checked. A matching SKU does not erase a contradictory variant URL. Without a form scope, unrelated product controls can make selection inconclusive; a missing Offer URL or optional variant ID does not become independent proof of identity.

The Actor requires an explicit target country. A restricted static `Shopify.country` declaration in the HTML must provide unambiguous accepted evidence before contextual price, currency and stock are compared. The URL country parameter alone does not prove the context. Missing, unsupported or conflicting declarations produce an inconclusive country check; independent variant contradictions can still produce `mismatch`.

### Interpret results correctly

- `match`: consistent within the fields and evidence compared, not Google approval or guaranteed purchasability.
- `mismatch`: at least one difference was proved within the snapshot. Inspect all findings; other fields may remain unknown.
- `unknown`: evidence missing, ambiguous or outside the requested context. Do not treat it as a pass.
- `invalid_input`: metadata does not meet the comparison contract.

The program compares supported JSON-LD, **not the visible price text**: visible text can disagree while the structured-data result remains `match`. A price supplied only through `Offer.priceSpecification` is not supported and remains inconclusive. `template` and `noscript` content is not accepted as active evidence. Availability is compared strictly, not using Google's automatic-update compatibility rules.

The program does not check checkout, delivery, tax, every market, runtime JavaScript changes, all Shopify themes, arbitrary structured-data formats or Merchant Center policy compliance. Admin stock quantity is not global availability. Snapshots can be stale; fingerprints identify the submitted UTF-8 content, not its authenticity or freshness. `feed_sha256` identifies the adapter-generated manifest, not the original platform INPUT bytes.

### Data handling

Use only data you own or are authorized to process. **Remove preview-access keys, tokens, passwords, signed access URLs, personal data and secrets before submitting HTML or URLs.** This Actor is not a general secret-redaction service.

Apify stores the input you submit. The program does not separately publish raw HTML to its dataset, but input, reports and URL evidence can contain your submitted information. Control run/storage access and retention appropriately. Do not publish private example runs. Logs record counts and validation failures, not entire snapshots.

Use `REPORT.csv` for spreadsheet review. Native platform dataset exports preserve submitted identifiers; treat untrusted exported text as data rather than executing formulas.

### Pricing and run limits

PPE pricing: **$0.005 per delivered record** ($5 per 1,000 results) plus **$0.00005 per run start** at the supported 256–512 MB. One record is $0.00505 and 100 records are $0.50005 before discounts or taxes; check effective Console/Store pricing before running. The synthetic demonstration is not inherently free: its delivered rows are billable too. CSV/HTML/JSON reports have no additional author event fee. Platform usage is included in event pricing; it is not charged separately to the user.

Every delivered status can be billable, including inconclusive and invalid rows; the value is the completed check and its explanation, not a guaranteed error finding. Omitted rows do not produce item charges. No additional minimum selectable run budget is configured. Rejected structural input may still incur a startup charge or platform usage without item results. Effective Console/Store pricing is authoritative. Platform compute/storage usage is distinct from developer revenue.

Apify's synthetic `apify-default-dataset-item` event automatically charges each stored dataset row. There is no additional manual charge for that same item. The synthetic `apify-actor-start` event is charged automatically at the startup rate above.

The Actor checks the SDK's remaining dataset-item allowance before auditing, processes only the permitted prefix, and records `omittedDueToBudget` and `budgetLimited` in `OUTPUT`. With no remaining allowance it returns a zero-result summary without reports. Dataset items are saved before report files; aborted or failed runs can have partial output. Cloud billing, interruption behavior and actual costs must be verified in a private platform run before paid release. The included budget integration test uses the SDK's **local simulation**, not a payment.

### Release status

This MVP compares supported static evidence; it is not a measured commercial accuracy benchmark or a guarantee of Google approval. Independent development, with no official affiliation with Shopify or Google.

### Platform references

- Actor definition and relative file paths: https://docs.apify.com/platform/actors/development/actor-definition/actor-json
- Input schema: https://docs.apify.com/platform/actors/development/actor-definition/input-schema
- Output schema: https://docs.apify.com/platform/actors/development/actor-definition/output-schema
- Synthetic PPE events and spending limits: https://docs.apify.com/platform/actors/monetize/pay-per-event

# Actor input Schema

## `products` (type: `array`):

1–100 rows. Supply html and explicit target_country per row. Total serialized input max 8 MiB; each HTML max 1 MiB UTF-8. See INPUT.example.json.

## `notice` (type: `string`):

Optional note describing capture context; not a certification. Do not include secrets.

## Actor input object example

```json
{
  "products": [
    {
      "id": "reference",
      "sku": "DEMO-1",
      "link": "https://shop.example/products/demo?variant=100",
      "price": "19.90 USD",
      "availability": "in_stock",
      "variant_id": "100",
      "product_form_id": "main",
      "target_country": "US",
      "html": "<script>var Shopify = Shopify || {};\nShopify.country = \"US\";</script><script type=\"application/ld+json\">{\"@type\": \"Product\", \"sku\": \"DEMO-1\", \"offers\": {\"@type\": \"Offer\", \"sku\": \"DEMO-1\", \"price\": \"19.90\", \"priceCurrency\": \"USD\", \"availability\": \"https://schema.org/InStock\", \"url\": \"https://shop.example/products/demo?variant=100\"}}</script><form id=\"main\"><input type=\"hidden\" name=\"id\" value=\"100\"></form>"
    },
    {
      "id": "wrong-price",
      "sku": "DEMO-1",
      "link": "https://shop.example/products/demo?variant=100",
      "price": "29.90 USD",
      "availability": "in_stock",
      "variant_id": "100",
      "product_form_id": "main",
      "target_country": "US",
      "html": "<script>var Shopify = Shopify || {};\nShopify.country = \"US\";</script><script type=\"application/ld+json\">{\"@type\": \"Product\", \"sku\": \"DEMO-1\", \"offers\": {\"@type\": \"Offer\", \"sku\": \"DEMO-1\", \"price\": \"19.90\", \"priceCurrency\": \"USD\", \"availability\": \"https://schema.org/InStock\", \"url\": \"https://shop.example/products/demo?variant=100\"}}</script><form id=\"main\"><input type=\"hidden\" name=\"id\" value=\"100\"></form>"
    },
    {
      "id": "other-country",
      "sku": "DEMO-1",
      "link": "https://shop.example/products/demo?variant=100",
      "price": "19.90 USD",
      "availability": "in_stock",
      "variant_id": "100",
      "product_form_id": "main",
      "target_country": "CA",
      "html": "<script>var Shopify = Shopify || {};\nShopify.country = \"US\";</script><script type=\"application/ld+json\">{\"@type\": \"Product\", \"sku\": \"DEMO-1\", \"offers\": {\"@type\": \"Offer\", \"sku\": \"DEMO-1\", \"price\": \"19.90\", \"priceCurrency\": \"USD\", \"availability\": \"https://schema.org/InStock\", \"url\": \"https://shop.example/products/demo?variant=100\"}}</script><form id=\"main\"><input type=\"hidden\" name=\"id\" value=\"100\"></form>"
    }
  ]
}
```

# Actor output Schema

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

No description

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

No description

## `jsonReport` (type: `string`):

No description

## `csvReport` (type: `string`):

No description

## `htmlReport` (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 = {
    "products": [
        {
            "id": "reference",
            "sku": "DEMO-1",
            "link": "https://shop.example/products/demo?variant=100",
            "price": "19.90 USD",
            "availability": "in_stock",
            "variant_id": "100",
            "product_form_id": "main",
            "target_country": "US",
            "html": "<script>var Shopify = Shopify || {};\nShopify.country = \"US\";</script><script type=\"application/ld+json\">{\"@type\": \"Product\", \"sku\": \"DEMO-1\", \"offers\": {\"@type\": \"Offer\", \"sku\": \"DEMO-1\", \"price\": \"19.90\", \"priceCurrency\": \"USD\", \"availability\": \"https://schema.org/InStock\", \"url\": \"https://shop.example/products/demo?variant=100\"}}</script><form id=\"main\"><input type=\"hidden\" name=\"id\" value=\"100\"></form>"
        },
        {
            "id": "wrong-price",
            "sku": "DEMO-1",
            "link": "https://shop.example/products/demo?variant=100",
            "price": "29.90 USD",
            "availability": "in_stock",
            "variant_id": "100",
            "product_form_id": "main",
            "target_country": "US",
            "html": "<script>var Shopify = Shopify || {};\nShopify.country = \"US\";</script><script type=\"application/ld+json\">{\"@type\": \"Product\", \"sku\": \"DEMO-1\", \"offers\": {\"@type\": \"Offer\", \"sku\": \"DEMO-1\", \"price\": \"19.90\", \"priceCurrency\": \"USD\", \"availability\": \"https://schema.org/InStock\", \"url\": \"https://shop.example/products/demo?variant=100\"}}</script><form id=\"main\"><input type=\"hidden\" name=\"id\" value=\"100\"></form>"
        },
        {
            "id": "other-country",
            "sku": "DEMO-1",
            "link": "https://shop.example/products/demo?variant=100",
            "price": "19.90 USD",
            "availability": "in_stock",
            "variant_id": "100",
            "product_form_id": "main",
            "target_country": "CA",
            "html": "<script>var Shopify = Shopify || {};\nShopify.country = \"US\";</script><script type=\"application/ld+json\">{\"@type\": \"Product\", \"sku\": \"DEMO-1\", \"offers\": {\"@type\": \"Offer\", \"sku\": \"DEMO-1\", \"price\": \"19.90\", \"priceCurrency\": \"USD\", \"availability\": \"https://schema.org/InStock\", \"url\": \"https://shop.example/products/demo?variant=100\"}}</script><form id=\"main\"><input type=\"hidden\" name=\"id\" value=\"100\"></form>"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ysond/my-actor").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 = { "products": [
        {
            "id": "reference",
            "sku": "DEMO-1",
            "link": "https://shop.example/products/demo?variant=100",
            "price": "19.90 USD",
            "availability": "in_stock",
            "variant_id": "100",
            "product_form_id": "main",
            "target_country": "US",
            "html": """<script>var Shopify = Shopify || {};
Shopify.country = \"US\";</script><script type=\"application/ld+json\">{\"@type\": \"Product\", \"sku\": \"DEMO-1\", \"offers\": {\"@type\": \"Offer\", \"sku\": \"DEMO-1\", \"price\": \"19.90\", \"priceCurrency\": \"USD\", \"availability\": \"https://schema.org/InStock\", \"url\": \"https://shop.example/products/demo?variant=100\"}}</script><form id=\"main\"><input type=\"hidden\" name=\"id\" value=\"100\"></form>""",
        },
        {
            "id": "wrong-price",
            "sku": "DEMO-1",
            "link": "https://shop.example/products/demo?variant=100",
            "price": "29.90 USD",
            "availability": "in_stock",
            "variant_id": "100",
            "product_form_id": "main",
            "target_country": "US",
            "html": """<script>var Shopify = Shopify || {};
Shopify.country = \"US\";</script><script type=\"application/ld+json\">{\"@type\": \"Product\", \"sku\": \"DEMO-1\", \"offers\": {\"@type\": \"Offer\", \"sku\": \"DEMO-1\", \"price\": \"19.90\", \"priceCurrency\": \"USD\", \"availability\": \"https://schema.org/InStock\", \"url\": \"https://shop.example/products/demo?variant=100\"}}</script><form id=\"main\"><input type=\"hidden\" name=\"id\" value=\"100\"></form>""",
        },
        {
            "id": "other-country",
            "sku": "DEMO-1",
            "link": "https://shop.example/products/demo?variant=100",
            "price": "19.90 USD",
            "availability": "in_stock",
            "variant_id": "100",
            "product_form_id": "main",
            "target_country": "CA",
            "html": """<script>var Shopify = Shopify || {};
Shopify.country = \"US\";</script><script type=\"application/ld+json\">{\"@type\": \"Product\", \"sku\": \"DEMO-1\", \"offers\": {\"@type\": \"Offer\", \"sku\": \"DEMO-1\", \"price\": \"19.90\", \"priceCurrency\": \"USD\", \"availability\": \"https://schema.org/InStock\", \"url\": \"https://shop.example/products/demo?variant=100\"}}</script><form id=\"main\"><input type=\"hidden\" name=\"id\" value=\"100\"></form>""",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("ysond/my-actor").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 '{
  "products": [
    {
      "id": "reference",
      "sku": "DEMO-1",
      "link": "https://shop.example/products/demo?variant=100",
      "price": "19.90 USD",
      "availability": "in_stock",
      "variant_id": "100",
      "product_form_id": "main",
      "target_country": "US",
      "html": "<script>var Shopify = Shopify || {};\\nShopify.country = \\"US\\";</script><script type=\\"application/ld+json\\">{\\"@type\\": \\"Product\\", \\"sku\\": \\"DEMO-1\\", \\"offers\\": {\\"@type\\": \\"Offer\\", \\"sku\\": \\"DEMO-1\\", \\"price\\": \\"19.90\\", \\"priceCurrency\\": \\"USD\\", \\"availability\\": \\"https://schema.org/InStock\\", \\"url\\": \\"https://shop.example/products/demo?variant=100\\"}}</script><form id=\\"main\\"><input type=\\"hidden\\" name=\\"id\\" value=\\"100\\"></form>"
    },
    {
      "id": "wrong-price",
      "sku": "DEMO-1",
      "link": "https://shop.example/products/demo?variant=100",
      "price": "29.90 USD",
      "availability": "in_stock",
      "variant_id": "100",
      "product_form_id": "main",
      "target_country": "US",
      "html": "<script>var Shopify = Shopify || {};\\nShopify.country = \\"US\\";</script><script type=\\"application/ld+json\\">{\\"@type\\": \\"Product\\", \\"sku\\": \\"DEMO-1\\", \\"offers\\": {\\"@type\\": \\"Offer\\", \\"sku\\": \\"DEMO-1\\", \\"price\\": \\"19.90\\", \\"priceCurrency\\": \\"USD\\", \\"availability\\": \\"https://schema.org/InStock\\", \\"url\\": \\"https://shop.example/products/demo?variant=100\\"}}</script><form id=\\"main\\"><input type=\\"hidden\\" name=\\"id\\" value=\\"100\\"></form>"
    },
    {
      "id": "other-country",
      "sku": "DEMO-1",
      "link": "https://shop.example/products/demo?variant=100",
      "price": "19.90 USD",
      "availability": "in_stock",
      "variant_id": "100",
      "product_form_id": "main",
      "target_country": "CA",
      "html": "<script>var Shopify = Shopify || {};\\nShopify.country = \\"US\\";</script><script type=\\"application/ld+json\\">{\\"@type\\": \\"Product\\", \\"sku\\": \\"DEMO-1\\", \\"offers\\": {\\"@type\\": \\"Offer\\", \\"sku\\": \\"DEMO-1\\", \\"price\\": \\"19.90\\", \\"priceCurrency\\": \\"USD\\", \\"availability\\": \\"https://schema.org/InStock\\", \\"url\\": \\"https://shop.example/products/demo?variant=100\\"}}</script><form id=\\"main\\"><input type=\\"hidden\\" name=\\"id\\" value=\\"100\\"></form>"
    }
  ]
}' |
apify call ysond/my-actor --silent --output-dataset

```

## MCP server setup

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

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/mSvlGz0FxIdMyXqbg/builds/nidHb5qIpemKXdvCz/openapi.json
