We run a small Shopify store, and over the last week we added FAQPage structured data to every collection and guide that already had a visible FAQ. There was no theme rewrite and no app. Here's the approach, with the snags we hit along the way.
Why FAQ schema, and why only where the FAQ is visible
Google's guidelines say structured data should describe content the visitor can actually see. So the rule we followed was simple: schema mirrors the visible FAQ, word for word, or it doesn't go in. We never wrote hidden Q&A just for markup.
1. Get an Admin API token
We use a custom app with the client_credentials grant. Tokens expire after about 24 hours, so refresh one before each batch of edits:
curl -s -X POST "https://$SHOP.myshopify.com/admin/oauth/access_token" \
-d grant_type=client_credentials \
-d client_id=$CLIENT_ID -d client_secret=$CLIENT_SECRET
If you see Invalid API key or access token halfway through a run, the token has expired. That bit us more than once.
2. Back up before you touch anything
Pull the current descriptionHtml (collections) or body (articles) and save it to a dated JSON file first. Rolling back is then a single mutation.
query { collectionByHandle(handle: "beginner-paddles") { id descriptionHtml } }
3. Build the JSON-LD from the visible FAQ
Parse the question and answer pairs from the HTML you already have. Our FAQs use <h3> for questions followed by <p> answers. Then emit:
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [{
"@type": "Question",
"name": "What paddle weight suits a beginner?",
"acceptedAnswer": { "@type": "Answer", "text": "..." }
}]
}
Snag: our first regex was greedy and swallowed the intro paragraph as "question one". Print the extracted pairs and read them before you write anything. An explicit list beats a clever regex when you only have five questions.
4. Append it with a mutation
mutation($c: CollectionInput!) {
collectionUpdate(input: $c) { collection { id } userErrors { field message } }
}
For blog posts, use articleUpdate(id:, article: { body }). Append a <script type="application/ld+json"> block to the end of the existing HTML. Shopify keeps it intact in collection descriptions and article bodies.
5. Verify live, logged out
curl -s https://example.com/collections/beginner-paddles \
| grep -c '"@type":"FAQPage"'
Then run the URL through Google's Rich Results Test. Also check that each page has only one FAQPage block. Some themes already emit one, and duplicates are worth avoiding.
What we'd do differently
- Put FAQs in a metafield from the start, so markup and visible copy come from one source.
- Script the backup, the build, the mutation and the verify step as a single command per page.
We used this on Nooksy, our Australian pickleball gear store. It took about an afternoon across 11 pages.
Top comments (0)