Skip to content
Instant · runs in your browser

QA Schema Generator: One Question, Community Answers

QAPage done correctly: one question, many answers - not a list of FAQs.

QA schema generator output describes exactly one question. Not a list of questions, not a page of headings you wrote and answered yourself. One question, an answer count, one accepted answer, and however many suggested answers other people contributed. Getting that wrong is the single most common QAPage mistake, and it turns valid-looking markup into a structured data violation.

If you wrote both the question and the answer, you do not want QAPage. You want FAQPage.

QAPage is for one question with several community answers, like a forum thread. Put the question itself in the field above.

Year first, then month, then day: 2024-03-14.

Dominate AI Search Using a Proven System

BlazeHive runs the whole system for you - finds the keywords buyers actually search, writes the pages end to end, and publishes them so you show up in Google and in AI answers. Free trial, no card.

Start with BlazeHive Free trial

QAPage or FAQPage, and why the difference is not cosmetic

QAPage describes a page where visitors submit competing answers. A forum thread. A community Q&A board. A support site where users reply to each other and one reply gets marked as accepted. The defining feature is that the answers come from people who are not you.

FAQPage describes a page where the site owner asks and answers. Your pricing FAQ. The 5 questions at the bottom of a product page. One voice, no competition, no accepted-answer semantics.

The 2 are not interchangeable. Google's structured data guidelines treat markup that misrepresents a page as a spam issue, and a QAPage block on a page nobody can answer says "here are competing community answers" about content that has none.

The quick test: can a stranger add an answer to this page? If no, it is a FAQPage.

What a valid QAPage schema block looks like

The shape is a single Question under mainEntity:

  • mainEntity is one Question object, never an array.
  • Question.name is the question itself, as a headline.
  • Question.text is the full body of the question, the paragraph underneath the title.
  • Question.author is the person who asked.
  • Question.dateCreated is when the question was posted.
  • Question.answerCount is the number of answers on the page.
  • Question.acceptedAnswer is one Answer, the accepted or top-voted one.
  • Question.suggestedAnswer is an array of the remaining Answer objects.

answerCount should reflect what is on the page. If a thread has fourteen answers and you mark up 3, do not claim fourteen unless all fourteen are visible.

Every field, and where it lands

Q&A items does the structural work. One line per entry, question | answer. The first line's left-hand side becomes Question.name. Every right-hand side becomes an answer, and any line after the first with no pipe is treated as another answer. The first answer becomes acceptedAnswer; everything after it becomes suggestedAnswer[].

The parser accepts tabs instead of pipes, strips leading bullets and 1. numbering, and understands Q: and A: prefixes, because that is how people paste from a forum export.

Paste several distinct question-and-answer pairs and the generator folds the extras in as answers and warns you, with a pointer to the FAQ generator. That warning is telling you that you picked the wrong schema type.

Description becomes Question.text, the full body of the question. Fill it in: a bare one-line question gives a parser nothing beyond the headline.

Author name becomes Question.author, emitted as a Person. This is the asker. Not the site, not the person who answered, not you. If the author URL field is filled it becomes author.url.

Published date becomes Question.dateCreated, and must be ISO 8601: 2024-03-14 or 2024-03-14T09:00:00+01:00. 14/03/2024 and March 14, 2024 are the 2 formats people paste, and neither is valid.

Name and page URL are shared form fields and are not emitted in a QAPage block, which carries mainEntity and nothing else. Leaving them blank changes nothing.

Author and dateCreated describe the asker, not you

This trips up almost everyone migrating from Article markup, where author means the person who wrote the page. On a QAPage, author and dateCreated sit on the Question, and the question was written by whoever posted it.

If your forum stores an author on each answer too, Answer.author and Answer.dateCreated are valid schema.org properties worth adding by hand once you have the block. This generator emits the question-level ones, which are the ones Google documents as recommended.

Putting your own byline on the Question is the version of this mistake that survives review, because nothing errors. It just describes the page incorrectly.

A worked Q&A schema example

