# Shopify Variant Feed Auditor — Price, Stock & Screenshots (`outis/shopify-variant-feed-auditor`) Actor

Audit Shopify product variants against your Google Shopping feed. Compare price and stock with Shopify data, JSON-LD and the rendered page. Get evidence, screenshots and clear mismatch or inconclusive results.

- **URL**: https://apify.com/outis/shopify-variant-feed-auditor.md
- **Developed by:** [Aristide Kamara](https://apify.com/outis) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$20.00 / 1,000 variant audits

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 Variant Feed Auditor

Check whether a Shopify landing page actually shows the variant, price and availability in your Google Shopping product feed. Get a structured diagnosis, an HTML evidence report, a spreadsheet-safe CSV and optional screenshots.

Built for Shopping specialists and agencies checking products before promotions, after theme changes, or during recurring catalogue reviews.

**Independent diagnostic tool. Not affiliated with Shopify or Google. A passing audit is not a guarantee of Merchant Center approval.**

### Try it

The default example is a clearly labelled synthetic demonstration. It requests no websites and charges no audit events. Normal platform usage may still apply.

```json
{ "demoMode": true }
```

For a live audit, turn off Demo mode and provide products from a store you are authorized to inspect:

```json
{
  "demoMode": false,
  "products": [
    {
      "id": "blue-bag-42",
      "link": "https://your-store.example/products/bag?variant=123456789",
      "variantId": "123456789",
      "sku": "BAG-BLUE",
      "price": "49.90 USD",
      "availability": "in_stock"
    }
  ],
  "maxProducts": 25,
  "renderPages": true,
  "captureScreenshots": true,
  "locale": "en-US"
}
```

Replace the fictional URL, variant and SKU with your own data. For a product with multiple variants, supply a numeric `variantId`, a `?variant=` URL, or a unique SKU. Contradictory identities are reported rather than guessed. The browser checks the original landing URL; the Actor never silently fixes a URL that preselects the wrong variant.

Alternatively, provide `feedUrl` pointing to a public **CSV, TSV or JSON array**. Each row uses the fields above. Feed URLs and inline products are mutually exclusive. Limit: 500 rows and 4 MB; split larger catalogues into batches. Extra feed columns are ignored. XML feeds and authenticated feeds are not supported.

### What is checked

1. **Feed values:** explicit ISO currency and decimal price, availability, variant identity and optional sale interval.
2. **Shopify variant:** exact ID/SKU in the public Ajax product data, price where currency can be verified, and purchasability.
3. **Structured data:** a Product/Offer matched to the same variant by ID or exact SKU. Ambiguous or conflicting offers remain unverified.
4. **Rendered page:** selected variant, one visible current price, and a recognized purchase-control availability state.

Rendered extraction targets **Shopify Dawn-compatible product layouts**. Use `priceSelector` for a different price element inside the main product section. A custom selector does not add support for an otherwise unsupported variant selector or purchase control. English add-to-cart/sold-out labels are recognized initially; other labels can make the rendered stock check inconclusive.

Availability uses `in_stock`, `out_of_stock`, `preorder` or `backorder`. Shopify's purchasable flag alone does not prove a preorder/backorder state.

For promotions, provide `sale_price` and optionally `sale_price_effective_date`, for example `2026-11-20T00:00:00Z/2026-11-30T00:00:00Z`. The sale must be lower than the regular price and use the same currency. Start is inclusive, end exclusive. Without an interval, a supplied sale price is active.

### Understand your results

| Status | Meaning |
|---|---|
| `pass` | All requested comparisons completed without a discrepancy or unresolved warning. |
| `fail` | At least one definite discrepancy or invalid input was found. Inspect individual checks for coverage. |
| `inconclusive` | A block, ambiguous extraction, unsupported layout, missing evidence or resource limit prevented a complete conclusion. |

Each row contains `productId`, `resultId`, `url`, `checkedAt`, `expected`, `observations`, `findings`, `checks`, `billable`, `isDemo`, `requestCount` and `durationMs`. Screenshots, when saved, are referenced by `screenshotKey` in this run's key-value store. `billable` describes eligibility for an audit charge, not proof of payment.

Findings have stable codes such as `RENDERED_VARIANT_MISMATCH`, `SHOPIFY_PRICE_MISMATCH`, `JSONLD_PRICE_MISMATCH`, `PRODUCT_PAGE_MISSING` and `JSONLD_UNVERIFIED`, with expected and observed values when available.

Open the output links:

- **REPORT:** readable HTML with expected and observed values.
- **EXPORT:** CSV with spreadsheet formulas neutralized.
- **SUMMARY:** status counts and audit event count.
- **SNAPSHOT:** compact state for the next run.
- **CHANGES:** produced when `previousResults` is supplied. Inconclusive rows are excluded from resolution claims.

For recurring monitoring, schedule the Actor and pass the previous SNAPSHOT in `previousResults`. Keep separate snapshots for separate stores. Connect the dataset to your own n8n/Make workflow or use Apify's integrations to deliver reports. The Actor itself does not send messages or modify products.

### Pricing and cost controls

The launch price is **$0.02 per completed variant audit ($20 per 1,000)**, with platform usage included for customers. The live Pricing tab is authoritative. The paid event is **`variant-audited`**, once for each completed, billable product audit. A successful check with no issues also counts. Definite missing product pages are useful completed diagnostics. Invalid input, inconclusive results and synthetic demonstrations do not trigger an audit event. There is no Actor-start fee or automatic dataset-row fee.

Set **Maximum total charge** in Apify and start with a small `maxProducts`. Default concurrency is one, maximum two. Each product has a bounded request count, response size and time budget. Duplicate identical product IDs are checked once; conflicting duplicates reject the input.

Application-level charge attempts are persisted before calling the official SDK. An interrupted, uncertain charge is not attempted again on resume. Storage delivery and charging are separate operations; `resultId` is stable within a run and can be used to deduplicate deliveries after network interruptions. Unchanged products are still audits and may be charged on subsequent runs.

### Coverage and limits

- Supports public Shopify product pages with `/products/handle` URLs and accessible Ajax data. Other commerce platforms are outside scope.
- Uses the **default anonymous storefront market**. Locale controls parsing/browser language; it does not select the network country. Explicit currency/country/market query overrides are currently inconclusive.
- No customer login, member prices, discount-code application, checkout, shipping, tax calculation, subscription-price validation or Merchant Center account access.
- Product-page crawl restrictions are respected. Public HTTP(S) only, ports 80/443. Private-network targets and unsafe redirects are rejected; DNS is pinned on each connection.
- Chromium requests are routed through the bounded public transport. Images, fonts, media, WebSockets and non-GET requests are blocked. Evidence screenshots show the audited text/layout and can omit imagery. Consent and custom scripts can limit coverage.
- Missing data is never treated as proof of a match. A `fail` can coexist with incomplete secondary checks; inspect `checks` and `findings`.
- Screenshots and reports are stored in the user's run storage. Share only what you intend to disclose.

### Support

Open an issue on this Actor with a redacted example input, run ID and finding code. Do not post API tokens, private feeds or customer credentials. Prefer a shareable public product URL and expected behavior.

### Development

Node.js 24 recommended. `npm ci`, `npm run check`, `npm run build`, then `npm start`. For rendered checks install Chromium using `npx playwright install chromium`. Apify input is read from the standard `storage/key_value_stores/default/INPUT.json` locally. Run `npm run test:live` for a bounded check against the public Shopify Dawn demonstration store.

# Changelog

This Actor's version history is a separate document: https://apify.com/outis/shopify-variant-feed-auditor/changelog.md

# Actor input Schema

## `demoMode` (type: `boolean`):

Shows two clearly labelled examples without requesting any website or charging audit events. Turn off and supply products or feedUrl for a live audit.

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

Google-style rows: id, link, price (49.90 USD), availability. Supply variantId, ?variant= in the URL or a unique sku. Optional sale_price and sale_price_effective_date. Maximum 500 rows.

## `feedUrl` (type: `string`):

Alternative to products. Up to 500 rows and 4 MB. No credentials, private URLs or XML feeds.

## `maxProducts` (type: `integer`):

Maximum unique products processed in this run. Larger feeds must be split into batches.

## `renderPages` (type: `boolean`):

Compare the selected variant and visible price in Chromium. Recommended. Default selectors target Shopify Dawn-compatible themes; unsupported layouts return inconclusive.

## `captureScreenshots` (type: `boolean`):

Save viewport screenshots in this run's key-value store. Images, fonts and tracking POST requests are blocked to reduce network use; screenshots show the audited text and layout.

## `locale` (type: `string`):

Locale for numeric parsing and the browser, for example en-US or fr-FR. This does not change the network's country or the store's market.

## `priceSelector` (type: `string`):

CSS selector within the main product section. Must select one visible current price, not a regular-and-sale price container. Leave empty for supported Dawn selectors.

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

One is recommended for smaller stores and predictable usage.

## `previousResults` (type: `array`):

Paste a prior run's SNAPSHOT JSON to produce CHANGES. Inconclusive observations are not reported as resolved issues.

## Actor input object example

```json
{
  "demoMode": true,
  "maxProducts": 25,
  "renderPages": true,
  "captureScreenshots": true,
  "locale": "en-US",
  "maxConcurrency": 1
}
```

# Actor output Schema

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

No description

## `report` (type: `string`):

No description

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

No description

## `csv` (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 = {
    "demoMode": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("outis/shopify-variant-feed-auditor").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 = { "demoMode": True }

# Run the Actor and wait for it to finish
run = client.actor("outis/shopify-variant-feed-auditor").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 '{
  "demoMode": true
}' |
apify call outis/shopify-variant-feed-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,outis/shopify-variant-feed-auditor"
        }
    }
}
```

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/iRFskfOGnXnkLBWUE/builds/chrXkL1mHvY5oY77E/openapi.json
