Product Feed SEO Optimizer
AGENT ROLE: Autonomous product-feed agent. Validate identifiers and attributes, score and rewrite titles against a demand-informed pattern, generate Product schema, join Merchant Center disapprovals, and emit the JSON in references/output.schema.json. One diff that improves organic and Shopping together.
OBJECTIVE
For every product in a feed, return: identifier validity (real GTIN check digit), attribute completeness, a title score with a rewritten demand-informed title (diff), recommended Product JSON-LD, and any Merchant Center disapproval — so the feed ranks in Shopping and the landing pages win rich results.
INPUTS
feed(REQUIRED via--feed): a Google Merchant product feed as TSV/CSV (id, title, description, link, image_link, availability, price, brand, gtin, mpn, condition, color, size, ...) or JSON array of product objects. Format is auto-detected.volumes(OPTIONAL via--volumes): JSON map of term -> monthly volume; drives demand-informed title front-loading.title_pattern(OPTIONAL, default"{brand} {title} {color} {size}"): tokens{brand} {title} {color} {size} {material} {gender} {category}.title_max(OPTIONAL, default 150): Merchant title limit;title_optimal(OPTIONAL, default 70): organic visible length.merchant_id(OPTIONAL via--merchant-id): when set, disapprovals are pulled from the Merchant API (products v1).max_products(OPTIONAL, default 100000) andmax_status_pages(OPTIONAL, default 40): cost guards.
AUTHENTICATION (Merchant API)
- The feed file is local and needs no credentials.
- IF
--merchant-idis passed THEN REQUIRE envMERCHANT_API_ACCESS_TOKEN(OAuth bearer,contentscope; an existingCONTENT_API_ACCESS_TOKENis accepted too). IF unset THEN STOPerror.code="AUTH_MISSING_MERCHANT_API". IF the token is rejected THEN STOPerror.code="AUTH_MERCHANT_API_FORBIDDEN". Callsaccounts.products.listin the Merchant API products v1. The Content API for Shopping it replaces was sunset on 2026-08-18.
EXPECTED TOOL CALLS
- Run
scripts/product_feed_optimizer.py --feed feed.tsv [--volumes volumes.json] [--merchant-id 1234567] [--title-pattern "{brand} {title} {color} {size}"]. - Zero network calls in the offline path; paginated
accounts.products.list(1,000/page) only when--merchant-idis set.
PROCEDURE (deterministic, per product)
STEP 1 — IDENTIFIER: strip non-digits from gtin; validate length in {8,12,13,14} and the GS1 mod-10 check digit. Status = gtin_valid / invalid_gtin / brand_mpn / identifier_exempt (when identifier_exists=false) / missing_identifier.
STEP 2 — ATTRIBUTES: check the required Merchant set (id, title, description, link, image_link, availability, price, condition, gtin-or-mpn); add color/size/gender/age_group for apparel categories; compute coverage and the missing list.
STEP 3 — TITLE AUDIT: penalize over-limit length, over-optimal length (info), promotional ALL-CAPS, promotional phrases, brand not front-loaded, and keyword stuffing; produce a 0-100 title score.
STEP 4 — REWRITE: compose a title from title_pattern; IF volumes given THEN front-load the highest-volume phrase the product actually matches; truncate at a word boundary to title_max. Emit old vs new and title_changed.
STEP 5 — SCHEMA: generate recommended Product JSON-LD (name, brand, sku, mpn, valid gtin, image, description, offers{price, priceCurrency, availability, itemCondition, url}).
STEP 6 — DISAPPROVALS: IF --merchant-id THEN map each product (by offer id) to its item-level issues with severity DISAPPROVED from the Merchant API.
STEP 7 — EMIT products sorted worst-coverage-first with feed-level rollups.
RATE LIMITS & ERROR HANDLING
- Merchant API
429-> backoff2^attempt(max 5) then STOPRATE_LIMITED;5xxretry <=3 then STOPREQUEST_FAILED; pages are fetched sequentially with pacing (concurrency 1) and capped at--max-status-pages. 401/403from the Merchant API STOPAUTH_MERCHANT_API_FORBIDDEN.- An empty or unparseable feed STOPs
error.code="EMPTY_FEED".
MISSING / INSUFFICIENT DATA
- WITHOUT
--volumestitles are rewritten structurally only (demand_informed=false,demand_lead_keyword=null); no volume is invented and no keyword is front-loaded on a guess. - WITHOUT
--merchant-iddisapprovals are not fetched (disapprovals_source="none"); the audit never claims a product is approved or disapproved. schema_status="recommended"reflects that JSON-LD is generated from feed data; the skill does not assert the markup is live on the page unless that is separately verified.- A GTIN that is absent is
missing_identifier(orbrand_mpn/identifier_exempt), never silently treated as valid; a present-but-wrong GTIN isinvalid_gtin.
OUTPUT
One JSON object per references/output.schema.json.
FILES
scripts/product_feed_optimizer.py— feed parsing, GS1 GTIN validation, attribute + title audit, demand-informed title rewrite, Product JSON-LD generation, Merchant API disapproval join.references/output.schema.json— output contract.
