---
title: "Structured Data in WordPress: Article, FAQPage, Speakable and Person"
url: https://hostmy.blog/structured-data-wordpress/
date: 2026-09-14
modified: 2026-09-03
lang: en
author: "Aditya Sharma"
description: "What each of the four AI-era schema types is actually for, the markup for each, and the one that you should deliberately keep small."
categories:
  - "RankReady"
image: https://hostmy.blog/wp-content/uploads/2026/09/hmb-card-1863-1024x538.jpg
word_count: 1635
---

# Structured Data in WordPress: Article, FAQPage, Speakable and Person

Four schema types carry most of the weight for machine readers on a WordPress blog. `Article`, `FAQPage`, `speakable` and `Person`. Each answers a different question, and adding one when you needed another is the usual reason a site has plenty of structured data and no benefit from it.

The questions they answer, in order: what is this page, what does it answer, which sentences are quotable, and who is responsible for it. Markup is one of several separate jobs in [translating AI search advice into WordPress work](/ai-search-optimization-wordpress/), and it is the one with the clearest right answer.

Everything below is the markup plus the reason. If you have not yet checked what your SEO plugin already writes, do that first, because duplicating an `Article` node is worse than having none. The inspection script is in [adding schema without duplicating Yoast](/schema-plugin-wordpress/).

## Four types, four different jobs

| Type | Answers | Usually already written by | Keep it small |
| ---- | ------- | -------------------------- | ------------- |
| `Article` | What is this page and when did it change | Your SEO plugin | Not applicable |
| `FAQPage` | What specific questions does it answer | Nobody, unless you added it | Yes, 4 to 6 entries |
| `speakable` | Which sentences are clean enough to quote | Almost nobody | Yes, 2 to 3 selectors |
| `Person` | Who wrote this and can that be verified | Partially, and usually thin | No, richer is better |

## Article tells a machine what the page is and when it changed

`Article` (or `BlogPosting`, which is a subtype and equally fine) is the base node. Your SEO plugin almost certainly writes it already, so your job is to check its fields rather than write a second one, which is also the short answer to [whether a Yoast site needs a second schema plugin](/yoast-alternative/).

`{
"@context": "https://schema.org",
"@type": "BlogPosting",
"@id": "https://yoursite.com/your-post/#article",
"headline": "Adding schema in WordPress without duplicating Yoast",
"datePublished": "2025-11-04T09:12:00+05:30",
"dateModified": "2026-08-19T16:40:00+05:30",
"author": { "@id": "https://yoursite.com/#/schema/person/author" },
"mainEntityOfPage": "https://yoursite.com/your-post/"
}`

The field that gets neglected is `dateModified`. It is the only machine readable statement your page makes about whether it is maintained.

That matters because stale pages are roughly three times more likely to lose citations they already held. The median cited page is 298 days old, so age itself is not the problem. Neglect is. A `dateModified` that has not moved in three years says exactly that, in a format a parser reads without ambiguity.

Check yours:

`curl -s https://yoursite.com/your-post/ | grep -o '"date\(Published\|Modified\)":"[^"]*"'`

Two identical timestamps on an old post means either nothing has been updated or your theme is not passing the modified date through. Both are worth fixing, and they have different fixes.

## FAQPage turns questions into data, and costs a heading each

`FAQPage` attaches question and answer pairs to a page as structured data rather than leaving them as prose a parser has to infer.

Google removed FAQPage rich results from Search entirely in 2026, after restricting them to a small set of government and health sites from 2023 onward. Add it here for AI-answer quotability, not for a rich snippet.

`{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Does RankReady conflict with Yoast?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. It writes the AI-era layer and leaves the Article, breadcrumb and organisation nodes to your SEO plugin."
}
}
]
}`

Two rules on this one, and the second is the one people ignore.

First, every question in the markup must appear visibly on the page. Schema describing content that is not there is detectable and pointless.

Second, cap the count. Each FAQ entry generates a heading, so a long FAQ block reshapes the whole outline of the page. Sequential heading order, H2 to H3 to H4 with no skips, correlates with a 2.8x lift, so the structure helps. Volume does not. Four to six real questions is the working range. Twenty invented ones is a measurable cost with nothing on the other side of the ledger.

Answer text is worth writing to the same standard as the page body. Keep each answer's first sentence short and self contained, because that sentence is the unit that gets lifted. The reasoning behind that is in [drafting under the ceiling from the start](/write-sentences-ai-can-quote/).

## speakable marks the sentences worth quoting

`speakable` is a property rather than a type, and it is the closest thing schema.org offers to pointing at a paragraph and saying "this part is the answer". It belongs to the small set of [schema types built to be lifted into an answer](/schema-markup-guide-ai/) rather than to earn a rich result.

`{
"@context": "https://schema.org",
"@type": "WebPage",
"@id": "https://yoursite.com/your-post/#webpage",
"speakable": {
"@type": "SpeakableSpecification",
"cssSelector": [".rnrd-summary", "h1"]
}
}`

