URL: /rules/schema/expected-type

---
title: "Expected schema for page type: what each kind of page should declare"
description: "A page the classifier confidently reads as a product, article, contact or docs page that declares none of the structured data that type should carry. squirrel names the type and the schema to add."
---

## What this rule asks

The per-type schema rules start from the markup: `schema/product` only looks at a page that already declares `Product`, so a product page with no structured data passes it. This rule starts from the page instead. The bundled [page classifier](/configuration/classifier) reads how a page is built and labels it a product page, an article, a contact page and so on. If it is sure, and the page declares none of the schema.org types that kind of page should carry, that is a finding.

Structured data is what makes a product page eligible for product rich results, an article eligible for article rich results, and a contact or about page legible to a search engine or an agent as the business behind the site. A page the model reads one way and the markup says nothing about is usually a template or CMS gap.

## What squirrel checks

The rule runs on every crawled page and emits one check named `expected-type`. It reads the page type the classifier stored for the page and the types the page declares: JSON-LD nodes at any depth, including `@graph` members and nested ones such as the `mainEntity` of a `WebPage`, microdata `itemtype` values and RDFa `typeof` values. Types are compared case-insensitively, and a prefix such as `https://schema.org/` or `schema:` is ignored.

Only a page type the **model** answered is trusted, and only at or above the `min_confidence` option (0.9 by default). When the classifier fell back to its rules path, the label came from the page's own JSON-LD `@type` or `og:type`, so judging the markup against it would be circular. The rule is also silent for a page with no classification at all, which is what you get with `--no-classifier` or `classifier.enabled = false`.

- Info with the message `Page type not classified with enough confidence to expect structured data`, when the page has no classification, the label came from the rules path, or the confidence is below `min_confidence`.
- Info with the message `No structured data expected for this page type` and the label as the value, for the page types that carry no expectation: `homepage`, `legal`, `pricing`, `other` and `unknown`. The site-level `WebSite` and `Organization` nodes have their own rules, [schema/entity-website-missing](/rules/schema/entity-website-missing) and [schema/entity-organization-missing](/rules/schema/entity-organization-missing).
- Pass with the message `<Page noun> declares <type>` when the page declares at least one type from the family for its label. The type is the spelling the page used. The nouns are in the table below.
- Pass with the message `Page declares <type>, read as a listing` when the page is read as an `article`, `blog_post` or `product_detail`, declares none of that family, and declares `CollectionPage`, `ItemList`, `SearchResultsPage` or `Blog`. The markup may veto a finding, never create one: a blog index or pillar page the model reads as a post is a listing, and is not asked for `Article` markup.
- Info with the message `Page markup too large to judge structured data`, when the page declares nothing from the family but a read hit one of those bounds. It is not a finding, because a missing schema is not proven.
- Fail with the message `Reads as <a|an> <page noun> but declares no <expected types> structured data`, when neither of the above holds. The message does not carry the confidence, so a report groups the failing pages of one page type into one finding. The check carries `details` with the page type (`pageType`, with `pageTypeSource` set to `model`), the confidence to two decimals, the expected types and the types the page does declare.

| Page type | Noun | Schema types that satisfy it |
|---|---|---|
| `article` | article | `Article` and every type ending in `Article`, plus `Report`, `BlogPosting`, `LiveBlogPosting`, `SocialMediaPosting`, `DiscussionForumPosting` |
| `blog_post` | blog post | the same family as `article` |
| `product_detail` | product page | `Product`, `ProductGroup`, `ProductModel`, `IndividualProduct`, `SomeProducts`, `Vehicle`, `Car`, `SoftwareApplication`, `MobileApplication`, `WebApplication` |
| `product_listing` | product listing | `CollectionPage`, `ItemList`, `SearchResultsPage`, `Blog`, `OfferCatalog` |
| `listing_index` | listing page | the same family as `product_listing` |
| `docs` | documentation page | the `article` family, plus `APIReference`, `HowTo`, `FAQPage`, `QAPage` |
| `contact` | contact page | `ContactPage`, or any organization type |
| `about` | about page | `AboutPage`, `ProfilePage`, `Person`, or any organization type |

An organization type is `Organization`, `Corporation`, `NGO`, `Brand`, `Airline`, `Consortium`, `ProfessionalService`, `EducationalOrganization`, `GovernmentOrganization`, `MedicalOrganization`, `SportsOrganization`, `PerformingGroup`, every `LocalBusiness` subtype such as `Restaurant` or `Dentist`, and any type ending in `Business` or `Organization`. `JobPosting` is not an article.

