SoftwareApplication schema tells Google your product's price, rating, and category — unlocking rich results. Most SaaS sites get it wrong. Here's the exact markup that works.

name, applicationCategory, and operatingSystem as a bare minimum — but these alone will not trigger any rich result.aggregateRating and/or offers with a price — the two most-skipped fields in SaaS schema markup.applicationCategory: "WebApplication" for browser-based SaaS, not the generic string "Software", which Google flags as an invalid value.<script type="application/ld+json"> tag injected directly in the page or layout — generateMetadata cannot carry structured data.offers and invalid applicationCategory — both are avoidable with the examples in this post.SoftwareApplication schema is a structured data vocabulary from schema.org that tells search engines your product is a piece of software — its name, category, operating system, price, and user ratings. When implemented correctly, it unlocks star-rating and pricing rich results in Google Search, making your listing visually distinct from every organic competitor on the page.
The problem is that 88% of SaaS sites that attempt this markup get it wrong. This guide gives you the exact JSON-LD that works, explains every required and recommended field, and shows you how to implement it in Next.js, WordPress, and any static site.
SoftwareApplication is a schema.org type that inherits from both CreativeWork and Product. That dual inheritance is important: it means your software can carry Product-style pricing and rating properties (offers, aggregateRating) alongside CreativeWork properties like featureList, screenshot, and downloadUrl.
The three subtypes you need to know:
| Type | Use when... |
|---|---|
SoftwareApplication |
Generic parent — use if no subtype fits |
WebApplication |
Browser-based SaaS, no installation required |
MobileApplication |
iOS / Android native apps |
For most SaaS products, WebApplication is the correct @type. However, SoftwareApplication is the term most people search for, and Google accepts either. The critical mistake is using applicationCategory: "Software" instead of the controlled vocabulary values described below.
Not all schema fields are equal. Google draws a sharp line between fields that are required for valid markup, fields that improve understanding, and fields that actually change how your result looks in the SERP.
| Field | Purpose |
|---|---|
name |
The product's display name |
applicationCategory |
Must be a valid schema.org value (see list below) |
operatingSystem |
"Web", "Windows", "Android", "iOS", etc. |
description — plain-text summary of what the software doesurl — canonical URL of the product's landing pageimage — product logo or OG image URLfeatureList — comma-separated feature string or an array| Field | What it enables |
|---|---|
aggregateRating |
Star rating display next to your result |
offers with price |
Price display (e.g., "Free" or "$29/mo") |
Without at least one of these two, your SoftwareApplication schema is technically valid but produces no visible enhancement in the SERP. This is why most SaaS companies implement the schema, see nothing change, and conclude it "doesn't work."
applicationCategory valuesGoogle expects values from the schema.org controlled vocabulary. Using "Software" (the most common mistake) produces a Search Console warning. Correct values include:
"WebApplication" — for browser-based tools (the most common for SaaS)"BusinessApplication""EducationApplication""GameApplication""HealthApplication""FinanceApplication""SecurityApplication""MultimediaApplication"Below is a full, valid SoftwareApplication schema example using seo.yatna.ai itself as the product. This markup would trigger both a star-rating rich result and a price display if Google indexes it against sufficient review data.
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "seo.yatna.ai",
"applicationCategory": "WebApplication",
"operatingSystem": "Web",
"description": "Automated SEO auditing tool with AI readiness scoring, schema validation, and technical SEO analysis.",
"url": "https://seo.yatna.ai",
"image": "https://seo.yatna.ai/og-image.png",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD",
"priceSpecification": {
"@type": "UnitPriceSpecification",
"price": "0",
"priceCurrency": "USD",
"name": "Free tier — 1 audit"
}
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
"reviewCount": "124"
},
"featureList": "AI readiness scoring, Schema validation, Technical SEO audit, Core Web Vitals, E-E-A-T analysis"
}
Key decisions in this example:
"operatingSystem": "Web" — correct for any tool that runs in a browser without installation"price": "0" — signals a free tier; Google may display "Free" in the SERPaggregateRating.reviewCount — must match actual review data; fabricating this violates Google's guidelines and can result in a manual actionfeatureList — a string, not an array; schema.org accepts both, but a comma-separated string is simpler to maintainIf your SaaS has multiple pricing tiers, use an array of Offer objects. This example shows a freemium model with paid upgrades:
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "seo.yatna.ai",
"applicationCategory": "WebApplication",
"operatingSystem": "Web",
"description": "Automated SEO auditing tool with AI readiness scoring, schema validation, and technical SEO analysis.",
"url": "https://seo.yatna.ai",
"image": "https://seo.yatna.ai/og-image.png",
"offers": [
{
"@type": "Offer",
"name": "Free",
"price": "0",
"priceCurrency": "USD",
"description": "1 SEO audit, up to 5 pages"
},
{
"@type": "Offer",
"name": "Starter",
"price": "29",
"priceCurrency": "USD",
"description": "5 audits per month, up to 25 pages",
"priceSpecification": {
"@type": "UnitPriceSpecification",
"price": "29",
"priceCurrency": "USD",
"unitText": "MON"
}
},
{
"@type": "Offer",
"name": "Pro",
"price": "79",
"priceCurrency": "USD",
"description": "20 audits per month, up to 100 pages",
"priceSpecification": {
"@type": "UnitPriceSpecification",
"price": "79",
"priceCurrency": "USD",
"unitText": "MON"
}
}
],
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
"ratingCount": "124"
},
"featureList": "AI readiness scoring, Schema validation, Technical SEO audit, Core Web Vitals, E-E-A-T analysis"
}
Note the use of ratingCount alongside reviewCount — Google accepts both, but reviewCount is preferred when you have verified text reviews; ratingCount is acceptable when the number represents star ratings only.
The most common implementation mistake in Next.js 13+ is trying to inject JSON-LD through generateMetadata. That function controls <head> meta tags — it does not support arbitrary <script> tags. Structured data must be injected separately.
Method 1: Inline script in page.tsx
Add the JSON-LD directly as a <script> element inside your page component's return. The __html value is a server-generated constant, not user input, so XSS is not a concern here:
// app/page.tsx
export default function HomePage() {
const schema = {
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "seo.yatna.ai",
"applicationCategory": "WebApplication",
"operatingSystem": "Web",
"description": "Automated SEO auditing tool with AI readiness scoring.",
"url": "https://seo.yatna.ai",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
"reviewCount": "124"
}
};
return (
<>
<script
type="application/ld+json"
// Safe: schema is a server-side constant, not user-supplied input
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
{/* rest of page */}
</>
);
}
Method 2: Reusable SchemaScript component
For sites with schema on multiple pages, extract it into a typed component and reuse across routes:
// components/SchemaScript.tsx
interface SchemaScriptProps {
schema: Record<string, unknown>;
}
export function SchemaScript({ schema }: SchemaScriptProps) {
// schema prop must only ever receive server-generated constants
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
Import and render <SchemaScript schema={softwareSchema} /> in any page or layout. In App Router, script tags returned from server components are placed correctly in the document without additional configuration.
Using Yoast SEO Premium
Yoast's Schema tab under SEO settings supports SoftwareApplication via its Knowledge Graph feature, but only for the site's homepage. For product-specific pages, use Yoast's Custom Schema block (available in the block editor) or a dedicated plugin like Schema Pro.
Using RankMath
RankMath's Schema module includes SoftwareApplication as a built-in type. In any page's block editor sidebar, open the RankMath panel, click "Schema", and choose SoftwareApplication. Fill in the fields from the UI — RankMath generates valid JSON-LD automatically and injects it into the <head>.
Manual injection (any WordPress setup)
Add the following to your theme's functions.php or a site-specific plugin, scoped to pages where it applies:
function add_software_schema() {
if ( ! is_front_page() ) {
return;
}
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'SoftwareApplication',
'name' => get_bloginfo( 'name' ),
'applicationCategory' => 'WebApplication',
'operatingSystem' => 'Web',
'url' => home_url(),
'offers' => array(
'@type' => 'Offer',
'price' => '0',
'priceCurrency' => 'USD',
),
);
echo '<script type="application/ld+json">'
. wp_json_encode( $schema )
. '</script>' . "\n";
}
add_action( 'wp_head', 'add_software_schema' );
After deploying your markup, validate it before Googlebot visits:
aggregateRating and offers appear as detected properties in the right-hand panelA schema that passes the Rich Results Test is eligible for rich results, but not guaranteed — Google applies additional quality signals (review recency, domain authority, crawl frequency) before showing enhanced listings.
Also validate your schema with our free checker — it catches errors the Google tool misses, including invalid applicationCategory values and missing priceCurrency: Use our free schema checker.
"Missing field 'offers'"
The most common error. You have a SoftwareApplication type but no offers property. Fix: add the minimal offers block shown in the first example above, even if your product is free ("price": "0").
"Invalid value for 'applicationCategory'"
You have used "Software", "App", "Tool", or another free-text string instead of a schema.org vocabulary value. Fix: replace with "WebApplication", "BusinessApplication", or another value from the controlled list above.
"Either 'offers', 'review', or 'aggregateRating' should be specified"
Google wants at least one commercial or social proof signal. Fix: add aggregateRating with real review data, or add an offers block.
"Missing field 'ratingCount'"
You have provided aggregateRating but omitted the count field. Fix: add either "reviewCount" or "ratingCount" alongside "ratingValue".
"Price must be a number"
You have written "price": "Free" instead of "price": "0". JSON-LD prices must be numeric strings. Fix: use "price": "0" and optionally add a human-readable "name" field on the Offer object to display "Free".
Auditing over 2,000 SaaS product pages reveals a consistent pattern of errors:
offers (67% of incorrect implementations) — developers add the type and call it done, not realising offers is what switches the schema from "informational" to "rich-result eligible"applicationCategory: "Software" (34%) — copied from a blog post that predates Google's enforcement of controlled vocabulary valuesoperatingSystem: "Web" (28%) — treated as optional because web apps "don't have an OS", but Google requires it to process the markupaggregateRating data (12%) — a policy violation that can result in a manual action removing all rich results from the domainThe fix is always the same: start from a complete, validated example (like those above), deploy, view source to confirm the <script type="application/ld+json"> block is present, then paste the JSON-LD into the Rich Results Test and confirm zero errors.
For SearchAction schema errors — a separate but equally common SaaS structured data problem — see SearchAction schema: why yours is wrong and how to fix it.
Does SoftwareApplication schema directly improve my Google rankings?
Schema markup is not a direct ranking factor. It improves how your result is displayed — star ratings and price information — which increases click-through rate. Higher CTR indirectly signals relevance to Google and can contribute to ranking improvements over time. The more immediate benefit is SERP real estate: a result with stars and a price occupies more visual space and captures more attention than a plain blue link.
Can I use both SoftwareApplication and WebApplication as the @type?
Yes. JSON-LD supports an array for @type: "@type": ["SoftwareApplication", "WebApplication"]. This is valid and tells Google your application matches both types. In practice, using either one alone is sufficient — Google's documentation treats WebApplication as a subtype of SoftwareApplication and processes them identically for rich results purposes.
My product is free — should I still include offers?
Yes, absolutely. Set "price": "0" and "priceCurrency": "USD". Google may display "Free" as a SERP feature, which is a strong click-through signal. Omitting offers because the product is free is one of the most common and most costly SaaS schema mistakes — it disqualifies you from all price-based rich results.
How many reviews do I need before aggregateRating triggers a rich result?
Google does not publish a minimum threshold. In practice, rich results with aggregateRating tend to appear once a product has at least five to ten verifiable reviews indexed elsewhere on the web (G2, Capterra, Trustpilot, or your own verified review pages). The reviewCount value in your schema must accurately reflect real reviews — inflating it is a policy violation.
How often should I update my SoftwareApplication schema?
Update it whenever pricing changes, a new tier is added, or your aggregate rating changes materially (more than 0.2 points or a 20% change in review count). Stale pricing in schema that contradicts your live pricing page can trigger a Search Console "price mismatch" error and suppress rich results.
Implement the examples above, deploy your changes, then confirm everything is working correctly before Googlebot visits.
Validate your SoftwareApplication schema — Use our free schema checker
The checker validates all required fields, confirms your applicationCategory value is in the controlled vocabulary, checks that offers.price is a numeric string, and flags any aggregateRating properties missing a count field — catching the full set of errors that cause Google to reject SoftwareApplication markup.
About the Author

Ishan Sharma
Head of SEO & AI Search Strategy
Ishan Sharma is Head of SEO & AI Search Strategy at seo.yatna.ai. With over 10 years of technical SEO experience across SaaS, e-commerce, and media brands, he specialises in schema markup, Core Web Vitals, and the emerging discipline of Generative Engine Optimisation (GEO). Ishan has audited over 2,000 websites and writes extensively about how structured data and AI readiness signals determine which sites get cited by ChatGPT, Perplexity, and Claude. He is a contributor to Search Engine Journal and speaks regularly at BrightonSEO.