# Shopify & WooCommerce Product CSV Validator and Auto-Fixer (`naricky/product-csv-import-validator`) Actor

Pre-flight check for Shopify and WooCommerce product import CSV/XLSX files. Replays ~40 importer rules (Default Title trap, variation linking, duplicate SKUs, prices, image links, Excel mangling, mojibake), auto-fixes safe issues with a change log and splits importer-safe chunks.

- **URL**: https://apify.com/naricky/product-csv-import-validator.md
- **Developed by:** [Ricky Lee](https://apify.com/naricky) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$590.00 / 1,000 product file validated (per 10,000 rows)s

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 & WooCommerce Product CSV Validator and Auto-Fixer

Pre-flight check for Shopify and WooCommerce product import CSV/XLSX files. It replays about 40 importer rules row by row (the Default Title trap, option and variation linking, duplicate SKUs, price formats, image links, Excel mangling, mojibake), auto-fixes the safe issues with a full change log, and splits big catalogs into importer-safe chunks that never cut a product in half.

Shopify's importer reports errors without a row or column, and can show "Import successful" while rows are dropped. WooCommerce can report "2490 products updated" while variations silently vanish. This Actor tells you, before you upload, exactly which row and column will fail, why, and what to change. Where a fix is safe and deterministic it writes the corrected file for you.

### Features

- **Both platforms in one tool**: Shopify (current 2024+ template and the legacy `Handle / Variant Price` template) and the WooCommerce core product CSV importer. The platform is detected from the header row, or you can force it.
- **Shopify structure checks**: the `The variant 'Default Title' already exists` trap (image rows that still carry variant columns), option names on the first row, missing or undeclared option values, duplicate option combinations, handles that Shopify would rewrite (`Mug Big` → `mug-big`), rows of one product split apart in the file, title on the first row, metafield column syntax, tag and title limits.
- **WooCommerce variation linking**: variations with no Parent, a parent that isn't `variable`, a parent listed after its variations, attribute names or values that the parent doesn't declare, duplicate attribute combinations, duplicate SKUs/IDs (the cause of silently skipped variations), variable products with no attributes or no variations.
- **Formats**: prices with currency symbols (`$12.50`), decimal commas (`12,50` → `12.50`, never `1250`), thousands separators, ambiguous values like `1,234` reported instead of guessed, integer stock and weights, TRUE/FALSE and 1/0 booleans, Status, inventory policy, weight units, sale dates (`31/12/2026` → `2026-12-31`), category `>` hierarchies, image lists.
- **Excel and encoding damage**: mojibake (`CafÃ©` → `Café`), Windows-1252 and UTF-16 files, BOM, semicolon/tab delimiters, scientific-notation SKUs and barcodes (`1.23457E+12`, expanded only when no digits were lost), barcodes that lost their leading zero (restored only when the GTIN check digit proves it), wrong check digits, line breaks and stray spaces in handles, SKUs and option values, ragged rows from unquoted commas.
- **Image links**: Google Drive and Dropbox share links converted to direct links, missing `https://`, spaces in URLs, local file paths.
- **Safe auto-fix**: only deterministic repairs, each one logged in `CHANGELOG.csv` with row, column, old and new value. Anything that needs a judgement call is reported with a plain-English fix and left alone.
- **Importer-safe chunks**: optional `FIXED_part_01..N.csv` under Shopify's 15 MB limit (or a row cap for WooCommerce). A Shopify handle or a WooCommerce parent with its variations is never split across files.
- **No store access**: it never logs in to or touches your store. Your file is processed in memory and results stay in your run storage.
- **Inputs**: file URLs (including Google Drive, Dropbox and Google Sheets share links), XLSX, or pasted CSV. Works from the API, schedules, Make, Zapier and n8n.

### Input

| Field | Description |
|---|---|
| `files` | URLs of product CSV/TSV/XLSX files. Drive, Dropbox and Google Sheets share links work if anyone with the link can view. |
| `csvContent` | Paste a CSV instead. The prefilled sample is a broken Shopify file; it is ignored when you also give URLs. |
| `fileName` | Name used for pasted content. |
| `platform` | `auto` (default), `shopify` or `woocommerce`. |
| `mode` | `validate_and_fix` (default) writes `FIXED.csv` and `CHANGELOG.csv`; `validate` only reports. |
| `decimalComma` | `auto` (default), `yes` (12,50 = 12.50) or `no` (1,234 = 1234). In `auto`, ambiguous values are reported, not changed. |
| `includeWarnings` | List warnings as well as errors (default true). |
| `ignoreRules` | Rule IDs to skip, e.g. `shopify.sku.duplicate`. |
| `splitChunks`, `chunkMaxRows`, `chunkMaxMb` | Write importer-safe chunk files. |
| `delimiter`, `sheetName` | Delimiter override; XLSX worksheet. |

### Output

**Dataset**: one row per finding, then one `file-summary` row per file.

```json
{"type": "finding", "file": "products.csv", "row": 3, "key": "cafe-mug",
 "column": "Variant Inventory Policy, Variant Fulfillment Service, Variant Price, Variant Requires Shipping, Variant Taxable",
 "rule": "shopify.variant.default_title_duplicate", "severity": "error",
 "message": "Extra row for a product without options carries variant columns (...), so Shopify tries to create a second 'Default Title' variant and rejects the product.",
 "importerMessage": "Validation failed: The variant 'Default Title' already exists.",
 "value": "deny; manual; 12.50; TRUE; TRUE", "fix": "Cleared the variant columns on this image/continuation row.",
 "fixable": true, "autoFixed": true, "newValue": "(cleared)"}
```

```json
{"type": "file-summary", "file": "products.csv", "platform": "shopify", "template": "legacy", "rows": 5,
 "products": 3, "variants": 4, "errors": 7, "warnings": 6, "unfixedErrors": 2, "changesApplied": 15,
 "verdict": "2 error(s) need manual fixes", "findingsByRule": {"...": 1}, "billedEvents": 1}
```

`row` is the row number you see in Excel (the header is row 1). Severity `error` means the importer rejects the row or silently drops data; `warning` means the import works but the data is probably wrong.

**Key-value store**: `<file>-FIXED.csv` (UTF-8, comma-separated, original column order), `<file>-CHANGELOG.csv` (every edited cell), optional `<file>-FIXED_part_NN.csv` chunks, `REPORT.html` (readable report) and `REPORT.json`.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Product file validated (and auto-fixed), per started block of 10,000 rows | $0.59 |

A 3,000-row migration file costs $0.59; a 25,000-row catalog costs $1.77. Files that fail to download or parse are not charged. You pay the same whether the file is clean or broken.

### Limits

- Files up to 50 MB. Legacy `.xls` files are not supported; save as `.xlsx` or CSV.
- Shopify and WooCommerce don't publish machine-readable importer specs. Rules follow the public CSV documentation and the importers' observed behaviour. If you hit a false positive, add the rule ID to `ignoreRules`.
- Excel scientific-notation values that lost digits can't be recovered; re-export them with the column formatted as Text.

# Actor input Schema

## `files` (type: `array`):

Links to your product import files (CSV, TSV or XLSX). Google Drive, Dropbox and Google Sheets share links are converted to direct downloads automatically; the file must be readable by anyone with the link.

## `csvContent` (type: `string`):

Paste a product CSV here instead of (or in addition to) URLs. The prefilled sample is a broken Shopify file showing the traps this tool catches; it is ignored when you also provide file URLs.

## `fileName` (type: `string`):

File name used in the report and for the fixed-file keys when you paste CSV content.

## `platform` (type: `string`):

Which importer's rules to replay. Auto-detect reads the header row.

## `mode` (type: `string`):

Auto-fix only applies safe, deterministic repairs and logs every changed cell. Findings that need a human are reported, never guessed.

## `decimalComma` (type: `string`):

How to read numbers such as 12,50 or 1,234 in price, weight and stock columns.

## `includeWarnings` (type: `boolean`):

Also list warnings (data-quality issues the importer accepts). Errors are always listed. Warning fixes are still applied and logged.

## `ignoreRules` (type: `array`):

Rule IDs to skip, e.g. shopify.sku.duplicate or barcode.check_digit. Ignored rules are neither reported nor fixed.

## `splitChunks` (type: `boolean`):

Also write FIXED_part_01..N.csv files. A Shopify product (all rows of a handle) or a WooCommerce variable product with its variations is never split across files.

## `chunkMaxRows` (type: `integer`):

Row cap per chunk. 0 = no row cap for Shopify (size cap only) and 5,000 rows for WooCommerce.

## `chunkMaxMb` (type: `integer`):

Size cap per chunk in MB. Shopify rejects product CSVs over 15 MB.

## `delimiter` (type: `string`):

Leave empty to detect (comma, semicolon, tab or pipe). Use \t or tab for tab-separated files.

## `sheetName` (type: `string`):

Worksheet to read from XLSX files. Leave empty for the first sheet.

## Actor input object example

```json
{
  "csvContent": "Handle,Title,Body (HTML),Vendor,Type,Tags,Published,Option1 Name,Option1 Value,Variant SKU,Variant Grams,Variant Inventory Qty,Variant Inventory Policy,Variant Fulfillment Service,Variant Price,Variant Compare At Price,Variant Requires Shipping,Variant Taxable,Variant Barcode,Image Src,Image Position,Status\ncafe-mug,CafÃ© Mug,<p>Stoneware mug</p>,Acme,Mugs,\"mug, coffee\",TRUE,Title,Default Title,MUG-001,350,12,deny,manual,$12.50,,TRUE,TRUE,1.23457E+12,https://drive.google.com/file/d/1AbCdEfGhIjK/view?usp=sharing,1,active\ncafe-mug,,,,,,,,,,,,deny,manual,12.50,,TRUE,TRUE,,https://cdn.example.com/mug-back.jpg,2,\nMug Big,Big Mug,<p>Large mug</p>,Acme,Mugs,mug,yes,Size,Large,MUG-001,500,5,deny,manual,\"12,50\",,TRUE,TRUE,36000291452,https://cdn.example.com/big mug.jpg,1,Active\ntee,Basic Tee,<p>Cotton tee</p>,Acme,Shirts,tee,TRUE,Size,S,TEE-S,200,3,deny,manual,9.99,8.00,TRUE,TRUE,,https://cdn.example.com/tee.jpg,1,active\ntee,,,,,,,,S,TEE-S2,200,4,deny,manual,9.99,,TRUE,TRUE,,,,\n",
  "fileName": "pasted-products.csv",
  "platform": "auto",
  "mode": "validate_and_fix",
  "decimalComma": "auto",
  "includeWarnings": true,
  "splitChunks": false,
  "chunkMaxRows": 0,
  "chunkMaxMb": 14
}
```

# Actor output Schema

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

No description

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

No description

## `files` (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 = {
    "csvContent": `Handle,Title,Body (HTML),Vendor,Type,Tags,Published,Option1 Name,Option1 Value,Variant SKU,Variant Grams,Variant Inventory Qty,Variant Inventory Policy,Variant Fulfillment Service,Variant Price,Variant Compare At Price,Variant Requires Shipping,Variant Taxable,Variant Barcode,Image Src,Image Position,Status
cafe-mug,CafÃ© Mug,<p>Stoneware mug</p>,Acme,Mugs,"mug, coffee",TRUE,Title,Default Title,MUG-001,350,12,deny,manual,$12.50,,TRUE,TRUE,1.23457E+12,https://drive.google.com/file/d/1AbCdEfGhIjK/view?usp=sharing,1,active
cafe-mug,,,,,,,,,,,,deny,manual,12.50,,TRUE,TRUE,,https://cdn.example.com/mug-back.jpg,2,
Mug Big,Big Mug,<p>Large mug</p>,Acme,Mugs,mug,yes,Size,Large,MUG-001,500,5,deny,manual,"12,50",,TRUE,TRUE,36000291452,https://cdn.example.com/big mug.jpg,1,Active
tee,Basic Tee,<p>Cotton tee</p>,Acme,Shirts,tee,TRUE,Size,S,TEE-S,200,3,deny,manual,9.99,8.00,TRUE,TRUE,,https://cdn.example.com/tee.jpg,1,active
tee,,,,,,,,S,TEE-S2,200,4,deny,manual,9.99,,TRUE,TRUE,,,,`
};

