OneShot

Product Feed & Merchant Center Compliance

Build Product Feed & Merchant Center Compliance on nextjs-feed

A Google Shopping product feed that survives the reviewer: spec-valid RSS 2.0 output validated offline against the transcribed product data spec, a feed-to-landing-page price and availability consistency crawler, GS1-correct GTIN handling with honest identifier_exists semantics, on-page structured data generated from the same source as the feed, and a diagnostic that reports each named disapproval cause — including the one it cannot check.

Verification pendingPinned for nextjs-feed; verification pending. No pass rate is published until the harness runs it.
Coming soon — $129

The thing that gets your products disapproved is usually not in your feed. The

#1 data-related disapproval cause is the feed disagreeing with the landing page

Google crawls — a mismatch that is invisible to every validator that only looks

at the file, and that no agent builds a check for, because it is not a property

of the feed. This pack builds the whole surface: RSS 2.0 output validated

offline against Google's product data spec shipped as versioned data, a

consistency crawler that compares price, currency, sale window and availability

against your real pages, GS1-correct GTIN handling, honest `identifier_exists`

semantics, and page structured data generated from the same object as the feed

so the two cannot drift. **This pack is currently `draft`: no clean-room receipt

has been published for it yet.** When one is, this line will read `N of M`

clean-room builds passed with all 15 acceptance checks — and until it does,

treat everything below about the build as design intent, not evidence. What is

already true today is that the acceptance suite runs hermetically: no Merchant

Center account, no Google API, no network.

Example use cases

  • **A Rails/Django/Next.js store with a custom storefront** that has no

platform-supplied Google channel — the merchant is hand-writing a feed

template and finding out what is wrong from Merchant Center Diagnostics, one

three-day review cycle at a time.

  • **An agency running Shopping feeds for a dozen clients** — the same

`feed-doctor` command, wired into each client's CI, turns "the client emailed

saying products vanished" into a build failure on the deploy that caused it.

  • **A headless commerce build where prices live in one service and pages render

in another** — exactly the architecture where feed and landing page drift

apart quietly, and exactly the one where a consistency crawler pays for

itself in a week.

  • **A handmade/vintage seller with no GTINs** — custom goods, antiques and

pre-1970 books are the case `identifier_exists` exists for, and the case

everyone gets backwards in both directions.

  • **A store adding a second target country** — the conditional rules

(apparel attributes, shipping, tax, currency) are country-scoped, and the

pack makes running one pipeline per country a config change rather than a

fork.

Scale envelope

Derived from the shipped architecture and the pinned stack, not from a load

test. Nothing here is receipt-measured; the pack has no receipt yet, and where

a number is an estimate it says so.

  • **Feed generation is O(n) string building, in memory.** At a typical ~700 B

to 1.5 KB of XML per item, a 50,000-item feed is roughly 35–75 MB of string —

fine for Node's default heap on a small container. **First ceiling: about

100,000 items**, where the whole-feed-in-memory approach starts to matter more

than anything else in the pipeline. **Estimate.**

  • **Upgrade path past that ceiling:** stream the feed instead of building it

(the builder is one function and the `<item>` loop is the only part that

changes), or split by `google_product_category` into several data sources —

Merchant Center accepts multiple. Google's own documented ceiling for a feed

file is far higher (4 GB), so this is a Node ceiling, not a Google one.

  • **The crawler is the real constraint, and it is time, not memory.** At the

default concurrency of 4 and a 200 ms page fetch, that is ~20 pages/second:

**10,000 products in roughly 8–9 minutes**, 100,000 in about 1.4 hours.

**Estimate — your page latency is the whole variable.**

  • **Upgrade path for the crawl:** raise `concurrency` to whatever your own