2 lines in the Q&A items box:

Why does my Postgres connection pool exhaust under pgbouncer? | Transaction pooling does not preserve session state, so your ORM re-registers prepared statements on every checkout and never releases them. Disable statement caching in the ORM, or move to session pooling.
Check max_client_conn on pgbouncer before touching the ORM. We had the same symptom with the default of 100 and twelve app pods.

With Description set to the body of the post, Author name dmitri-k, and Published date 2024-03-14:

{
  "@context": "https://schema.org",
  "@type": "QAPage",
  "mainEntity": {
    "@type": "Question",
    "name": "Why does my Postgres connection pool exhaust under pgbouncer?",
    "text": "Running pgbouncer in transaction mode in front of Postgres 15. The pool hits its ceiling after about twenty minutes of normal traffic.",
    "author": { "@type": "Person", "name": "dmitri-k" },
    "dateCreated": "2024-03-14",
    "answerCount": 2,
    "acceptedAnswer": {
      "@type": "Answer",
      "text": "Transaction pooling does not preserve session state, so your ORM re-registers prepared statements on every checkout and never releases them. Disable statement caching in the ORM, or move to session pooling."
    },
    "suggestedAnswer": [
      {
        "@type": "Answer",
        "text": "Check max_client_conn on pgbouncer before touching the ORM. We had the same symptom with the default of 100 and twelve app pods."
      }
    ]
  }
}

The second line had no pipe, so it became the second answer rather than a second question. That is the behavior you want on a thread.

What your schema builder checks before you ship

Required, and flagged if missing: a question, at least one answer, and text on the accepted answer. Recommended and flagged if absent: the asker's name, the question body, and the creation date.

Beyond that, 3 things worth checking yourself:

  • Every question and answer must be visible in the page text. Markup describing answers a visitor cannot see is a violation, and it is the fastest way to lose eligibility for everything, not just this feature.
  • Do not mark up a thread with zero answers. A Question with answerCount: 0 and no acceptedAnswer has nothing to offer a parser.
  • One QAPage block per page. If the URL hosts several threads, it is an index page, and index pages get QAPage markup on the thread pages they link to, not on themselves.

On rich results: Q&A appearances in Google have narrowed sharply since 2023, so do not build a community page for the SERP treatment. Ship the markup because it describes the page accurately and because non-Google parsers and AI answer engines read it.

Dominate AI Search Using a Proven System

BlazeHive runs the whole system for you - finds the keywords buyers actually search, writes the pages end to end, and publishes them so you show up in Google and in AI answers. Free trial, no card.

Start with BlazeHive Free trial

Frequently Asked Questions

When should I use FAQPage instead?

Whenever you wrote both sides. Product FAQs, support articles, pricing pages, anything with a "Frequently Asked Questions" heading you authored. QAPage is only for pages where visitors can submit competing answers.

Can a QAPage have more than one question?

No. mainEntity is a single Question. If your page holds several distinct questions, either split it into one page per question or use FAQPage, depending on who wrote the answers.

What if no answer has been accepted yet?

Emit every answer as suggestedAnswer and leave acceptedAnswer out. This generator promotes the first answer by default, so put the accepted one first, or remove the property by hand if nothing has been accepted.

Does upvote count belong in the markup?

Answer.upvoteCount is a valid schema.org property and useful on a page that has voting. It is not emitted here. Add it per answer in your template if your platform tracks it.

What date format does it need?

ISO 8601 only: 2024-03-14 or 2024-03-14T09:00:00+01:00. Slash-separated and long-form dates are rejected with a warning rather than silently emitted, since a malformed date is worse than no date.

Where do I put the script tag?

Anywhere in <head> or <body> on the thread page itself. The output escapes <, > and & as unicode sequences, so an answer containing </script> cannot break out of the tag it sits in. It is still valid JSON and every parser decodes it. If keeping markup correct across a growing site is the real problem, that is the end-to-end SEO content work BlazeHive automates.

Related free tools

All tools →