# Merchant Feed Quality Auditor (`danielmichalek/merchant-feed-quality-auditor`) Actor

Audit your product CSV or JSON before a Merchant Center upload. Find missing fields, duplicate IDs, invalid GTIN checksums and price conflicts. Ready to check your feed?

- **URL**: https://apify.com/danielmichalek/merchant-feed-quality-auditor.md
- **Developed by:** [Daniel Michałek](https://apify.com/danielmichalek) (community)
- **Categories:** E-commerce
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 60.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$30.00 / 1,000 completed feed audit reports

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

## Merchant Feed Quality Auditor

### What it does

Check a customer-supplied product CSV or JSON before a Merchant Center upload. The auditor identifies missing base fields, duplicate offer IDs, malformed prices, currency conflicts, sale prices that are not lower, invalid GTIN lengths or checksums, invalid URL syntax and unsupported availability or condition values. It returns a row-level correction list and a readable summary. This is a limited offline validation pilot for merchants and agencies.

### Input

Supply exactly one of rows or csv. Use Merchant Center field names: id, title, description, link, image_link, availability and price. Optional fields include sale_price, gtin, brand, mpn, identifier_exists, condition, item_group_id, color and size. CSV accepts a BOM, quoted multiline values and an explicitly selected comma, semicolon or tab delimiter. The g: prefix is accepted. Duplicate normalized column names fail rather than silently replacing data.

IDs, GTINs and MPNs must be strings, preserving leading zeros. Prices use a decimal point, a space and an uppercase currency code, for example 129.90 PLN. Up to six decimal places are compared with exact integer arithmetic. expectedCurrency optionally checks price currency consistency. No automatic currency conversion occurs.

### Output

One dataset report contains counts, every finding, product row statuses, corrective actions and explicit limitations. AUDIT is the complete JSON report, FINDINGS_CSV is a downloadable row-level checklist and REPORT is Markdown. CSV cells with spreadsheet formula prefixes are neutralized. No product values are changed automatically.

### Pricing and cost control

Proposed PAY_PER_EVENT price: USD 0.03 per completed feed-report event, with up to 5000 products and 3 MB total input. maxRows can lower the limit. Overflow is rejected without truncation. Exactly one report is emitted after exports are saved. No AI, paid APIs, proxy, ad-account access or network fetches are required. Cloud pricing and actual cost remain unverified until the guarded deployment and run.

### Limitations and fair use

These checks do not guarantee Google approval. Category-specific fields, country policies, tax, shipping, image content, URL availability, landing page prices and GS1 identifier assignment are not verified. Some products have valid identifier exemptions. Missing identifiers, shared GTINs and incomplete variant attributes therefore produce review warnings rather than universal rejection claims. Use Google Merchant Center diagnostics for the final decision.

Input is saved in the run storage according to the platform retention settings; do not upload credentials or private customer records. No third-party AI processor receives the feed. Technical errors fail the run, and storage failures prevent the report billing emission. A valid feed with zero findings still produces one report.

### Example

examples/input.json includes a valid offer, an offer with deliberately broken fields and a custom product exemption. examples/input-csv.json demonstrates the CSV path. Run npm ci --ignore-scripts, npm test, then use the existing factory release.mjs --local-run --write-example. The canonical production source stays in this directory; the Apifi boty folder is only a view.

# Actor input Schema

## `rows` (type: `array`):

Product objects. Supply exactly one of rows or csv. IDs, MPNs and GTINs must be strings. Prefilled values are synthetic demonstration data; replace them with your own samples.

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

Quoted CSV with header. Maximum total input 3 MB. No file download or network requests. Clear the prefilled rows field when supplying CSV.

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

Explicit CSV delimiter.

## `expectedCurrency` (type: `string`):

Optional uppercase three-letter currency code, e.g. PLN.

## `maxRows` (type: `integer`):

Hard cap; larger feeds fail without truncation.

## `maxItems` (type: `integer`):

One completed report per run.

## Actor input object example

```json
{
  "rows": [
    {
      "id": "001",
      "title": "Blue ceramic mug",
      "description": "A reusable ceramic mug, 300 ml.",
      "link": "https://example.com/mug",
      "image_link": "https://example.com/mug.jpg",
      "availability": "in_stock",
      "price": "99.95 EUR",
      "gtin": "4006381333931",
      "brand": "Example",
      "condition": "new"
    },
    {
      "id": "001",
      "title": "",
      "description": "A reusable ceramic mug, 300 ml.",
      "link": "https://example.com/mug",
      "image_link": "/bad.jpg",
      "availability": "in_stock",
      "price": "99.95 EUR",
      "gtin": "4006381333932",
      "brand": "Example",
      "condition": "new",
      "sale_price": "120 EUR"
    },
    {
      "id": "custom",
      "title": "Blue ceramic mug",
      "description": "A reusable ceramic mug, 300 ml.",
      "link": "https://example.com/mug",
      "image_link": "https://example.com/mug.jpg",
      "availability": "in_stock",
      "price": "99.95 EUR",
      "gtin": "",
      "brand": "",
      "condition": "new",
      "identifier_exists": false
    }
  ],
  "delimiter": ",",
  "maxRows": 5000,
  "maxItems": 1
}
```

# Actor output Schema

## `results` (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 = {
    "rows": [
        {
            "id": "001",
            "title": "Blue ceramic mug",
            "description": "A reusable ceramic mug, 300 ml.",
            "link": "https://example.com/mug",
            "image_link": "https://example.com/mug.jpg",
            "availability": "in_stock",
            "price": "99.95 EUR",
            "gtin": "4006381333931",
            "brand": "Example",
            "condition": "new"
        },
        {
            "id": "001",
            "title": "",
            "description": "A reusable ceramic mug, 300 ml.",
            "link": "https://example.com/mug",
            "image_link": "/bad.jpg",
            "availability": "in_stock",
            "price": "99.95 EUR",
            "gtin": "4006381333932",
            "brand": "Example",
            "condition": "new",
            "sale_price": "120 EUR"
        },
        {
            "id": "custom",
            "title": "Blue ceramic mug",
            "description": "A reusable ceramic mug, 300 ml.",
            "link": "https://example.com/mug",
            "image_link": "https://example.com/mug.jpg",
            "availability": "in_stock",
            "price": "99.95 EUR",
            "gtin": "",
            "brand": "",
            "condition": "new",
            "identifier_exists": false
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("danielmichalek/merchant-feed-quality-auditor").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 = { "rows": [
        {
            "id": "001",
            "title": "Blue ceramic mug",
            "description": "A reusable ceramic mug, 300 ml.",
            "link": "https://example.com/mug",
            "image_link": "https://example.com/mug.jpg",
            "availability": "in_stock",
            "price": "99.95 EUR",
            "gtin": "4006381333931",
            "brand": "Example",
            "condition": "new",
        },
        {
            "id": "001",
            "title": "",
            "description": "A reusable ceramic mug, 300 ml.",
            "link": "https://example.com/mug",
            "image_link": "/bad.jpg",
            "availability": "in_stock",
            "price": "99.95 EUR",
            "gtin": "4006381333932",
            "brand": "Example",
            "condition": "new",
            "sale_price": "120 EUR",
        },
        {
            "id": "custom",
            "title": "Blue ceramic mug",
            "description": "A reusable ceramic mug, 300 ml.",
            "link": "https://example.com/mug",
            "image_link": "https://example.com/mug.jpg",
            "availability": "in_stock",
            "price": "99.95 EUR",
            "gtin": "",
            "brand": "",
            "condition": "new",
            "identifier_exists": False,
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("danielmichalek/merchant-feed-quality-auditor").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 '{
  "rows": [
    {
      "id": "001",
      "title": "Blue ceramic mug",
      "description": "A reusable ceramic mug, 300 ml.",
      "link": "https://example.com/mug",
      "image_link": "https://example.com/mug.jpg",
      "availability": "in_stock",
      "price": "99.95 EUR",
      "gtin": "4006381333931",
      "brand": "Example",
      "condition": "new"
    },
    {
      "id": "001",
      "title": "",
      "description": "A reusable ceramic mug, 300 ml.",
      "link": "https://example.com/mug",
      "image_link": "/bad.jpg",
      "availability": "in_stock",
      "price": "99.95 EUR",
      "gtin": "4006381333932",
      "brand": "Example",
      "condition": "new",
      "sale_price": "120 EUR"
    },
    {
      "id": "custom",
      "title": "Blue ceramic mug",
      "description": "A reusable ceramic mug, 300 ml.",
      "link": "https://example.com/mug",
      "image_link": "https://example.com/mug.jpg",
      "availability": "in_stock",
      "price": "99.95 EUR",
      "gtin": "",
      "brand": "",
      "condition": "new",
      "identifier_exists": false
    }
  ]
}' |
apify call danielmichalek/merchant-feed-quality-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,danielmichalek/merchant-feed-quality-auditor"
        }
    }
}
```

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/xbtuyHHzxdVw3euFI/builds/6LsWusoVtcdtYY64F/openapi.json