origin tolerates (it is a parameter, and you are crawling yourself, so the

limit is your infrastructure and your WAF, not a third party's rate limit);

or stop crawling everything — crawl the items that changed since the last

run, plus a rotating sample. That is the change most catalogues over ~20,000

items should make on day one.

  • **Validation is pure CPU** over a JSON rule set. It is not the bottleneck at

any catalogue size a Node process can hold.

  • **Storage: none.** The pack persists nothing between runs, which is why there

is no database in the stack and why `verify.sh` needs no Docker.

  • **One feed per target country** is an architectural boundary, not a

limitation to route around: the conditional rules and the currency check are

country-scoped. Three countries is three configs through one pipeline.

**What this comfortably covers as shipped:** a single-country catalogue in the

hundreds-to-tens-of-thousands of SKUs, with the doctor run in CI on every

deploy and on a daily cron. **What it isn't sized for out of the box:**

six-figure catalogues crawled in full every night, or multi-channel feed

transformation across Google, Meta, Amazon and TikTok — the second one is what

the feed-management SaaS category exists for, and this pack does not pretend

otherwise.

**On token savings:** we are not publishing one. Every dollar figure in this

catalogue that we stand behind comes from a harness receipt, and this pack does

not have one yet. Buy it for the bug classes above — particularly the two that

are invisible to any feed-only validator — not for a build-cost estimate we

would have to invent.

What you get

Features:

  • `buildFeed()` — RSS 2.0 output with the exact `http://base.google.com/ns/1.0`

namespace, deterministic attribute order, full XML escaping, and absent

optionals omitted rather than emitted empty

  • `validateCatalog()` — conformance to the shipped product data spec:

required and conditional attributes, lengths, enums, price format, apparel

and variant rules, retired and unknown attribute names

  • `crawlCatalog()` — bounded-concurrency landing-page crawl comparing feed to

page: price amount, currency, effective sale price, availability, missing

markup, JavaScript-only markup, unreachable page

  • `productJsonLd()` — schema.org Product/Offer markup generated from the same

`Offer` as the feed, sale-window aware, server-rendered

  • `checkGtin()` — GS1 mod-10 across all four GTIN lengths, restricted and

coupon prefixes, ISBN-10 detection, all rules read from the spec file

  • `resolveIdentifiers()` — the gtin / mpn / brand / `identifier_exists`

decision, with the custom-product case and the misrepresentation case

separated

  • `diagnose()` + `formatReport()` — findings grouped under named disapproval

causes with source URLs, blocking vs. warning counts, and an explicit

`NOT CHECKED` section

  • `CatalogSource` seam — Shopify, your own database or a CSV export is one

class; a `JsonCatalogSource` ships

  • The product data spec as versioned **data** (`spec/gmc-product-spec.json`),

with `spec/REVISION.md` and `MIGRATION.md` covering how to refresh it

  • Full vitest acceptance suite plus a local fixture storefront with planted

price and stock drift — no Merchant Center account, no network, no Docker

  • One-page human go-live checklist covering the only manual steps

Screens/pages:

  • `GET /feed.xml` — the product feed, `application/rss+xml`
  • `/products/[id]` — reference landing page whose JSON-LD is rendered into the

server HTML from the same `Offer` as the feed

  • CLI: `npm run feed-doctor` — validate + crawl + diagnostic report; exits 1

when a blocking finding exists, so it works as a CI gate

  • CLI: `npm run feed-write` — writes `public/feed.xml`

How it works

  1. 01

    Buy the pack

    Instant download: PRD, architecture decisions, task graph, acceptance tests, scaffold, and per-phase prompts.

  2. 02

    Feed it to your agent

    Claude Code or Cursor builds inside the pinned scaffold — no context needed beyond the pack itself.

  3. 03

    Verify

    Run the pack's acceptance script. It checks the same things our verification harness checked.

nextjs-feed versions lock

node24.18.1
next16.2.12
react19.2.8
react-dom19.2.8
typescript6.0.3
cheerio1.2.0
vitest4.1.10
@types/node24.13.3
@types/react19.2.18
@types/react-dom19.2.4

Prescribed services

Disclosure: some links on this page are affiliate links. We may earn a commission if you sign up through them, at no extra cost to you. We only link to services the pack actually verified against.