// Run the Actor and wait for it to finish
const run = await client.actor("naricky/product-csv-import-validator").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 = { "csvContent": """Handle,Title,Body (HTML),Vendor,Type,Tags,Published,Option1 Name,Option1 Value,Variant SKU,Variant Grams,Variant Inventory Qty,Variant Inventory Policy,Variant Fulfillment Service,Variant Price,Variant Compare At Price,Variant Requires Shipping,Variant Taxable,Variant Barcode,Image Src,Image Position,Status
cafe-mug,CafÃ© Mug,<p>Stoneware mug</p>,Acme,Mugs,\"mug, coffee\",TRUE,Title,Default Title,MUG-001,350,12,deny,manual,$12.50,,TRUE,TRUE,1.23457E+12,https://drive.google.com/file/d/1AbCdEfGhIjK/view?usp=sharing,1,active
cafe-mug,,,,,,,,,,,,deny,manual,12.50,,TRUE,TRUE,,https://cdn.example.com/mug-back.jpg,2,
Mug Big,Big Mug,<p>Large mug</p>,Acme,Mugs,mug,yes,Size,Large,MUG-001,500,5,deny,manual,\"12,50\",,TRUE,TRUE,36000291452,https://cdn.example.com/big mug.jpg,1,Active
tee,Basic Tee,<p>Cotton tee</p>,Acme,Shirts,tee,TRUE,Size,S,TEE-S,200,3,deny,manual,9.99,8.00,TRUE,TRUE,,https://cdn.example.com/tee.jpg,1,active
tee,,,,,,,,S,TEE-S2,200,4,deny,manual,9.99,,TRUE,TRUE,,,,
""" }

