# Product Catalog Quality Checker — CSV, SKU & Prices (`abdulwhab95/catalog-product-data-quality-checker`) Actor

Audit CSV catalogs or Apify product datasets for missing names, invalid prices and duplicate SKUs. Preserve originals, get per-product issue codes and a spreadsheet-safe CSV report. Arabic numbers supported. No AI API key.

- **URL**: https://apify.com/abdulwhab95/catalog-product-data-quality-checker.md
- **Developed by:** [ABDULWAHAB NASER RASHED ALQARAWI](https://apify.com/abdulwhab95) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 audited product records

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

## Product Catalog Quality Checker — CSV, SKU & Prices

Find missing product names, invalid prices and duplicate SKUs before using a catalog in another workflow. Each delivered audit preserves the original record and includes normalized fields, a PASS/WARN/FAIL status, stable issue codes and plain-English explanations. Arabic names and Arabic/Persian digits are supported.

### Try it

Run the prefilled four-product example. It deliberately includes one valid product, a duplicate SKU, an empty name, a negative price and a free sample. A successful Actor run means the audit completed; individual products can still FAIL the catalog checks.

```json
{
  "csvText": "name,sku,price,currency\nCoffee,A-1,2.750,KWD\n,A-1,-1,KWD\nSample,,0,KWD\nTea,B-1,1.500,KWD\n",
  "maxResults": 100
}
```

Supply **exactly one** of `csvText`, `csvUrl`, `records`, or `datasetId`. Remove the prefilled CSV example before using another source. CSV is UTF-8 (optional BOM), with an explicit comma, semicolon or tab delimiter. Quoted multiline cells are accepted. A URL must be publicly accessible HTTP(S); no login or proxy is required. Dataset access is read-only and must be granted to this Actor; finish the source run first.

For existing WooCommerce/Zid scraper outputs, use `datasetId` or `records` with their `name`, `sku`, `price`, `currency` and `url` fields. For other exports map exact columns:

```json
{
  "records": [{"Product": "Cup", "Item code": "001", "Retail": "1.250"}],
  "fieldMap": {"name": "Product", "sku": "Item code", "price": "Retail"}
}
```

Automatic aliases include name/title/اسم المنتج/الاسم, sku/رمز المنتج/كود المنتج, price/Regular price/Sale price/السعر/سعر المنتج, currency/العملة, url/permalink/رابط المنتج. Alias matching ignores case and surrounding header spaces. If multiple columns match, select one explicitly in `fieldMap`; no price precedence is guessed.

### Checks

| Code | Meaning |
|---|---|
| NAME\_REQUIRED | A nonblank text name is required. |
| PRICE\_REQUIRED / PRICE\_INVALID / PRICE\_NEGATIVE | Price must be a finite, nonnegative decimal. Zero and -0 are accepted. |
| SKU\_MISSING / SKU\_INVALID | Missing or non-text SKU is a warning. Codes must be text to preserve leading zeros. |
| SKU\_DUPLICATE | Every member of a duplicate SKU group is flagged as an error. |
| CURRENCY\_FORMAT | Optional supplied currency must have three ASCII letters; actual currency validity is not certified. |
| URL\_FORMAT | Optional supplied product URL must have HTTP(S), a host and no spaces or credentials. |
| SPREADSHEET\_FORMULA | A mapped text value could be interpreted as a spreadsheet formula. |

SKU comparison uses Unicode NFC normalization and surrounding whitespace removal, remains case-sensitive and does not strip leading zeros. Duplicate checks cover **all supplied input records**, even if the delivery cap stops the report early. A repeated SKU can be legitimate across stores or variants: scope the input to the catalog you intend to check.

Prices accept dot decimals, Arabic `٫`, and comma decimals when `decimalSeparator` is `","`. Comma mode rejects dots in text prices (for example `"1.234"`) to avoid misreading grouping separators. JSON numbers and Arabic decimal marks work in either mode. Grouping separators, currency symbols, booleans, nested objects, scientific notation in text and prices longer than 40 characters are not accepted. JSON numeric precision is limited by JSON parsing; use strings for exact prices and SKUs. Uppercase currency and trimmed text appear only in `normalized`; `original` is retained unchanged.

This is a data-quality audit, not an import validator for a particular commerce platform. It does not infer variable-product parent prices, resolve missing variants, check price accuracy, convert currencies, fetch product/image URLs or repair records. Variable-product parent rows without prices will be flagged. No external LLM or other paid API is called.

### Output and limits

- One dataset row per **audited product**, including products with errors: `rowNumber` (one-based data record, excluding CSV header), `status`, `issueCodes`, `issues`, `normalized`, `original`, `observedAt`.
- `RUN_REPORT` gives delivered PASS/WARN/FAIL counts, total input records, unreported records and `auditComplete`. A successful partial delivery is explicitly marked; never treat it as a complete catalog audit.
- Download **Spreadsheet-safe audit CSV** (`CATALOG_REPORT.csv`) from Output. It includes normalized fields and issue codes for delivered rows only, and escapes spreadsheet formula prefixes. Generic dataset exports preserve original values: choose JSON to keep originals without spreadsheet interpretation.
- 1–5,000 input records, 100 distinct columns, 200 characters per header, 10,000 characters per mapped audit field, and 5 MB both for source CSV and decoded JSON records. Unmapped fields such as long descriptions are preserved within the same 5 MB total limit. CSV additionally has a 100,000-character cell parser limit. A CSV close to 5 MB may exceed the decoded-record limit and must be split. Malformed/duplicate headers and ragged records fail before any result fee. Entirely blank CSV lines are malformed records.
- `maxResults` defaults to 100 and is capped at 5,000. Set it to your input size for full delivery, and set Apify's maximum cost per run. Processing has a 240-second deadline; the default run timeout is 300 seconds. Supported memory: 512 MB–1 GB.
- Inputs and outputs use your Apify account storage and retention. Nothing writes back to a source dataset or store. When a run resumes with the same input and output dataset, already delivered rows are skipped. Start a new run if inputs change. Concurrent edits to a source dataset are unsupported.

### Price

**$0.001 per delivered product audit ($1 per 1,000)** plus **$0.001 per run start** at supported memory sizes. Products with validation errors are still completed audits and are billed. Malformed input or failed fetching generates no result fee; the start fee still applies. There is no additional automatic dataset-row fee. Your displayed Apify pricing and platform terms govern charges. Development/testing consumes the author's platform resources.

### Open-source foundation

Uses [Frictionless Framework 5.18.1](https://github.com/frictionlessdata/frictionless-py), under its MIT license, for explicit in-memory name/price schema checks. Product rules, Arabic price handling, reports and Apify integration are implemented separately. The installed dependency retains its upstream license and notices. Not an official Frictionless, WooCommerce or Zid product.

### عربي

افحص ملف المنتجات CSV أو نتائج أدواتك على Apify. الأداة تكشف الاسم الناقص والسعر غير الصالح وتكرار SKU، وتطلع سبب كل مشكلة وتحافظ على البيانات الأصلية. السعر صفر صحيح، وSKU يُحفظ كنص حتى لا تضيع الأصفار. السعر دولار لكل ألف منتج مفحوص، مع 0.001 دولار لبدء التشغيل؛ المنتج الذي تظهر فيه أخطاء يُحسب لأنه تم فحصه. حد النتائج الافتراضي 100؛ ارفعه لعدد منتجاتك إذا تريد التقرير كاملاً. لا توجد تعديلات تلقائية على متجرك ولا جدول تشغيل يُنشأ تلقائياً.

### Batch delivery and limits

Audits are saved in batches of up to 100 using native per-event charging. RUN\_REPORT and the CSV include only rows actually delivered, including a spending-limited batch. A 5,000-row input is a cap; parsing complexity and the 240-second processing deadline still apply. Plain negative numeric CSV cells are no longer prefixed with an apostrophe; formula-like strings remain escaped. Original JSON records are unchanged.

# Actor input Schema

## `csvText` (type: `string`):

Supply exactly one source. Remove this example before using a URL, records or dataset ID.

## `csvUrl` (type: `string`):

Public HTTP(S) URL returning UTF-8 CSV, up to 5 MB. No authenticated downloads.

## `records` (type: `array`):

Alternative input: 1–5000 JSON product objects, up to 5 MB in total.

## `datasetId` (type: `string`):

Dataset with 1–5000 products accessible to your account. Finish the source run before auditing.

## `fieldMap` (type: `object`):

Optional exact source columns for name, price, sku, currency, url. Resolve ambiguous aliases explicitly.

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

CSV only; select the actual delimiter.

## `decimalSeparator` (type: `string`):

Dot by default. Comma mode rejects dots in text prices to avoid grouping ambiguity; JSON numbers and Arabic decimal marks are accepted.

## `maxResults` (type: `integer`):

Integer from 1 to 5000.

## `maxRequests` (type: `integer`):

Integer from 1 to 300.

## Actor input object example

```json
{
  "csvText": "name,sku,price,currency\nArabic coffee,COFFEE-1,2.750,KWD\n,COFFEE-1,-1,KWD\nFree sample,,0,KWD\nTea,TEA-1,1.500,KWD\n",
  "delimiter": ",",
  "decimalSeparator": ".",
  "maxResults": 100,
  "maxRequests": 100
}
```

# Actor output Schema

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

No description

## `runReport` (type: `string`):

No description

## `safeCsvReport` (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 = {
    "csvText": `name,sku,price,currency
Arabic coffee,COFFEE-1,2.750,KWD
,COFFEE-1,-1,KWD
Free sample,,0,KWD
Tea,TEA-1,1.500,KWD`
};

// Run the Actor and wait for it to finish
const run = await client.actor("abdulwhab95/catalog-product-data-quality-checker").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 = { "csvText": """name,sku,price,currency
Arabic coffee,COFFEE-1,2.750,KWD
,COFFEE-1,-1,KWD
Free sample,,0,KWD
Tea,TEA-1,1.500,KWD
""" }

# Run the Actor and wait for it to finish
run = client.actor("abdulwhab95/catalog-product-data-quality-checker").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 '{
  "csvText": "name,sku,price,currency\\nArabic coffee,COFFEE-1,2.750,KWD\\n,COFFEE-1,-1,KWD\\nFree sample,,0,KWD\\nTea,TEA-1,1.500,KWD\\n"
}' |
apify call abdulwhab95/catalog-product-data-quality-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abdulwhab95/catalog-product-data-quality-checker"
        }
    }
}
```

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/6gLGdTsTo8RznXJ41/builds/AXMl3eYadLPM8RcIt/openapi.json
