# Google Merchant Center Feed Checker: Price, Stock & Image Audit (`code54dev/feedguard`) Actor

Pre-check a Google Merchant Center feed (XML, CSV/TSV or Shopify products.json) against live product pages. Flags price and stock mismatches, 404s and redirects, missing GTIN/brand/MPN, bad images, title limits, shipping/returns. Independent tool, not affiliated with Google.

- **URL**: https://apify.com/code54dev/feedguard.md
- **Developed by:** [Code54](https://apify.com/code54dev) (community)
- **Categories:** E-commerce, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## 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

**FeedGuard: Google Merchant Center Feed Checker** pre-checks your product feed against your live product pages, so you can fix the mismatches that cause Merchant Center disapprovals before Google finds them. Point it at a Merchant Center feed (XML/RSS, CSV/TSV) or a Shopify store's `/products.json`, and it fetches every product page and flags price and availability mismatches, 404s and redirects, missing GTIN/brand/MPN, broken or too-small images, title and description problems, currency mismatches and missing shipping/return info. You get one row per item, a summary of your top issues, and a shareable HTML report. It is an independent pre-check by Code54, not affiliated with Google, and not a guarantee of approval.

### What does the Merchant Center feed checker detect?

For each feed item FeedGuard compares **feed values vs the live page** and reports:

| Check | Issue codes |
|---|---|
| **Price mismatch**, including `sale_price`, read from JSON-LD `Product/Offer`, microdata, `og:price` / `product:price`, with a visible-price fallback | `PRICE_MISMATCH`, `SALE_PRICE_MISMATCH`, `SALE_PRICE_INVALID`, `PRICE_NOT_FOUND_ON_PAGE` |
| **Availability mismatch** (in stock / out of stock / pre-order) | `AVAILABILITY_MISMATCH` |
| **Landing page problems**: 404/410, other non-200, redirects, redirect to homepage, blocked or unreachable | `PAGE_NOT_FOUND`, `PAGE_HTTP_ERROR`, `PAGE_REDIRECT`, `PAGE_REDIRECT_TO_HOMEPAGE`, `PAGE_BLOCKED`, `PAGE_FETCH_FAILED` |
| **Missing or invalid identifiers**: GTIN (with check-digit validation), brand, MPN | `GTIN_MISSING`, `GTIN_INVALID`, `GTIN_MISMATCH`, `BRAND_MISSING`, `MPN_MISSING`, `IDENTIFIERS_MISSING` |
| **Image problems**: broken URL, not an image, under 100×100 px (error) or 250×250 px (apparel warning) | `IMAGE_MISSING`, `IMAGE_BROKEN`, `IMAGE_TOO_SMALL`, `IMAGE_SMALL_FOR_APPAREL` |
| **Title and description**: missing, over 150 / 5,000 characters, ALL CAPS, too short | `TITLE_TOO_LONG`, `TITLE_ALL_CAPS`, `DESCRIPTION_MISSING`, `DESCRIPTION_TOO_LONG`, … |
| **Currency mismatch** between feed, page and your expected currency | `CURRENCY_MISMATCH` |
| **Shipping and return info** not found in the feed or on the page (heuristic) | `SHIPPING_INFO_MISSING`, `RETURN_INFO_NOT_DETECTED` |

Every issue has a severity: `error` (likely disapproval), `warning` (likely problem or limited performance) or `info` (worth knowing). Each item gets the highest severity of its issues.

### What feeds and inputs does it accept?

- **Merchant Center XML/RSS or Atom feeds** (`g:` attributes)
- **CSV / TSV feeds** (comma, tab, semicolon or pipe; Google Sheets exports work)
- **Shopify stores**: a `/products.json` URL, or just the store's home page URL
- **A plain list of product page URLs** (page-level checks only)
- Options: `maxItems` (default 100), `concurrency`, target `country` and `currency`, price tolerance, and **sample-only mode** to check a random sample of a big feed quickly.

If you run it with empty input, FeedGuard checks 10 items of a public demo store so you can see the output format.

#### Example input

```json
{"feedUrl": "https://example-store.com/products.json", "maxItems": 100, "concurrency": 5, "currency": "USD", "country": "US"}
```

### What do I get back?

1. **Dataset**: one row per item with `id`, `url`, `severity`, `issues[]` (code, severity, message, feed value, page value), the feed and page values side by side, HTTP status, final URL after redirects, and image size.
2. **`SUMMARY` record** (key-value store): items checked, counts by severity, counts by issue type and a top-issues list.
3. **`REPORT.html`** (key-value store): a clean report you can open in a browser or send to a client.

#### Example output (one item)

```json
{"id":"7340901859408-42146889","url":"https://example-store.com/products/tee","httpStatus":200,"severity":"error",
 "issueCodes":["PRICE_MISMATCH"],
 "issues":[{"code":"PRICE_MISMATCH","severity":"error","message":"Feed price 24.99 does not match the page price 29.99.","feedValue":24.99,"pageValue":29.99}],
 "feed":{"price":24.99,"availability":"in_stock","currency":"USD"},
 "page":{"price":29.99,"priceSource":"json-ld","availability":"in_stock","currency":"USD"},
 "image":{"status":200,"width":1200,"height":1200}}
```

### Who is this for?

- Ecommerce owners and Shopify merchants fixing Merchant Center disapprovals
- Google Shopping and PMax agencies auditing client feeds before launch
- Feed managers who want a repeatable weekly check (schedule the Actor)
- AI agents, via Apify MCP (`call-actor`)

### How do I fix "price mismatch" and "availability mismatch" disapprovals?

1. Run FeedGuard on your feed with `maxItems` set to your catalog size, or start with a 25-item sample.
2. Open `REPORT.html` and fix the `error` items first: the feed price (or sale price) must match the price a shopper sees on the landing page, and in-stock status must match too.
3. Re-run until the error count is zero, then check the Diagnostics tab in Merchant Center. Google's own check is the final word.

### How does FeedGuard behave on your site? (politeness)

- Identifies itself clearly: `FeedGuardBot/0.1 (independent product-feed pre-check; +https://code54.dev; support@code54.dev)`.
- **Respects `robots.txt`**: pages disallowed for bots are skipped and reported as `ROBOTS_BLOCKED`, and any `Crawl-delay` is honored.
- Rate limited: a pause between requests to the same site (default 0.5 s) and low default concurrency.
- It never tries to get around bot protection. If a store shows a CAPTCHA or challenge, the item is reported as `PAGE_BLOCKED` instead.
- Images are checked by downloading only the first 128 KB.

### FAQ

#### Is FeedGuard affiliated with Google?

No. FeedGuard is an independent tool by Code54 LLC. It is not affiliated with, endorsed by or sponsored by Google. "Google Merchant Center" is used only to describe the feed format it checks.

#### Does a clean report guarantee Merchant Center approval?

No. FeedGuard is a pre-check. Google applies its own policies, crawls from its own locations and may find issues FeedGuard does not (and the reverse). Treat the report as a fast way to catch the common, mechanical errors.

#### Why did FeedGuard flag a price mismatch when my page looks right?

Common causes: a multi-currency store showing another currency to our server's location (set `currency`, or use a proxy for your country), a variant price shown only after JavaScript runs, or taxes shown on the page but not in the feed. Visible-text matches are lower confidence and are reported as `warning`, not `error`.

#### Does it run JavaScript?

Not in this version. FeedGuard reads server-rendered HTML and structured data (JSON-LD, microdata, Open Graph), which is what most stores and Google's first pass rely on. If a price only appears after JavaScript runs, you will see `PRICE_NOT_FOUND_ON_PAGE`.

#### What does it cost?

FeedGuard is free to use. You only pay normal Apify platform usage for the run, which is typically a fraction of a cent per 20 items, and Apify plans include monthly free credit. Use `sampleOnly` to try it on a handful of items first.

#### Can I check just a sample of a large feed?

Yes. Turn on `sampleOnly` and set `sampleSize` (for example 50). FeedGuard picks random items from the first 2,000 in the feed.

### Related Code54 Actors

- [USAspending Expiring Contracts & Recompete Scraper](https://apify.com/code54dev/expiring-federal-contracts): federal contracts ending in the next 3–18 months, with incumbent, ceiling, options and a recompete signal.
- [Federal Contractor / Incumbent Lookup by UEI](https://apify.com/code54dev/federal-incumbent-lookup): a 5-year USAspending profile of any contractor by UEI or company name.
- [Grants.gov Scraper: Nonprofit Grant Deadlines](https://apify.com/code54dev/grants-gov-opportunity-feed): open and forecasted federal grants with deadlines, award amounts and eligibility.

***

Need a custom feed audit or data tool? Code54 builds them: https://code54.dev

*Product of Code54 LLC, 2108 N St, Suite #18005, Sacramento, CA 95816. FeedGuard is an independent pre-check and is not affiliated with or endorsed by Google. Results are best-effort and are not a guarantee of Merchant Center approval. Questions: support@code54.dev*

# Actor input Schema

## `feedUrl` (type: `string`):

URL of a Google Merchant Center-style feed: an XML/RSS (g: namespace) or Atom feed, a CSV/TSV file, or a Shopify store's /products.json (or just the store's home page URL). Leave empty if you use 'Product page URLs'.

## `productUrls` (type: `array`):

Check specific product pages instead of a feed. Only page-level checks run (HTTP status, redirects, structured-data price/availability/identifiers, image, title/description). Used only when 'Product feed URL' is empty.

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

Maximum number of feed items to check in this run. Lower values finish faster and use less platform credit.

## `sampleOnly` (type: `boolean`):

Pick a random sample from the feed (up to its first 2,000 items) instead of the first N items. A quick, cheap way to spot systemic problems in a large feed.

## `sampleSize` (type: `integer`):

Number of random items to check when 'Check only a random sample' is on (never more than 'Max items').

## `concurrency` (type: `integer`):

How many pages to fetch in parallel. Keep it low (3–5) for small stores; FeedGuard also waits between requests to the same site.

## `requestDelaySecs` (type: `number`):

Minimum pause between two page requests to the same domain. A robots.txt Crawl-delay, if present, takes precedence when it is longer. Raise this if a store starts showing bot challenges.

## `country` (type: `string`):

Two-letter country code of the market you sell to, for example US, GB, CA, DE. It sets the language preference sent to the store. Multi-country stores may show different prices by visitor location; use a proxy from that country if needed.

## `currency` (type: `string`):

For example USD or EUR. Used when feed prices have no currency (such as Shopify), and to flag items where the feed or the page uses a different currency. Leave empty to only compare feed vs page.

## `priceTolerancePercent` (type: `number`):

Treat prices as matching if they differ by no more than this percentage (minimum one cent). Google expects an exact match, so keep it at 1 or lower unless you see rounding noise.

## `checkImages` (type: `boolean`):

Download the first 128 KB of each image to confirm it loads and read its size (under 100x100 px is an error, under 250x250 a warning for apparel).

## `checkShippingReturns` (type: `boolean`):

Look for shipping and return/refund information on the product page (structured data, text or policy links) and in the feed. Heuristic: results are informational.

## `proxyConfiguration` (type: `object`):

Off by default. Use Apify Proxy only if a store blocks datacenter IPs or you need a specific country. Image checks always connect directly.

## Actor input object example

```json
{
  "feedUrl": "https://example-store.com/products.json",
  "maxItems": 10,
  "sampleOnly": false,
  "sampleSize": 25,
  "concurrency": 5,
  "requestDelaySecs": 0.5,
  "country": "US",
  "priceTolerancePercent": 1,
  "checkImages": true,
  "checkShippingReturns": true
}
```

# Actor output Schema

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

One row per feed item (JSON/CSV/Excel via the dataset API).

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

Human-readable report with top issues and the items to fix first.

## `summary` (type: `string`):

Counts by issue type, top issues, and run settings.

# 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 = {
    "feedUrl": "https://allbirds.com/products.json",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("code54dev/feedguard").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 = {
    "feedUrl": "https://allbirds.com/products.json",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("code54dev/feedguard").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 '{
  "feedUrl": "https://allbirds.com/products.json",
  "maxItems": 10
}' |
apify call code54dev/feedguard --silent --output-dataset

```

## MCP server setup

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

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/1P0bfrKypm4AGvbhY/builds/nQjPA8Cw5ShFGDy6i/openapi.json