# Run the Actor and wait for it to finish
run = client.actor("naricky/product-csv-import-validator").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 '{
  "csvContent": "Handle,Title,Body (HTML),Vendor,Type,Tags,Published,Option1 Name,Option1 Value,Variant SKU,Variant Grams,Variant Inventory Qty,Variant Inventory Policy,Variant Fulfillment Service,Variant Price,Variant Compare At Price,Variant Requires Shipping,Variant Taxable,Variant Barcode,Image Src,Image Position,Status\\ncafe-mug,CafÃ© Mug,<p>Stoneware mug</p>,Acme,Mugs,\\"mug, coffee\\",TRUE,Title,Default Title,MUG-001,350,12,deny,manual,$12.50,,TRUE,TRUE,1.23457E+12,https://drive.google.com/file/d/1AbCdEfGhIjK/view?usp=sharing,1,active\\ncafe-mug,,,,,,,,,,,,deny,manual,12.50,,TRUE,TRUE,,https://cdn.example.com/mug-back.jpg,2,\\nMug Big,Big Mug,<p>Large mug</p>,Acme,Mugs,mug,yes,Size,Large,MUG-001,500,5,deny,manual,\\"12,50\\",,TRUE,TRUE,36000291452,https://cdn.example.com/big mug.jpg,1,Active\\ntee,Basic Tee,<p>Cotton tee</p>,Acme,Shirts,tee,TRUE,Size,S,TEE-S,200,3,deny,manual,9.99,8.00,TRUE,TRUE,,https://cdn.example.com/tee.jpg,1,active\\ntee,,,,,,,,S,TEE-S2,200,4,deny,manual,9.99,,TRUE,TRUE,,,,\\n"
}' |
apify call naricky/product-csv-import-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,naricky/product-csv-import-validator"
        }
    }
}
```

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/qh9RcknRTMl4miBRr/builds/y3JXRdC8drDPvb9cj/openapi.json
