Product Catalog Reconciliation — SKU, Margin & Stock QA
Pricing
from $19.00 / catalog reconciliation report
Product Catalog Reconciliation — SKU, Margin & Stock QA
Compare supplier and store CSV/JSON exports: duplicate SKUs, exact or explicit matches, optional GTIN matching, cost-versus-retail margin and stock differences. Up to 5,000 rows per catalog. Printable HTML, JSON and a CSV review queue. Fictional demo. No store writes.
Pricing
from $19.00 / catalog reconciliation report
Rating
0.0
(0)
Developer
El Fajad
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Product Catalog Reconciliation
Compare a supplier export with a store export and get a prioritized review queue: duplicate SKU identities, matched product rows, supplier-only/store-only records, unit-cost versus retail arithmetic, reference-price differences and quantity conflicts. Process up to 5,000 rows per catalog with printable HTML, full JSON and CSV.
Built for merchants and agencies preparing a catalog review. Supply CSV text or JSON rows; there are no store writes, automatic price changes, upstream paid Actors or live website requests. No AI key or VPS is needed.
Try the fictional demo
{ "mode": "demo" }
Fixed synthetic catalogs illustrate leading-zero SKUs, duplicate identities, a below-cost listing, a zero-supplier-stock review, a unique GTIN fallback and an unmatched case difference. Custom catalog inputs are ignored in demo mode. No $19 report event is charged; the $0.00005 startup event still applies.
Open Catalog brief → Printable HTML. Print or save as PDF, or download the CSV review queue and full JSON.
Reconcile your exports
{"mode": "reconcile","reportLabel": "Supplier / store catalog review","minimumGrossMarginPercent": 30,"matchByBarcode": false,"supplierCatalog": {"defaultCurrency": "USD","rows": [{ "sku": "001", "title": "Ceramic mug", "cost": "8.00", "price": "14.00", "quantity": "0" }]},"storeCatalog": {"defaultCurrency": "USD","rows": [{ "sku": "001", "title": "Ceramic coffee mug", "price": "7.00", "quantity": "10" }]}}
- Export the supplier and store catalog for the same scope, unit basis and currency. Inventory exports with multiple locations should be filtered to one comparable location first; duplicate SKUs are not automatically summed.
- Provide
rowsorcsvTexton each side. Map nonstandard headers withcolumns. - Keep identifiers and money as strings. Set currencies explicitly, per row or using
defaultCurrency. Neither defaults to USD. - Set maximum charge to at least $19.01. Your account's remaining budget and spending limits must allow it. The small saved default is for the demo.
- Review findings and their row IDs before making changes. Download results before your Apify retention period expires.
An entirely disjoint pair of valid catalogs still produces a billable report. Reports with no findings or only duplicate-identity issues are also billable when each side has at least one usable SKU. This Actor does not verify the exports are complete, recent or correctly assigned to products.
CSV input and field mapping
{"mode": "reconcile","supplierCatalog": {"csvText": "Item Code,Unit Cost,Recommended Retail,Units\n001,8.00,14.00,0\n","defaultCurrency": "USD","columns": { "sku": "Item Code", "cost": "Unit Cost", "price": "Recommended Retail", "quantity": "Units" }},"storeCatalog": {"csvText": "Variant SKU,Title,Variant Price,Variant Inventory Qty\n001,Ceramic mug,7.00,10\n","defaultCurrency": "USD","columns": { "sku": "Variant SKU", "title": "Title", "price": "Variant Price", "quantity": "Variant Inventory Qty" }}}
Map your actual headers; Shopify exports and product/inventory CSV formats vary. This is not a Shopify CSV validator or an import-ready patch generator. You can paste UTF-8 text from your exported file into csvText through the JSON input editor; this MVP does not download file URLs or support XLSX directly.
Canonical fields: sku, title, barcode, cost, price, quantity, currency. Omitted optional fields remain unknown. Explicitly mapped absent columns fail clearly rather than silently using another field. Set a mapping to null to ignore that field. Unmapped extra columns are not used in analysis.
Each catalog can use delimiter: "comma" (default), "semicolon" or "tab". CSV quoting, embedded newlines, BOM and CRLF are supported. Duplicate/empty headers, malformed quoting or inconsistent row widths are rejected; no malformed rows are silently skipped. sourceRow means the one-based data record number, excluding the header, not the physical CSV line number.
Identity matching
| Match | Behavior |
|---|---|
| Exact SKU | Default. Case, whitespace, punctuation and leading zeros remain significant. |
Explicit skuMap | One-to-one { "supplierSku": "A", "storeSku": "B" } entries, applied before exact matches. Conflicting/repeated mappings fail. Unresolved mapped rows are reserved and not matched by fallback. |
| Optional GTIN fallback | Enabled by matchByBarcode: true. Unique valid GTIN-8/12/13/14 values are compared in zero-padded 14-digit form. Does not bypass duplicate SKUs or existing exact-SKU conflicts. |
| Case/space suggestions | Unique unmatched SKUs differing only by case, surrounding spaces or Unicode normalization are flagged for manual review. They are never automatic matches. |
| Title similarity | No fuzzy title or AI identity matching. |
Duplicate SKUs on either side exclude those rows from automatic matching, even if duplicate fields are identical. Quantities are not summed. A valid GTIN check digit does not prove GS1 allocation, ownership, product identity or that the item represents the same pack/unit. Exact SKU matches with different valid GTINs get a high-priority review.
Missing/numeric/control-character SKUs are unusable. JSON identifiers must be strings: "001", not 1. Export completeness is supplied context, not independently verified. Supplier-only/store-only findings describe the supplied exports, not discontinued or newly launched products.
Money and stock checks
cost= supplied supplier unit cost.supplierCatalog.price= reference price, which may be recommended retail or another price you chose.storeCatalog.price= store retail price. A reference-price difference is a review item, not proof of an incorrect listing.- Money must be a nonnegative decimal string, e.g.
"12.50", at most four decimal places and at most999999999.9999. Currency symbols, thousands separators, decimal commas, scientific notation, surrounding spaces and numeric JSON money are invalid; they are not guessed or converted. - Currencies must be explicit uppercase three-letter codes on each row or via that catalog's default. Codes and their real-world assignment are not independently verified. Different or missing currencies prevent price/margin arithmetic. No exchange-rate conversion occurs.
- Gross margin =
(retail price − supplied unit cost) / retail price × 100, before tax, shipping, fees, discounts or other costs. Displayed percentages are truncated to two decimal places. The threshold defaults to 30% and is a configurable review rule, not a recommended price. - A zero retail price makes margin undefined. Retail below supplied unit cost is high priority. Missing/invalid cost or price remains unknown.
- Quantities are nonnegative whole units, up to 999,999,999. Different stock figures are review items; zero supplier units and positive store units get high priority. Your own inventory, reservations, warehouse scope or capture times can explain differences. No automatic sync or inferred lost-revenue estimate.
Pricing
Launch price: $19 per delivered non-demo catalog report, for up to 5,000 rows on each side. The Pricing tab is authoritative.
catalog-report: one event per usable report, including no-match/no-finding reports and duplicate-identity reviews.- Fictional demo: no report event.
- Invalid input, parsing failure, insufficient report budget or a catalog with zero usable string SKUs: no report event. Unusable collections save data-quality details before failing.
- Startup event: $0.00005 at supported memory sizes, including demos and failures.
- No separate dataset-row charge or customer platform-usage pass-through.
The report budget is checked before parsing real catalogs. HTML, CSV and full reconciliation are saved before the report row is billed. Already-delivered rows are skipped if a run is resurrected. An effective budget below $19 prevents a real report; account limits may override saved run settings. Owner validation runs show owner resource usage separately.
Outputs and limits
The default dataset contains one report summary, HTML/CSV links, counts, coverage, the first 100 findings and first 25 matches. datasetPreview explicitly describes these display limits. All rows are processed and the full result is available from the key-value store:
| Record | Content |
|---|---|
catalog-<analysisId>.html | Printable full checklist and match table |
REVIEW-QUEUE.csv | Every finding and source row reference; review-only, not an import file |
RECONCILIATION | Full JSON report, every finding and match |
NORMALIZED-ROWS | Every interpreted source record, row ID, field state and data-quality issue |
RUN-SUMMARY | Demo/delivery/counts/budget state |
CSV fields beginning with formula characters are prefixed with an apostrophe. Spreadsheet software can still convert numeric-looking identifiers or strip leading zeros; import SKU columns as text or use JSON for exact identifiers. Do not feed this review CSV into a platform import workflow.
Input limit: 8 MiB total, at most 4 MiB CSV text per side, 5,000 data rows per side, 60 columns per row and 10,000 characters per cell. SKU limit 160 characters; displayed titles up to 1,000. Oversized catalogs fail instead of sampling. Optional unused fields are not included in normalized outputs. No network collection, automatic file downloads, dataset reads or external model calls.
API, MCP and development
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('elfajad/product-catalog-reconciliation').call({mode: 'reconcile',supplierCatalog: { defaultCurrency: 'USD', rows: [{ sku: '001', cost: '8.00' }] },storeCatalog: { defaultCurrency: 'USD', rows: [{ sku: '001', price: '7.00' }] }}, { memory: 256, timeout: 120, maxTotalChargeUsd: 19.01 });const { items } = await client.dataset(run.defaultDatasetId).listItems();const full = await client.keyValueStore(run.defaultKeyValueStoreId).getRecord('RECONCILIATION');
Available through Apify API and MCP after publication. Limited permissions; only the current run's output storage is needed. Treat supplier costs and private exports as confidential: do not publish them in Issues or public task examples.
Node.js 22+, Apify SDK (Apache-2.0) and csv-parse (MIT). Dependencies pinned. Run npm ci, npm test, npm run sample.
For workflow context, see Shopify SKU guidance and Shopify CSV import/export guidance. Use Issues for problems with the run ID and non-sensitive field names; omit credentials and private catalog contents.