Shopify seo metadata
Skill kgelster/awesome-ecom-skills/skills/shopify-seo-metadata
Shopify agency playbooks as Claude agent skills: catalog cleanup, SEO metadata, structured data, redirects, landing pages. Markdown-only. The playbook companion to Shopify's official AI toolkit.
npx -y skills add kgelster/awesome-ecom-skills --skill shopify-seo-metadataAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
Use when a store has missing SEO titles or meta descriptions and you want to backfill them: "backfill missing SEO titles/descriptions", "meta description sweep", "SEO meta backfill across products/collections/pages/blog posts", "missing page titles", "fix blank search-appearance metadata", "generate SEO meta for products". Safe-mode: fills only the empty search-appearance fields (SEO title, meta description) on products, collections, pages, and blog articles, never touching human-written values. Not for image alt text (use shopify-alt-text) or JSON-LD (use shopify-json-ld).
SKILL.md
9.3 KB, as published. Nobody here has run it
Shopify SEO Metadata
Safe-mode backfill of missing search-appearance metadata (the SEO title and meta description Google shows in the results snippet) across products, collections, pages, and blog articles. The core principle: fill blanks, never overwrite. A merchant's hand-written meta title is worth more than anything a model generates; this skill only touches fields that are empty.
Sister skill shopify-alt-text runs the same fill-missing backfill for
product-image alt text; shopify-json-ld handles structured data. To first
measure how much metadata is missing store-wide, shopify-catalog-audit is the
FIND stage that scopes this FIX.
Store access
Every skill that touches the Admin API opens with this stanza (minimum scopes for this skill below). Two lanes; pick one.
Lane A: custom-app token (scriptable). In Shopify admin: Settings → Apps and sales channels → Develop apps → create an app → grant the scopes below, then install and copy the Admin API access token. Export it; never write it to a committed file:
export SHOPIFY_STORE="your-store.myshopify.com"
export SHOPIFY_ACCESS_TOKEN="<your Admin API access token>" # env, not disk
curl -s "https://$SHOPIFY_STORE/admin/api/2025-07/graphql.json" \
-H "X-Shopify-Access-Token: $SHOPIFY_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"{ shop { name } }"}'
Lane B: Shopify CLI OAuth (no stored token). shopify store auth --store $SHOPIFY_STORE --scopes read_products,write_products,read_content,write_content
then shopify store execute to run a validated operation. Good for token-less
stores where the owner logs in interactively.
Minimum scopes for this skill: read_products + write_products cover
products and collections (both live on the products scope). Pages and blog
articles need read_content + write_content. Scope only what the run touches.
Toolkit preflight. Lane B rides on the Shopify CLI: run shopify version
first and install it if missing. For the full Admin GraphQL schema and
validated execution, pair this skill with Shopify's official AI toolkit
plugin. In Claude Code, check claude plugin list; if it isn't there:
claude plugin marketplace add Shopify/Shopify-AI-Toolkit
claude plugin install shopify-plugin@shopify-ai-toolkit
Recommended, not required: Lane A needs only curl and a token. The toolkit gives your agent the API; this skill gives it the playbook.
Recipes, not scripts
Every GraphQL block in references/queries-and-prompts.md is a copy-and-adapt starting point. Write throwaway code per engagement against the current schema, run it, discard it. Two rules keep a metadata backfill safe.
Safe mode is the whole job
State this out loud before any run, and treat it as non-negotiable:
- Fill missing ONLY. Never overwrite a value that already exists. For each
entity, read
seo { title description }(products/collections) or theglobal.title_tag/global.description_tagmetafields (pages/articles) FIRST. A field with a non-empty value is skipped, full stop. An "overwrite everything" mode is a different, deliberate job: it is never the default and a stranger running this skill does not get it for free. - Fill the two fields independently. A product can have a title but no description. Generate and write only the blank one; leave the populated one byte-for-byte untouched.
Preview first, then mutate
Never open a bulk write without a number in front of you.
- Run the read-only COUNT (per entity, in the reference) of how many items have a missing field. This is the exact count the mutation loop will act on, using the same skip logic.
- Sanity-check the number against expectation. A 40-product store reporting 3,000 items needing SEO means the skip logic is wrong or you are pointed at the wrong store. A count far above expectation means STOP and re-scope: do not proceed into a mutation run to "see what happens." An unscoped write over a catalog is not free to undo.
- Only after the count is sane do you generate and write.
Preflight the LLM key with a real completion
Before a bulk run, send the generation model a real 1-token completion, not
a models.list() / model-listing call. A key with read access to the model list
can still fail on an actual completion (billing lapsed, org quota, wrong
project). A dead key discovered mid-run burns the whole queue as failed rows and
wastes the crawl. One tiny completion up front is the cheapest insurance there
is.
Generate, write, verify: per entity
For each entity type the shape is identical; only the read query, the write mutation, and the content source field differ (all four are in the reference):
- Products: source = product title + body HTML. Write via
productUpdatewithinput.seo { title description }. - Collections: source = collection title + description. Write via
collectionUpdate, sameseoinput. - Pages: source = title +
bodySummary. SEO lives inglobal.title_tag(single line) andglobal.description_tag(multi line) metafields; write viametafieldsSet. - Blog articles: source = title +
summary(+ parent blog title for context). Sameglobal.*_tagmetafields, samemetafieldsSet.
The source data is the ceiling on output quality. Products generate from
body HTML: if a bulk description cleanup or rewrite just ran on this catalog
(e.g. via shopify-catalog-cleanup), confirm its post-edit re-scan passed
before you generate, or the products whose markup the transform mangled get
that garbage baked into fresh meta at scale. Enrichment does not fix bad
source data; it launders it into new fields.
Log every write (resource id, field, old value, new value, status) to a CSV or newline log as you go. That log is your evidence and your retry list.
Verify AFTER the write: via the Admin API, never the storefront
A mutation returning no userErrors is a claim, not proof. Confirm it:
- After the run, re-read
seo { title description }(or theglobal.*_tagmetafields) for 2–3 written resources throughadmin/api/2025-07/graphql.jsonand confirm the value matches what you wrote. - Read back through the Admin API, not the storefront. The storefront
<meta>tags are CDN-cached and lie about freshness: a stale edge page will show the old (or empty) tag long after the write landed, sending you chasing a non-bug. The Admin API is the source of truth.
Prompt guidance
Quality of the generated meta depends on three things the model can't infer:
- Feed the store's positioning into every prompt. A one-paragraph brand description (who they sell to, the voice, the category) is the difference between on-brand copy and generic filler. A thin or wrong description produces off-brand meta at scale: write a real one before a bulk run.
- Length targets. SEO title ~50–60 characters (Google truncates past ~60); meta description ~150–160. Models cannot count characters reliably, so validate length in code and re-prompt anything over the limit rather than trusting the model to obey.
- No keyword stuffing. Front-load the one primary keyword a buyer would actually search, write for a human reading a result, and stop. Repeated keywords and ALL-CAPS promo language read as spam and help nothing.
Full query/mutation/prompt detail: references/queries-and-prompts.md.
Provenance and maintenance
Last verified: 2026-07-05. GraphQL pinned to Admin API 2025-07; Shopify
deprecates versions on a rolling quarterly schedule, so verify the seo field
and the resource mutations against
shopify.dev before trusting a
version-specific claim. Read-only re-verification a stranger can run, confirming
the token reaches the store and the seo field resolves on products:
curl -s "https://$SHOPIFY_STORE/admin/api/2025-07/graphql.json" \
-H "X-Shopify-Access-Token: $SHOPIFY_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"{ products(first: 3) { nodes { title seo { title description } } } }"}'
Canonical docs: the seo field on resources and the metafield model at
Admin GraphQL. This skill captures
the operational safe-mode doctrine the reference doesn't; it is not a
replacement for it.