@seoagent-official/seoagent 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,155 @@
1
+ # Programmatic SEO Protocol
2
+
3
+ Programmatic SEO is template-driven scale: one template + a dataset → hundreds or thousands of pages targeting long-tail keyword variations. Done well, it captures real search demand at scale. Done badly, it's thin-content spam that gets penalized.
4
+
5
+ ## Core Rules (apply to every programmatic project)
6
+
7
+ 1. **Every page must provide unique value.** Just swapping `{city}` in a template is thin content. Each page needs at least one piece of data that *only* this page can show.
8
+ 2. **Proprietary data > public data.** "Best restaurants in {city}" using public Google data → already done. "Average response time of plumbers in {city}, based on our 50,000-job dataset" → unique.
9
+ 3. **Internal linking prevents orphaning.** Every programmatic page must link to at least 3 other programmatic pages in the same set + 1 hub page.
10
+ 4. **Match real search intent.** Run WebSearch on 5-10 sample queries before building. If the SERP is dominated by aggregators or directories, you may have a shot. If it's dominated by Wikipedia or government sites, skip.
11
+ 5. **Quality > quantity.** 200 great pages beat 5,000 thin ones. Google's site-quality signals are aggregated — thin programmatic pages drag down your whole domain.
12
+
13
+ ## The 12 Programmatic Playbooks
14
+
15
+ ### 1. Comparison Pages
16
+ Pattern: `/{tool-a}-vs-{tool-b}` (e.g. `/notion-vs-airtable`)
17
+ Data needed: features matrix, pricing, use cases, reviews per tool
18
+ Search intent: high-intent, decision stage
19
+ Word count: 1500-2500
20
+ Schema: `Product` per tool + `ItemList` of comparison points
21
+ Example: G2's comparison pages, Capterra, Webflow's "Webflow vs X" pages
22
+
23
+ ### 2. Alternative Pages
24
+ Pattern: `/{tool}-alternatives` (e.g. `/zapier-alternatives`)
25
+ Data needed: list of 5-15 alternatives with key differentiators per
26
+ Search intent: competitor research
27
+ Word count: 2000-3000
28
+ Schema: `ItemList` with each alternative as a `Product`
29
+ Critical: don't just list competitors; explain WHY someone should choose each one
30
+
31
+ ### 3. Use Case Pages
32
+ Pattern: `/{tool}-for-{use-case}` (e.g. `/airtable-for-project-management`)
33
+ Data needed: tool capabilities, use-case-specific workflow, sample template
34
+ Search intent: evaluating fit
35
+ Word count: 1200-1800
36
+ Best when: you have a real example or template to share
37
+
38
+ ### 4. Integration Pages
39
+ Pattern: `/integrations/{tool}` or `/{your-tool}-{their-tool}-integration`
40
+ Data needed: setup steps, common workflows, example automations
41
+ Search intent: people Google "{your tool} {other tool}" to verify integration exists
42
+ Word count: 800-1200
43
+ Schema: `HowTo` for the setup walkthrough
44
+ Each integration needs: setup steps, common workflows, an FAQ on quirks
45
+
46
+ ### 5. Location Pages
47
+ Pattern: `/{service}-{city}` or `/{tool}/cities/{city}`
48
+ Data needed: location-specific stats, local examples, location-relevant pricing
49
+ Search intent: local-intent searches ("plumber chicago")
50
+ Word count: 600-1000
51
+ Critical: needs Local Business schema with address, geo coordinates, hours
52
+ Risk: highest spam risk — make sure each page genuinely has unique local content
53
+
54
+ ### 6. Industry / Persona Pages
55
+ Pattern: `/for/{industry}` or `/{tool}-for-{persona}`
56
+ Data needed: industry-specific use cases, customers in that vertical, ROI metrics specific to that vertical
57
+ Search intent: SaaS evaluation by industry
58
+ Word count: 1000-1500
59
+
60
+ ### 7. Glossary Entries
61
+ Pattern: `/glossary/{term}` or `/{topic}/{term}`
62
+ Data needed: clear definition, examples, related terms
63
+ Search intent: informational, AI-search-friendly
64
+ Word count: 400-800 (shorter than long_tails)
65
+ Schema: `DefinedTerm` + `Article`
66
+ Build at scale once: 50-100 terms in your domain's vocabulary
67
+
68
+ ### 8. Listing / Directory Pages
69
+ Pattern: `/{category}/{location-or-attribute}` (e.g. `/best-{tool}-for-{use-case}`)
70
+ Data needed: curated list of items with structured data per item
71
+ Search intent: "best X" searches
72
+ Word count: 1500-3000 (mostly structured)
73
+ Schema: `ItemList`
74
+
75
+ ### 9. Calculator / Tool Pages
76
+ Pattern: `/{topic}-calculator` or `/{tool}-calculator`
77
+ Data needed: a working calculator + explanatory content
78
+ Search intent: high — the calculator IS the value
79
+ Word count: 600-1000 of explainer copy alongside the tool
80
+ Best programmatic format: the tool itself is unique value per page
81
+
82
+ ### 10. Template / Example Pages
83
+ Pattern: `/templates/{template-slug}`
84
+ Data needed: a downloadable or copyable template + use case explanation
85
+ Search intent: people Googling "{x} template"
86
+ Word count: 500-1000
87
+ Schema: `CreativeWork` or specific subtype
88
+
89
+ ### 11. Profile / Listing-of-Things Pages
90
+ Pattern: `/companies/{company}` or `/people/{person}` or `/products/{product}`
91
+ Data needed: structured profile with proprietary data
92
+ Search intent: branded searches for individual entities
93
+ Word count: 800-1500 with structured data carrying weight
94
+ Schema: `Organization`, `Person`, or `Product` as appropriate
95
+
96
+ ### 12. Translated Pages
97
+ Pattern: `/{lang}/{slug}` or subdomain
98
+ Data needed: properly translated content (not machine-translated boilerplate)
99
+ Search intent: language-specific search demand
100
+ Critical: machine translation is detected by Google. Use professional translation or skip.
101
+ Schema: hreflang tags + canonical
102
+
103
+ ## Choosing a Playbook
104
+
105
+ Match the playbook to your assets:
106
+ - **Have unique data?** → Listing pages (#8), Profile pages (#11), Comparison pages (#1)
107
+ - **Have a tool?** → Calculator pages (#9), Integration pages (#4)
108
+ - **Have customers in many segments?** → Industry pages (#6), Use case pages (#3)
109
+ - **Have many integrations?** → Integration pages (#4)
110
+ - **Have geographic relevance?** → Location pages (#5) (with caution)
111
+
112
+ You can combine playbooks: comparison pages × industry → `/{tool-a}-vs-{tool-b}-for-{industry}`. Be selective — combinatorial explosions create thin content fast.
113
+
114
+ ## Implementation Framework
115
+
116
+ 1. **Keyword Pattern Research**
117
+ - Run WebSearch on 5-10 sample queries that fit the pattern
118
+ - Identify search volume signals (autocomplete, "people also ask")
119
+ - Confirm the SERP isn't dominated by Wikipedia or government sites
120
+ 2. **Data Source**
121
+ - Identify your data source. Is it proprietary? Public-but-aggregated? User-generated?
122
+ - Each page needs at least one *unique* data point
123
+ 3. **Template Design**
124
+ - Mock up the template with a real example. Is it 600+ words of actual content per page?
125
+ - Design the URL pattern. Flat is better than nested.
126
+ 4. **Internal Linking Strategy**
127
+ - Plan the hub page (a category index)
128
+ - Plan how programmatic pages link to each other (related, alternatives)
129
+ 5. **Build a Pilot Set**
130
+ - Build 10-20 pages first. Wait 4-8 weeks. Check rankings.
131
+ - If those rank, scale up. If they don't, diagnose before building 1,000.
132
+ 6. **Sitemap & Indexation**
133
+ - Submit programmatic pages in a separate sitemap so you can monitor indexation rate
134
+ - If <50% are indexed after 8 weeks, you have a quality problem — Google is rejecting them
135
+ 7. **Monitor and Prune**
136
+ - Pages ranking on positions 50+ after 6 months are dragging down site quality
137
+ - Either improve them or `noindex` them
138
+
139
+ ## When NOT to Build Programmatic
140
+
141
+ - The SERP is owned by Wikipedia, government, or huge brands → skip
142
+ - You don't have unique data → skip (or get unique data first)
143
+ - Your dataset has gaps that produce empty/thin pages → tighten the dataset first
144
+ - Your domain is new (< 6 months old) → focus on traditional content first
145
+
146
+ ## After Building
147
+
148
+ 1. Add a hub page that lists all programmatic pages with internal linking
149
+ 2. Submit a dedicated sitemap to Google Search Console
150
+ 3. Track indexation rate weekly for the first 8 weeks
151
+ 4. Persist the pattern to `.seoagent/strategy/clusters/programmatic-{pattern}.md` so the agent knows about it next session
152
+
153
+ ## Persistence
154
+
155
+ Programmatic pages get saved to `.seoagent/content/programmatic/{slug}.md` with `page_type: programmatic`. The cluster file in `strategy/clusters/` tracks the pattern (template) and the URL list.
@@ -0,0 +1,163 @@
1
+ # Rewrite & Refresh Protocol (Phase 4b)
2
+
3
+ When to rewrite an existing article instead of writing a new one. The single most-asked feature in SEO content workflows: "update my existing post."
4
+
5
+ ## When to Rewrite
6
+
7
+ Strong signals an article needs a refresh:
8
+
9
+ 1. **Stats / facts > 18 months old** — readers and search engines penalize stale data
10
+ 2. **Ranking dropped** — used to be on page 1, now on page 3+
11
+ 3. **Search intent shifted** — Google's SERP for the keyword now shows different content types
12
+ 4. **Competitor content is now stronger** — newer competitors cover what you missed
13
+ 5. **Cluster expanded** — new sub_pillars exist that the pillar should reference
14
+ 6. **Tone drift** — the article doesn't match the current `context.md` brand voice
15
+ 7. **User explicitly asks** — "rewrite this", "update this post", "refresh the homepage"
16
+
17
+ If none of these are true: don't rewrite. Refreshing for the sake of it can hurt rankings (Google sees disruptive changes to ranking pages).
18
+
19
+ ## Procedure
20
+
21
+ ### Step 1: Identify the Target
22
+
23
+ If the user gives a slug, file path, or URL:
24
+ - Read `.seoagent/content/{slug}.md` if it's a SEOAgent-generated article
25
+ - WebFetch the live URL if it's not in `.seoagent/`
26
+ - If neither — ask the user where the source of truth is
27
+
28
+ ### Step 2: Read Context
29
+
30
+ Before any edits, read:
31
+ - `.seoagent/context.md` — current brand voice, banned topics, audience
32
+ - `.seoagent/strategy/clusters/{cluster}.md` — the article's role and link graph
33
+ - `.seoagent/briefs/{slug}.md` if it exists — the original brief
34
+
35
+ ### Step 3: Diagnose the Gaps
36
+
37
+ Run a structured diagnosis. Use this exact template — output it to the user before editing:
38
+
39
+ ```markdown
40
+ ## 🔍 Refresh Diagnosis — {slug}
41
+
42
+ ### Stale Content
43
+ - {stat or fact} — currently says "X (2024 data)", should be updated
44
+ - {section} — references discontinued tool / outdated framework
45
+
46
+ ### Missing Coverage
47
+ - New sub_pillar exists in cluster: {sub_pillar} — pillar should link to it
48
+ - Top-3 competitor now covers {topic}; we don't
49
+
50
+ ### Structural Issues
51
+ - {observation about hierarchy, AI extractability, etc.}
52
+
53
+ ### Tone & Voice
54
+ - {observation if context.md has shifted since article was written}
55
+
56
+ ### What's Working (Preserve)
57
+ - {section} ranks well; keep largely intact
58
+ - {section} has good backlinks per current analysis
59
+
60
+ ## Plan (Y/N)
61
+ 1. Update {N} stats with 2026 figures
62
+ 2. Add new H2: "{section title}" linking to {sub_pillar}
63
+ 3. Tighten {section} — currently {old word count}, target {new word count}
64
+ 4. Refresh hero image alt text + add OG card
65
+ ```
66
+
67
+ Wait for user confirmation before executing.
68
+
69
+ ### Step 4: Execute the Rewrite
70
+
71
+ Rules:
72
+ - **Preserve the URL slug.** Never change `slug` — even if the title changes, the URL stays.
73
+ - **Preserve sections that rank.** If a section is the page's strongest signal, keep its core wording.
74
+ - **Use `Edit`, not `Write`.** Edit one section at a time so changes are reviewable.
75
+ - **Update `dateModified`** in JSON-LD. Don't change `datePublished` — that resets ranking signal.
76
+ - **Bump `version`** in frontmatter (1 → 2 → 3).
77
+
78
+ ### Step 5: Update the Frontmatter
79
+
80
+ ```yaml
81
+ ---
82
+ slug: tech-seo-guide
83
+ page_type: pillar
84
+ title: "The Complete Technical SEO Guide for 2026" # year may update
85
+ status: drafted
86
+ created_at: 2024-01-15T10:00:00Z # NEVER change
87
+ updated_at: 2026-04-27T10:00:00Z # update this
88
+ version: 3 # bump
89
+ word_count: 3450 # update if changed
90
+ ---
91
+ ```
92
+
93
+ Add to JSON-LD:
94
+ ```json
95
+ {
96
+ "@type": "Article",
97
+ "datePublished": "2024-01-15",
98
+ "dateModified": "2026-04-27"
99
+ }
100
+ ```
101
+
102
+ Both dates in the schema. Google uses `dateModified` to know freshness without resetting ranking signal.
103
+
104
+ ### Step 6: Update Internal Links
105
+
106
+ If the rewrite added or changed internal links:
107
+ - Update the cluster file's "Internal Linking" section
108
+ - If you added a link DOWN to a sub_pillar, also add the reverse link UP from the sub_pillar (use `Edit`)
109
+
110
+ ### Step 7: Log and Sync
111
+
112
+ Append to `.seoagent/changelog.md`:
113
+ ```
114
+ [2026-04-27] Rewrote tech-seo-guide v3: updated 7 stats, added "AI Search Readiness" H2 linking to ai-search-readiness sub_pillar, tightened from 3120 → 3450 words
115
+ ```
116
+
117
+ Run `seoagent sync`.
118
+
119
+ ## Special Cases
120
+
121
+ ### Rewriting a Landing Page
122
+
123
+ Same protocol but:
124
+ - Track conversion impact — note in changelog if there's a CRO concern
125
+ - Don't ship the rewrite during a campaign in flight — coordinate with the user
126
+ - Update OG card and Twitter card alt text (often forgotten)
127
+ - Update JSON-LD `Product` / `Offer` if pricing or feature claims changed
128
+
129
+ ### Rewriting Without an Existing Brief
130
+
131
+ If the article exists but `.seoagent/briefs/{slug}.md` does not:
132
+ 1. Generate a brief from the live article first (Phase 3 protocol)
133
+ 2. Show it to the user, confirm it represents the *intended* article
134
+ 3. Then rewrite against that brief
135
+
136
+ This catches situations where the article drifted from any original spec.
137
+
138
+ ### "Annual Refresh" — Updating Year References
139
+
140
+ If the user says "update for 2026" and only the year needs to change:
141
+ 1. Find every `2024`, `2025` reference in the article
142
+ 2. Update only the ones that genuinely refer to current-year data
143
+ 3. Update `dateModified` in JSON-LD
144
+ 4. Don't bump major version — this is a minor refresh
145
+
146
+ Output a brief diff summary so the user can confirm nothing else changed.
147
+
148
+ ### Rewriting AI-Generated Content That Wasn't Yours
149
+
150
+ If the user wants to rewrite a post that wasn't drafted with SEOAgent (no brief, no `.seoagent/content/{slug}.md`):
151
+ 1. WebFetch the live URL
152
+ 2. Save a copy to `.seoagent/content/{slug}.md` with `imported_at` in frontmatter
153
+ 3. Generate a brief representing the *current* article
154
+ 4. Then run the diagnosis (Step 3) and rewrite
155
+
156
+ This brings the article into SEOAgent's persistence model so future refreshes have history.
157
+
158
+ ## Common Pitfalls
159
+
160
+ - **Changing the URL.** Breaks backlinks, breaks rankings. Use a 301 redirect only if absolutely necessary, never silently.
161
+ - **Resetting `datePublished`.** Tells Google "this is a new article" — kills accumulated ranking signal.
162
+ - **Rewriting the entire article.** A 90% rewrite is a new article. If you're doing that, change the slug too — but accept the ranking reset.
163
+ - **Forgetting the link graph.** A pillar rewrite without updating sub_pillar links creates broken hub-and-spoke structure.
@@ -0,0 +1,297 @@
1
+ # Schema Markup Library
2
+
3
+ JSON-LD is the recommended format. Embed in `<script type="application/ld+json">` tags in `<head>`. The article frontmatter `json_ld` array becomes one or more `<script>` tags at render time.
4
+
5
+ ## Recommended Schema by Page Type
6
+
7
+ | Page type | Required | Add when relevant |
8
+ |---|---|---|
9
+ | Pillar article | `Article` | `FAQPage`, `HowTo` |
10
+ | Sub-pillar article | `Article` | `FAQPage`, `HowTo` |
11
+ | Long-tail article | `Article` | `HowTo` |
12
+ | Landing page (homepage / about) | `Organization` | `WebSite`, `BreadcrumbList` |
13
+ | Landing page (pricing / product) | `Product` + `Offer` | `AggregateRating`, `Review` |
14
+ | Landing page (SaaS feature) | `SoftwareApplication` | `Offer` |
15
+ | FAQ page | `FAQPage` | — |
16
+ | Glossary entry | `Article` + `DefinedTerm` | — |
17
+ | Programmatic listing | `ItemList` | `Product`, `Organization`, `Place` per item |
18
+ | Local business / location | `LocalBusiness` (or specific subtype) | `OpeningHoursSpecification`, `GeoCoordinates` |
19
+ | Recipe (rare for SaaS — included for completeness) | `Recipe` | — |
20
+
21
+ ## Article Schema Templates
22
+
23
+ ### Article (default for blog content)
24
+
25
+ ```json
26
+ {
27
+ "@context": "https://schema.org",
28
+ "@type": "Article",
29
+ "headline": "The Complete Technical SEO Guide for 2026",
30
+ "author": {
31
+ "@type": "Person",
32
+ "name": "Jane Doe",
33
+ "url": "https://example.com/authors/jane"
34
+ },
35
+ "datePublished": "2026-04-27",
36
+ "dateModified": "2026-04-27",
37
+ "image": "https://example.com/blog/technical-seo-guide/hero.png",
38
+ "publisher": {
39
+ "@type": "Organization",
40
+ "name": "Acme",
41
+ "logo": {
42
+ "@type": "ImageObject",
43
+ "url": "https://example.com/logo.png"
44
+ }
45
+ },
46
+ "mainEntityOfPage": "https://example.com/blog/technical-seo-guide"
47
+ }
48
+ ```
49
+
50
+ Required: `headline`, `author`, `datePublished`, `dateModified`, `image`, `publisher.logo`. Missing any of these and Google may reject the rich result.
51
+
52
+ ### FAQPage (add to articles with FAQ sections)
53
+
54
+ ```json
55
+ {
56
+ "@context": "https://schema.org",
57
+ "@type": "FAQPage",
58
+ "mainEntity": [
59
+ {
60
+ "@type": "Question",
61
+ "name": "What is technical SEO?",
62
+ "acceptedAnswer": {
63
+ "@type": "Answer",
64
+ "text": "Technical SEO is the practice of optimizing a website's infrastructure so search engines can crawl, render, and index it efficiently. It covers crawlability, indexation, site speed, schema markup, and AI search readiness."
65
+ }
66
+ },
67
+ {
68
+ "@type": "Question",
69
+ "name": "How is technical SEO different from on-page SEO?",
70
+ "acceptedAnswer": {
71
+ "@type": "Answer",
72
+ "text": "Technical SEO covers infrastructure (crawl, render, index, speed). On-page SEO covers content and HTML structure (titles, meta, headings, keywords, internal links). Both matter; they target different layers."
73
+ }
74
+ }
75
+ ]
76
+ }
77
+ ```
78
+
79
+ The `Answer.text` should be the actual paragraph from the article — Google flags mismatches.
80
+
81
+ ### HowTo (add to step-by-step content)
82
+
83
+ ```json
84
+ {
85
+ "@context": "https://schema.org",
86
+ "@type": "HowTo",
87
+ "name": "How to Submit a Sitemap to Google Search Console",
88
+ "description": "Step-by-step guide to submitting your sitemap.xml to Google Search Console.",
89
+ "totalTime": "PT5M",
90
+ "step": [
91
+ {
92
+ "@type": "HowToStep",
93
+ "name": "Open Google Search Console",
94
+ "text": "Sign in to Google Search Console at search.google.com/search-console.",
95
+ "url": "https://example.com/blog/submit-sitemap-gsc#step-1"
96
+ },
97
+ {
98
+ "@type": "HowToStep",
99
+ "name": "Navigate to Sitemaps",
100
+ "text": "In the left sidebar, click 'Sitemaps' under the 'Indexing' section.",
101
+ "url": "https://example.com/blog/submit-sitemap-gsc#step-2"
102
+ }
103
+ ]
104
+ }
105
+ ```
106
+
107
+ Each step's `url` should be a fragment link to that step's heading on the article page. Use anchor IDs.
108
+
109
+ ## Landing Page Schema Templates
110
+
111
+ ### Organization (homepage)
112
+
113
+ ```json
114
+ {
115
+ "@context": "https://schema.org",
116
+ "@type": "Organization",
117
+ "name": "Acme",
118
+ "url": "https://acme.com",
119
+ "logo": "https://acme.com/logo.png",
120
+ "description": "Acme builds invoicing software for freelancers and small businesses.",
121
+ "foundingDate": "2024",
122
+ "sameAs": [
123
+ "https://twitter.com/acme",
124
+ "https://linkedin.com/company/acme",
125
+ "https://github.com/acme"
126
+ ],
127
+ "contactPoint": {
128
+ "@type": "ContactPoint",
129
+ "contactType": "customer support",
130
+ "email": "support@acme.com"
131
+ }
132
+ }
133
+ ```
134
+
135
+ ### WebSite (homepage — for sitelinks search box)
136
+
137
+ ```json
138
+ {
139
+ "@context": "https://schema.org",
140
+ "@type": "WebSite",
141
+ "url": "https://acme.com",
142
+ "potentialAction": {
143
+ "@type": "SearchAction",
144
+ "target": "https://acme.com/search?q={search_term_string}",
145
+ "query-input": "required name=search_term_string"
146
+ }
147
+ }
148
+ ```
149
+
150
+ ### Product (pricing / product page)
151
+
152
+ ```json
153
+ {
154
+ "@context": "https://schema.org",
155
+ "@type": "Product",
156
+ "name": "Acme Pro",
157
+ "description": "...",
158
+ "brand": { "@type": "Brand", "name": "Acme" },
159
+ "offers": [
160
+ {
161
+ "@type": "Offer",
162
+ "name": "Starter",
163
+ "price": "29",
164
+ "priceCurrency": "USD",
165
+ "availability": "https://schema.org/InStock",
166
+ "url": "https://acme.com/pricing#starter"
167
+ },
168
+ {
169
+ "@type": "Offer",
170
+ "name": "Pro",
171
+ "price": "79",
172
+ "priceCurrency": "USD",
173
+ "availability": "https://schema.org/InStock",
174
+ "url": "https://acme.com/pricing#pro"
175
+ }
176
+ ],
177
+ "aggregateRating": {
178
+ "@type": "AggregateRating",
179
+ "ratingValue": "4.8",
180
+ "reviewCount": "127"
181
+ }
182
+ }
183
+ ```
184
+
185
+ Only include `aggregateRating` if you have real review data — Google will manually penalize fake ratings.
186
+
187
+ ### SoftwareApplication (SaaS feature page)
188
+
189
+ ```json
190
+ {
191
+ "@context": "https://schema.org",
192
+ "@type": "SoftwareApplication",
193
+ "name": "Acme",
194
+ "applicationCategory": "BusinessApplication",
195
+ "operatingSystem": "Web",
196
+ "offers": {
197
+ "@type": "Offer",
198
+ "price": "29",
199
+ "priceCurrency": "USD"
200
+ }
201
+ }
202
+ ```
203
+
204
+ ### LocalBusiness (location page)
205
+
206
+ ```json
207
+ {
208
+ "@context": "https://schema.org",
209
+ "@type": "LocalBusiness",
210
+ "name": "Acme Plumbing — Chicago",
211
+ "image": "https://acme.com/locations/chicago.jpg",
212
+ "address": {
213
+ "@type": "PostalAddress",
214
+ "streetAddress": "123 Main St",
215
+ "addressLocality": "Chicago",
216
+ "addressRegion": "IL",
217
+ "postalCode": "60601",
218
+ "addressCountry": "US"
219
+ },
220
+ "geo": {
221
+ "@type": "GeoCoordinates",
222
+ "latitude": "41.8781",
223
+ "longitude": "-87.6298"
224
+ },
225
+ "telephone": "+1-555-0100",
226
+ "openingHoursSpecification": [
227
+ {
228
+ "@type": "OpeningHoursSpecification",
229
+ "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
230
+ "opens": "08:00",
231
+ "closes": "18:00"
232
+ }
233
+ ]
234
+ }
235
+ ```
236
+
237
+ Specific subtypes (`Restaurant`, `Plumber`, `Dentist`, etc.) win over generic `LocalBusiness` if your business fits one. See https://schema.org/LocalBusiness for the list.
238
+
239
+ ## Cross-Cutting Schemas
240
+
241
+ ### BreadcrumbList (every non-homepage page)
242
+
243
+ ```json
244
+ {
245
+ "@context": "https://schema.org",
246
+ "@type": "BreadcrumbList",
247
+ "itemListElement": [
248
+ { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://acme.com" },
249
+ { "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://acme.com/blog" },
250
+ { "@type": "ListItem", "position": 3, "name": "Technical SEO Guide", "item": "https://acme.com/blog/technical-seo-guide" }
251
+ ]
252
+ }
253
+ ```
254
+
255
+ ### DefinedTerm (glossary entries)
256
+
257
+ ```json
258
+ {
259
+ "@context": "https://schema.org",
260
+ "@type": "DefinedTerm",
261
+ "name": "Crawl Budget",
262
+ "description": "The number of pages a search engine crawler will fetch from a site within a given timeframe.",
263
+ "inDefinedTermSet": {
264
+ "@type": "DefinedTermSet",
265
+ "name": "Acme SEO Glossary"
266
+ },
267
+ "url": "https://acme.com/glossary/crawl-budget"
268
+ }
269
+ ```
270
+
271
+ ## Validation
272
+
273
+ Always tell users to test before deploying:
274
+ - **Rich Results Test**: https://search.google.com/test/rich-results — checks if Google can parse the markup and which rich-result types it qualifies for
275
+ - **Schema Validator**: https://validator.schema.org — checks pure schema.org compliance
276
+ - Both tools accept either a URL or pasted code
277
+
278
+ Common validation errors:
279
+ - Missing required fields (`Article` requires `image`, `Product` requires `offers` with `price`)
280
+ - Invalid date formats (use ISO 8601: `2026-04-27` or `2026-04-27T10:00:00Z`)
281
+ - Wrong type for a field (`price` must be a string, not a number, in JSON-LD)
282
+ - Mismatched content (FAQ schema's `Answer.text` doesn't match the visible page text)
283
+
284
+ ## When Multiple Schema Types Apply
285
+
286
+ A pillar article with FAQs and step-by-step instructions can have all three:
287
+ ```yaml
288
+ json_ld:
289
+ - "@type": Article
290
+ ...
291
+ - "@type": FAQPage
292
+ ...
293
+ - "@type": HowTo
294
+ ...
295
+ ```
296
+
297
+ Each renders as a separate `<script type="application/ld+json">` tag. Multiple types on one page is supported and recommended.