# Phoenix Catalog Proof (`phoenix-tools/phoenix-catalog-proof`) Actor

Compare an approved product catalog with its current version. Detect product, price, stock and image changes, optionally verify URLs, and export evidence-ready HTML, CSV and JSON reports before you publish.

- **URL**: https://apify.com/phoenix-tools/phoenix-catalog-proof.md
- **Developed by:** [Phoenix Tools](https://apify.com/phoenix-tools) (community)
- **Categories:** E-commerce, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 catalog comparisons

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

## Phoenix Catalog Proof

Compare two ecommerce product catalogs, detect risky changes, verify important links, and download an evidence-ready report. Phoenix Catalog Proof accepts CSV, JSON, XML, inline product arrays, public feed URLs, and existing Apify datasets.

Use it before a store migration, feed upload, marketplace sync, campaign launch, or client handover. No browser, store login, or external API key is required.

### What Phoenix Catalog Proof checks

- Products added to or removed from the current catalog
- Price and currency changes
- Stock and availability changes
- Product title changes
- Duplicate SKU, GTIN, ID, or product URL values
- Products without a usable matching identity
- Missing image and product URLs
- Broken image URLs and unexpected non-image responses
- Broken product URLs and redirects
- Canonical URL mismatches, when canonical inspection is enabled

The Actor keeps the complete finding list in the Apify dataset and creates three readable evidence files:

- `REPORT.html`, a branded report for review or client delivery
- `REPORT.csv`, a spreadsheet-ready finding list
- `REPORT.json`, a portable summary and finding bundle

### Common use cases

#### Ecommerce migration quality assurance

Compare the approved export with the live export after a Shopify, WooCommerce, marketplace, PIM, or ERP migration. Find dropped products, changed prices, missing images, and broken links before customers do.

#### Product feed approval

Create a repeatable proof before sending a feed to Google Merchant Center, Meta, a marketplace, or an affiliate partner. The output records both old and current values.

#### Agency and client reporting

Run the same comparison during onboarding, release approval, or scheduled maintenance. Share the HTML report with non-technical stakeholders and keep the dataset for automation.

#### Catalog monitoring

Save an Actor task and schedule it. Feed it two snapshots from your existing export or data pipeline and send the output to Make, Zapier, a webhook, or another Actor.

### Quick start

1. Open the Actor's **Input** tab.
2. Set the baseline source to the older or approved catalog.
3. Set the current source to the new or live catalog.
4. Keep the default matching order, `sku`, `gtin`, `id`, and `productUrl`, unless your catalog needs another order.
5. Disable URL checks for a fast data-only comparison, or keep them enabled to audit live links.
6. Click **Start**.
7. Open **Output** to view the dataset and download the HTML, CSV, or JSON report.

The prefilled sample is safe to run and demonstrates price, stock, addition, removal, and missing-image findings.

### Supported catalog sources

Each side of the comparison supports three source types.

| Source | When to use it |
| --- | --- |
| Public feed URL | The catalog is available through an HTTP or HTTPS CSV, JSON, or XML URL |
| Inline data | You want to paste CSV, JSON, XML, or a JSON product array directly into the input |
| Apify dataset | An earlier Actor or workflow already stored the catalog in an Apify dataset |

For nested JSON or XML, set **Product array path** to a dot path such as `catalog.products`. If it is empty, the Actor searches common containers such as `products`, `items`, `entries`, `offers`, and `data`.

### Automatic field recognition

Common names are detected automatically, including:

| Meaning | Common aliases |
| --- | --- |
| ID | `id`, `product_id`, `item_id`, `offer_id` |
| SKU | `sku`, `variant_sku`, `merchant_sku`, `mpn` |
| GTIN | `gtin`, `ean`, `barcode`, `upc`, `isbn` |
| Title | `title`, `name`, `product_name` |
| Price | `price`, `sale_price`, `regular_price`, `amount` |
| Currency | `currency`, `currency_code`, `price_currency` |
| Stock | `stock`, `availability`, `inventory_quantity`, `quantity` |
| Image | `image`, `image_url`, `image_link`, `thumbnail` |
| Product URL | `product_url`, `url`, `link`, `product_link` |

Use **Common field mapping** for different names or nested values. For example:

```json
{
  "sku": "variant.code",
  "price": "pricing.current.amount",
  "currency": "pricing.current.currency",
  "image": "media.primary.url"
}
```

If the two feeds use different structures, add only the differences under **Baseline-only field overrides** and **Current-only field overrides**.

### Input example

```json
{
  "baseline": {
    "sourceType": "url",
    "label": "Approved export",
    "url": "https://example.com/catalog-approved.csv",
    "format": "auto"
  },
  "current": {
    "sourceType": "url",
    "label": "Live export",
    "url": "https://example.com/catalog-live.csv",
    "format": "auto"
  },
  "matchingKeys": ["sku", "gtin", "id", "productUrl"],
  "settings": {
    "checkImageUrls": true,
    "checkProductUrls": true,
    "inspectCanonicals": false,
    "maxUrlChecks": 500,
    "priceTolerance": 0.01
  }
}
```

### Dataset output

Every dataset row is one finding with stable fields:

```json
{
  "findingId": "22f7f9efc6b6cb53",
  "severity": "medium",
  "category": "price_changed",
  "message": "Price changed from 19.99 to 21.99.",
  "matchField": "sku",
  "matchValue": "PHX-100",
  "baseline": {
    "sku": "PHX-100",
    "title": "Black mug",
    "price": "19.99",
    "currency": "EUR"
  },
  "current": {
    "sku": "PHX-100",
    "title": "Black mug",
    "price": "21.99",
    "currency": "EUR"
  }
}
```

Severity values are `critical`, `high`, `medium`, `low`, and `info`. Category values are stable for API and automation use.

### Limits and performance

- Default maximum: 5,000 products per catalog
- Hard maximum: 50,000 products per catalog
- Default feed download limit: 25 MB per source
- Default URL audit limit: 500 combined product and image URLs
- Default URL concurrency: 10
- HTML, CSV, and JSON evidence files include up to 5,000 findings to remain easy to download
- The Apify dataset always contains the complete finding list

For a quick test, disable URL checks. For a full audit, enable them and raise `maxUrlChecks` only as needed.

### Security and privacy

Phoenix Catalog Proof is read-only. It does not submit forms, modify stores, or request ecommerce credentials.

- Only HTTP and HTTPS catalog URLs are accepted
- Embedded URL credentials are rejected
- Localhost, private network, link-local, and common metadata destinations are blocked
- Redirect destinations are validated before they are followed
- Feed size, product count, timeout, redirect, and concurrency limits are enforced
- HTML output is escaped and CSV formula injection is neutralized
- Catalog data remains in the storage attached to the user's Apify run

Use only catalog data and URLs you own or are authorized to process. Do not place customer records, secrets, API tokens, or personal data in product feeds.

### Pricing

The Actor records one `catalog-block-processed` event for every started block of 1,000 products in the larger catalog. The exact price is shown in the Actor's Apify Pricing tab before a run starts. Normal Apify spending limits remain available to the user.

### FAQ

#### Does it scrape a store automatically?

No. It compares structured catalog sources you provide. This makes it faster, cheaper, and more predictable than browser scraping.

#### Can it compare CSV with JSON or XML?

Yes. Baseline and current sources can use different supported formats and different field mappings.

#### Does it require identical column names?

No. Common aliases are recognized automatically. Custom mappings and side-specific overrides handle different structures.

#### Will URL checks change the website?

No. URL checks use read-only HTTP requests. The Actor does not log in or submit data.

#### Can I automate recurring checks?

Yes. Save the input as an Apify task, schedule the task, or run the Actor through the API, Make, Zapier, webhooks, or MCP.

#### What should I send when requesting support?

Open the Actor's **Issues** tab and include the run URL, feed format, relevant mapping, and the category that looks wrong. Remove secrets and personal data first.

### Support

For defects or feed compatibility questions, use the Actor's **Issues** tab. Phoenix Tools maintains the Actor and reviews reproducible reports.

# Actor input Schema

## `baseline` (type: `object`):

The older or approved catalog used as the reference point.

## `current` (type: `object`):

The new or live catalog to audit.

## `matchingKeys` (type: `array`):

Fields are tried in this order. The first non-empty value identifies a product.

## `fieldMapping` (type: `object`):

Optional dot paths used for both catalogs. Examples: variant.sku, pricing.amount, media.imageUrl.

## `baselineFieldMapping` (type: `object`):

Optional mappings used only for the baseline catalog.

## `currentFieldMapping` (type: `object`):

Optional mappings used only for the current catalog.

## `settings` (type: `object`):

Control limits, URL checks, and report behavior.

## Actor input object example

```json
{
  "baseline": {
    "sourceType": "inline",
    "label": "Approved catalog",
    "format": "auto",
    "items": [
      {
        "sku": "PHX-100",
        "title": "Example black mug",
        "price": "19.99",
        "currency": "EUR",
        "stock": "in stock",
        "image_url": "https://example.com/images/mug.jpg",
        "product_url": "https://example.com/products/mug"
      },
      {
        "sku": "PHX-200",
        "title": "Example desk mat",
        "price": "29.99",
        "currency": "EUR",
        "stock": "in stock",
        "image_url": "https://example.com/images/mat.jpg",
        "product_url": "https://example.com/products/mat"
      }
    ]
  },
  "current": {
    "sourceType": "inline",
    "label": "Current catalog",
    "format": "auto",
    "items": [
      {
        "sku": "PHX-100",
        "title": "Example black mug",
        "price": "21.99",
        "currency": "EUR",
        "stock": "out of stock",
        "image_url": "",
        "product_url": "https://example.com/products/mug"
      },
      {
        "sku": "PHX-300",
        "title": "Example laptop stand",
        "price": "39.99",
        "currency": "EUR",
        "stock": "in stock",
        "image_url": "https://example.com/images/stand.jpg",
        "product_url": "https://example.com/products/stand"
      }
    ]
  },
  "matchingKeys": [
    "sku",
    "gtin",
    "id",
    "productUrl"
  ],
  "fieldMapping": {},
  "baselineFieldMapping": {},
  "currentFieldMapping": {},
  "settings": {}
}
```

# Actor output Schema

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

No description

## `htmlReport` (type: `string`):

No description

## `findings` (type: `string`):

No description

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

No description

## `jsonReport` (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 = {
    "baseline": {
        "sourceType": "inline",
        "label": "Approved catalog",
        "format": "auto",
        "items": [
            {
                "sku": "PHX-100",
                "title": "Example black mug",
                "price": "19.99",
                "currency": "EUR",
                "stock": "in stock",
                "image_url": "https://example.com/images/mug.jpg",
                "product_url": "https://example.com/products/mug"
            },
            {
                "sku": "PHX-200",
                "title": "Example desk mat",
                "price": "29.99",
                "currency": "EUR",
                "stock": "in stock",
                "image_url": "https://example.com/images/mat.jpg",
                "product_url": "https://example.com/products/mat"
            }
        ]
    },
    "current": {
        "sourceType": "inline",
        "label": "Current catalog",
        "format": "auto",
        "items": [
            {
                "sku": "PHX-100",
                "title": "Example black mug",
                "price": "21.99",
                "currency": "EUR",
                "stock": "out of stock",
                "image_url": "",
                "product_url": "https://example.com/products/mug"
            },
            {
                "sku": "PHX-300",
                "title": "Example laptop stand",
                "price": "39.99",
                "currency": "EUR",
                "stock": "in stock",
                "image_url": "https://example.com/images/stand.jpg",
                "product_url": "https://example.com/products/stand"
            }
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("phoenix-tools/phoenix-catalog-proof").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 = {
    "baseline": {
        "sourceType": "inline",
        "label": "Approved catalog",
        "format": "auto",
        "items": [
            {
                "sku": "PHX-100",
                "title": "Example black mug",
                "price": "19.99",
                "currency": "EUR",
                "stock": "in stock",
                "image_url": "https://example.com/images/mug.jpg",
                "product_url": "https://example.com/products/mug",
            },
            {
                "sku": "PHX-200",
                "title": "Example desk mat",
                "price": "29.99",
                "currency": "EUR",
                "stock": "in stock",
                "image_url": "https://example.com/images/mat.jpg",
                "product_url": "https://example.com/products/mat",
            },
        ],
    },
    "current": {
        "sourceType": "inline",
        "label": "Current catalog",
        "format": "auto",
        "items": [
            {
                "sku": "PHX-100",
                "title": "Example black mug",
                "price": "21.99",
                "currency": "EUR",
                "stock": "out of stock",
                "image_url": "",
                "product_url": "https://example.com/products/mug",
            },
            {
                "sku": "PHX-300",
                "title": "Example laptop stand",
                "price": "39.99",
                "currency": "EUR",
                "stock": "in stock",
                "image_url": "https://example.com/images/stand.jpg",
                "product_url": "https://example.com/products/stand",
            },
        ],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("phoenix-tools/phoenix-catalog-proof").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 '{
  "baseline": {
    "sourceType": "inline",
    "label": "Approved catalog",
    "format": "auto",
    "items": [
      {
        "sku": "PHX-100",
        "title": "Example black mug",
        "price": "19.99",
        "currency": "EUR",
        "stock": "in stock",
        "image_url": "https://example.com/images/mug.jpg",
        "product_url": "https://example.com/products/mug"
      },
      {
        "sku": "PHX-200",
        "title": "Example desk mat",
        "price": "29.99",
        "currency": "EUR",
        "stock": "in stock",
        "image_url": "https://example.com/images/mat.jpg",
        "product_url": "https://example.com/products/mat"
      }
    ]
  },
  "current": {
    "sourceType": "inline",
    "label": "Current catalog",
    "format": "auto",
    "items": [
      {
        "sku": "PHX-100",
        "title": "Example black mug",
        "price": "21.99",
        "currency": "EUR",
        "stock": "out of stock",
        "image_url": "",
        "product_url": "https://example.com/products/mug"
      },
      {
        "sku": "PHX-300",
        "title": "Example laptop stand",
        "price": "39.99",
        "currency": "EUR",
        "stock": "in stock",
        "image_url": "https://example.com/images/stand.jpg",
        "product_url": "https://example.com/products/stand"
      }
    ]
  }
}' |
apify call phoenix-tools/phoenix-catalog-proof --silent --output-dataset

```

## MCP server setup

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

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/QFlteejcZIMlcU3YQ/builds/kZBr3TFmQnYngJgqS/openapi.json