The selector approach is more durable than XPath on WordPress, because themes rewrite their DOM structure more often than they rename a class you control.

Pick two or three selectors, not ten. The point is a small, deliberate set. Marking the whole article body as speakable communicates nothing, which is the same as not marking it.

The obvious candidate is a short summary block near the top of the post. That placement is doing double duty, since 41.9 percent of AI citations come from the first 30 percent of a page.

## Person makes the author resolvable

Most WordPress sites emit an author as a string. A string is a name. A `Person` node with `sameAs` is an identity that can be cross referenced.

`{
"@context": "https://schema.org",
"@type": "Person",
"@id": "https://yoursite.com/#/schema/person/author",
"name": "Your Name",
"url": "https://yoursite.com/about/",
"jobTitle": "Founder, Your Company",
"sameAs": [
"https://www.linkedin.com/in/example/",
"https://github.com/example",
"https://profiles.wordpress.org/example/"
]
}`

Two details make this work rather than decorate the page.

The `@id` must be stable and must be the same value the `Article` node references in its `author` field. That is what joins the two nodes into one graph instead of leaving two unrelated blobs on the page.

The `sameAs` entries must be profiles that actually exist and actually belong to that person. Three real ones beat ten aspirational ones. Identity markup only carries weight when the prose behind it holds up, which is [how experience and authority get judged sentence by sentence](/eeat-seo-ai-answers/).

## Put it in the graph, not beside it

The single biggest implementation mistake is emitting a separate `<script type="application/ld+json">` block that repeats nodes your SEO plugin already owns.

Rank Math, Yoast, AIOSEO and SEOPress each publish an `@graph` array with `@id` references linking the nodes. New structured data should either be added into that graph, or describe something outside it.

Safe to add as a separate block: `FAQPage`, `speakable` on a `WebPage` node, `HowTo` where genuine.

Never add as a separate block: a second `Article`, a second `BreadcrumbList`, a second `Organization`. Those create contradictions, and a parser has no reliable way to pick the right one.

One URL's JSON-LD, correct and duplicated

One URL's JSON-LD, correct and duplicated

What one page should emit
Article x1
author -> Person
mainEntityOfPage -> WebPage
WebPage x1
Person x1

What two SEO plugins emit
Article x1 (plugin A)
Article x1 (plugin B)
-
author -> Person
author -> a different Person

WebPage x2

Two Article nodes for one URL is the failure mode. One canonical, one Article, one author.

The duplicate is not extra coverage. It is a second answer to a question that has one answer, emitted by a second plugin that does not know the first one ran.

The practical test is one question. Could a reader verify every claim in your JSON by reading the visible page? If the answer is no for any field, that field is a liability rather than an asset.

Verify after every change:

`curl -s https://yoursite.com/your-post/ | grep -c 'application/ld+json'`

Then run the URL through Google's Rich Results Test, which catches type errors that valid JSON hides, or reproduce the same checks offline with [a curl and Python validation loop](/check-schema-without-google/).

## The five minute version on WordPress

Hand rolling all four means filter callbacks into your SEO plugin's graph, custom fields for the FAQ pairs, a `speakable` selector that survives theme updates, and a `Person` node with a stable `@id`. All of it sits in `functions.php` until something changes underneath it.

That set is what [RankReady](https://wordpress.org/plugins/rankready-ai-llm-seo/) writes for you. It is a WordPress AI SEO plugin built to run alongside Rank Math, Yoast, AIOSEO or SEOPress, so your existing plugin keeps ownership of `Article`, breadcrumbs and organisation data while RankReady adds the AI-era layer without colliding with it. Setup takes about five minutes.

What that gives you is a page that parses cleanly and describes itself consistently. Citations are not something a plugin can promise, and the vendors promising them are guessing.

## The order to add them in

Doing all four at once on a site that has none is how mistakes get shipped together and become hard to unpick. Add them in this order instead.

Audit what exists. Count the JSON-LD blocks, read the types, fix any duplicate `Article` before adding anything.

- Fix `dateModified` so it reflects reality. This is one field and it costs nothing.

- Add `Person` with a stable `@id` and real `sameAs` entries. Do this once for the site rather than per post.

- Add `speakable` pointing at a short summary near the top of each post.

- Add `FAQPage` last, and only on pages where you have four to six questions somebody genuinely asked.

Steps 1 and 2 fix problems. Steps 3 to 5 add capability. Doing them in the other order means building on top of contradictions you have not found yet.

## Start with the field nobody checks

Before adding any new type, run the `dateModified` check from earlier in this post across your ten best pages. It is one line, and on most WordPress sites it surfaces at least one page claiming it has never been touched since publication.

Once your schema is consistent, the layer underneath it is whether crawlers reach the page at all, which is layer one of [the whole audit, layer by layer](/five-layer-ai-seo-audit/).

Which of your four types is doing the least work right now, and would you notice if it disappeared tomorrow?