# Supplier Pack Guard — CSV Pack Size & Unit Cost Audit (`brandgard/supplier-pack-guard`) Actor

Compare supplier CSV catalogs by SKU. Catch hidden price increases when packs shrink, normalize grams/kg and ml/l, and flag uncertain data. Up to 5,000 rows per catalog. HTML, CSV and JSON reports. $0.25 per completed audit; fixed demo free.

- **URL**: https://apify.com/brandgard/supplier-pack-guard.md
- **Developed by:** [Brandon Gardner](https://apify.com/brandgard) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.25 / completed catalog audit

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

## Supplier Pack Guard

Compare two supplier catalogs and find changes that a simple price comparison misses. A case shrinking from 12 items to 10 at the same price increases its per-item cost by 20%.

Designed for purchasing teams, small distributors and ecommerce catalog operators who already have permission to use their supplier files.

### Try it

Run with exactly `{"demo":true}` to process the free fixed synthetic sample. It includes a smaller glove pack, a soap price increase, equivalent grams/kilograms, an unknown unit, and unmatched SKUs. The fixed demonstration does not trigger any priced event. It cannot process custom data.

For your data, set `demo` to false, paste the before and after CSV text, and confirm column mappings. No website credentials or scraping are needed.

```csv
sku,price,currency,pack_count,item_quantity,unit
GLOVES-01,24,USD,12,1,each
COFFEE-01,60,USD,12,250,g
```

`price` is the price of the entire pack. `pack_count` is the number of items in that pack. `item_quantity` is the amount inside ONE item, measured in `unit`. Twelve 250 g bags at $60 cost $20/kg. Twelve individual gloves use pack_count=12, item_quantity=1, unit=each.

### Inputs and limits

- Two comma-separated UTF-8 CSV snapshots, each with a header and 1–5,000 rows; maximum 2 MB each.
- Required mapped columns: `sku`, `price`, `currency`, `pack_count`, `item_quantity`, `unit`.
- Optional `beforeColumns` and `afterColumns` objects map all six canonical fields to exact supplier headers. Default headers are shown above.
- SKUs are matched exactly after outer whitespace removal. Leading zeros and case are preserved. Duplicate or missing SKUs stop the run; fuzzy matching is not used.
- Supported units: `each`, `g`, `kg`, `oz` (mass), `lb`, `ml`, `l`. No fluid ounces, boxes, bundles, density assumptions or inferred pack sizes.
- Explicit uppercase three-letter currency code; currencies are compared literally, never converted or validated against a currency registry.
- Plain nonnegative decimal prices; positive quantities. Up to 12 whole digits and 6 decimal places. No currency symbols, grouping commas, exponents or decimal commas. Item counts must be whole numbers.
- `thresholdPercent` is a string, defaults to `"5"`, and must be between 0 and 10000. All changes appear; this threshold adds a notable-increase flag.

For renamed columns, supply every mapping, for example:

```json
{"sku":"Product ID","price":"Case Price","currency":"Currency","pack_count":"Case Count","item_quantity":"Item Size","unit":"Unit"}
```

### Results

Open the run's Output tab for the readable HTML report, CSV and full JSON. If a browser blocks the embedded JSON or CSV view, open Storage → Key-value store to download the record. Reports are saved as `REPORT`, `REPORT-CSV`, `REPORT-HTML`; `OUTPUT` contains the summary. The default dataset contains an audit summary, not individual SKU findings. A resurrected run can add another summary row; it does not charge a second audit event for that run.

Each SKU has a status: `unchanged`, `changed`, `needs_review`, `incomparable`, `only_in_before`, or `only_in_after`. JSON contains original mapped values, source record numbers and normalized unit costs. Record 1 is the first data record after the header, including CSV records with embedded newlines.

Invalid prices, quantities or units produce review findings rather than guessed calculations. Structurally malformed CSV, ambiguous SKUs and oversized inputs fail the run. Matching currencies and dimensions are required for a comparison. A zero original cost has no percentage denominator and receives a specific flag if the new cost is positive.

CSV exports prefix potentially executable spreadsheet cells with an apostrophe. JSON preserves the original mapped values. HTML escapes supplied text and contains no scripts or external resources.

### Pricing and privacy

**$0.25 per completed catalog audit**, including platform usage, for two catalogs of up to 5,000 rows each within the size limits. The live Pricing tab is authoritative. One `audit-completed` event is charged per valid bounded comparison, including a report with no changes or with review findings. Structural input failures do not trigger that event. Reports are stored before the event is charged. There is no separate startup or dataset-item fee. The fixed synthetic demonstration is free.

For custom data, set the run's maximum cost to at least $0.25. Each new run is a separate comparison and purchase; supplying the same files again does not create a new baseline or a subscription. Your automation must supply both snapshots each time. Use a saved task or the Apify API to repeat the comparison when a supplier sends a new catalog. Review flags before changing store prices.

Processing runs in your Apify run. No catalog data is sent to an AI provider or external website. Inputs and reports remain in Apify storage according to its retention and access settings; review those settings before supplying confidential pricing. The Actor does not automatically delete run data. Only upload data you are authorized to process. It does not edit your catalog or storefront.

### Limits of the result

A missing SKU may mean an incomplete feed, not a discontinued product. Identical SKUs cannot prove product identity, quality or supplier accuracy. Taxes, discounts, freight and exchange rates are not reconciled. Costs and percentages display six decimals; flags use higher precision. Review findings before changing prices or placing orders.

For support, open an Actor issue with a small anonymized example and the run ID. Do not post confidential supplier data publicly.

# Actor input Schema

## `demo` (type: `boolean`):

Free fixed sample. Disable before supplying your own catalogs. Do not provide other fields when enabled.

## `beforeCsv` (type: `string`):

Comma-separated CSV including header: sku,price,currency,pack_count,item_quantity,unit. Omit only for fixed demonstration.

## `afterCsv` (type: `string`):

New snapshot using the same SKU identities. Missing products are not assumed discontinued.

## `beforeColumns` (type: `object`):

Map all six canonical field names to exact CSV headers. Example: {"sku":"Item ID","price":"Case Price","currency":"Currency","pack_count":"Case Count","item_quantity":"Item Size","unit":"Unit"}. Omit for standard headers.

## `afterColumns` (type: `object`):

Same format as Before mapping. Omit for standard headers.

## `thresholdPercent` (type: `string`):

Defaults to 5 when omitted. Use a nonnegative plain decimal, at most 10000. Omit for demonstration.

## Actor input object example

```json
{
  "demo": true
}
```

# Actor output Schema

## `html` (type: `string`):

No description

## `csv` (type: `string`):

No description

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

// Run the Actor and wait for it to finish
const run = await client.actor("brandgard/supplier-pack-guard").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 = { "demo": True }

# Run the Actor and wait for it to finish
run = client.actor("brandgard/supplier-pack-guard").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 '{
  "demo": true
}' |
apify call brandgard/supplier-pack-guard --silent --output-dataset

```

## MCP server setup

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

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/bXRyIR0P8gnHGVR9E/builds/g6NYzaXZvoCoRuuhz/openapi.json