The listing veto reads only top-level and `@graph` node types, so a nested `isPartOf` of type `Blog` does not turn a post into a listing. The reads are bounded: JSON-LD is read to a depth of 8 and 2,000 objects, microdata and RDFa to 50,000 elements, 5,000 annotated elements and 64 types per attribute. When a bound is hit, or the page has no document, the rule does not claim a schema is missing. Severity is warning and weight is 5. The rule is skipped on a page detected as a soft 404, because an error template says nothing about what the site meant the page to be.

## How to fix it

Add JSON-LD that matches what the page is.

```html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "ContactPage",
  "name": "Contact us",
  "url": "https://example.com/contact",
  "mainEntity": {
    "@type": "Organization",
    "name": "Example Ltd",
    "telephone": "+1-555-0100",
    "email": "hello@example.com"
  }
}
</script>
```

- **Product pages**: `Product` with `name`, `image` and `offers`, for product rich results. [schema/product](/rules/schema/product) then checks the details.
- **Articles and blog posts**: `Article` or `BlogPosting` with `headline`, `author` and `datePublished`. [schema/article](/rules/schema/article) checks those.
- **Listings and collections**: `CollectionPage` or `ItemList`, ideally with a `BreadcrumbList`.
- **Documentation**: `TechArticle`, `HowTo` or `FAQPage`.
- **Contact and about pages**: `ContactPage` or `AboutPage`, and an `Organization` or `LocalBusiness` with `name`, `url` and contact details, so a search engine or an agent can tie the page to the business behind it.

If the classifier misread the page, raise `min_confidence` or disable the rule. A page that genuinely should carry no markup, such as a one-off landing page filed under the wrong type, is fine to leave.

| | |
|---|---|
| **Rule ID** | `schema/expected-type` |
| **Category** | [Structured Data](/rules/schema) |
| **Scope** | Per-page |
| **Severity** | warning |
| **Weight** | 5/10 |

## Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `min_confidence` | number | `0.9` | Minimum classifier confidence (0 to 1) before a page type is trusted |

### Configuration example

```toml squirrel.toml
[rule_options."schema/expected-type"]
min_confidence = 0.95
```

`squirrel config validate` reports a value outside 0 to 1. Raising it trades findings for certainty: a page the model labels at 0.92 is flagged at the default and left alone at 0.95.

## Enable / disable

### Disable this rule

```toml squirrel.toml
[rules]
disable = ["schema/expected-type"]
```

### Disable all Structured Data rules

```toml squirrel.toml
[rules]
disable = ["schema/*"]
```

### Enable only this rule

```toml squirrel.toml
[rules]
enable = ["schema/expected-type"]
```

`enable` replaces the default `["*"]`, so this is the only rule selected. Leave `disable` out: a rule that matches any `disable` pattern is skipped even when `enable` names it. Only an `enabled` setting under a rule's own `[rule_options]` table overrides both lists.

## Related rules

- [schema/coverage-outlier](/rules/schema/coverage-outlier): pages missing markup that the rest of their page type carries. That rule compares a page with its siblings; this one compares it with what its type should declare.
- [schema/product](/rules/schema/product): the required properties of a `Product` that is declared.
- [schema/article](/rules/schema/article): the required properties of an `Article` that is declared.
- [schema/breadcrumb](/rules/schema/breadcrumb): the trail on a page that has no `BreadcrumbList`, read from the classifier's breadcrumbs section.

Structured data findings ship in every audit next to the core SEO, performance and agent experience rules. See [Fix SEO issues with AI](https://squirrelscan.com/learn/fix-seo-issues-with-ai) for how an agent works through a report.

## References

- [Structured data general guidelines, Google Search Central](https://developers.google.com/search/docs/appearance/structured-data/sd-policies)
- [Product structured data, Google Search Central](https://developers.google.com/search/docs/appearance/structured-data/product)
- [Article structured data, Google Search Central](https://developers.google.com/search/docs/appearance/structured-data/article)
- [ContactPage, schema.org](https://schema.org/ContactPage)
- [AboutPage, schema.org](https://schema.org/AboutPage)

## Check your site

Run `squirrel audit https://example.com` and open the Structured Data section of the report. Each finding names the page, the type the classifier read and the schema to add. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

<CardGroup cols={2}>
  <Card title="Install squirrel" icon="terminal" href="/quickstart">
    One command to install, one to run your first audit.
  </Card>
  <Card title="See a sample report" icon="file-text" href="https://squirrelscan.com/reports/squirrelscan-sample">
    What the Structured Data section looks like on a real site.
  </Card>
</CardGroup>
