Everything you need to implement schema.org/JobPosting correctly — from required fields to salary markup, remote jobs, WordPress integration, and passing Google's Rich Results Test first time.
What You'll Learn
- What JobPosting schema is and how it works
- All required & recommended properties for 2026
- Full working JSON-LD code with explanations
- How to mark up remote jobs and salary ranges
- WordPress implementation with plugins and manual code
- Common errors and how to fix each one
- Do's and don'ts that Google explicitly checks
- How to validate and monitor in Search Console
What Is JobPosting Schema Markup?
JobPosting schema is structured data you add to a job listing page using the schema.org/JobPosting vocabulary. Written as a JSON-LD script block in your page's HTML, it gives Google a machine-readable description of every important detail in the job ad — the title, description, employer, location, salary, employment type, and more.
When Google's crawler finds correctly implemented JobPosting schema, it can surface that listing directly inside the Google Jobs experience — the dedicated job-search interface that appears at the top of relevant search results pages. This is also sometimes called a job posting rich result.
JSON-LD vs Microdata vs RDFa: Google supports all three formats, but officially recommends JSON-LD for structured data. All examples in this guide use JSON-LD because it's easiest to maintain — it lives in a script block and never needs to be woven into your HTML markup.
How Google Jobs Works
Google Jobs is not a separate website — it's a feature built into Google Search. When a user searches for a job-related query like "software engineer jobs remote" or "marketing manager jobs London," Google may surface a dedicated Jobs panel above or alongside standard results, populated entirely from structured data it has crawled from employers' and job boards' websites.
Your job listings compete in this panel based on schema completeness, data freshness, page quality, and other algorithmic signals. Schema is the entry point — without it, you simply cannot appear, no matter how great the page is.
Why JobPosting Schema Matters More in 2026
The stakes for correct implementation have risen considerably in 2026 for three reasons:
- AI Overviews now reference Google Jobs data. Google's AI-generated answer cards for job searches pull structured data directly. If your schema is wrong or missing, your listings are invisible in AI search too.
- Google is stricter about spam. In 2025, Google updated its job posting content policies to penalize misleading salary data, vague descriptions, and listings without proper
validThroughdates. Violating these can get your entire domain demoted. - Direct competition from aggregators. Indeed, LinkedIn, and Glassdoor all implement schema perfectly. If you don't, you lose clicks to aggregators even for your own job listings.
New in 2026: Google now penalizes listings where the validThrough date has passed but the job is still indexed. Always set accurate expiry dates and unpublish filled roles promptly.
Required Properties (2026)
Google will not show a rich result for any job posting that is missing one or more of these four properties. They are non-negotiable.
| Property | Type | Status | What Google Expects |
|---|---|---|---|
| title | Text | Required | The job title exactly as it appears in the listing. Don't stuff keywords here — Google will suppress it. |
| description | Text / HTML | Required | Full job description. Supports basic HTML (<p>, <ul>, <strong>). Must be substantive — not a summary. |
| hiringOrganization | Organization | Required | Employer details: name and ideally sameAs (company website URL) and logo. |
| jobLocation | Place | Required* | Physical work location with a full PostalAddress. *Remote-only roles use jobLocationType instead — see section below. |
Also treat as required: datePosted and validThrough. Technically recommended by schema.org, Google's guidance states listings without datePosted are ranked lower and those without validThrough are removed from Google Jobs after a default period.
Recommended & Optional Properties
The more complete your schema, the better Google understands the listing — and the better it performs in search. These properties are optional but strongly recommended.
When the listing was published. Affects freshness ranking. Use ISO format: 2026-06-01.
When the listing expires. Critical — Google removes listings after this date. Use with time: 2026-07-01T23:59:59.
Values: FULL_TIME, PART_TIME, CONTRACTOR, TEMPORARY, INTERN, VOLUNTEER, PER_DIEM, OTHER. Can be an array.
Salary with currency, min/max values, and pay period (HOUR, WEEK, MONTH, YEAR). Strongly correlated with click-through rate.
Use TELECOMMUTE for remote roles. Must be combined with applicantLocationRequirements for geo-restricted remote jobs.
Geographic requirement for remote applicants. Specify country, state, or region using Country, State, or City schema types.
Set to true if applicants can apply directly on your page without going to a third-party ATS. Eligible for Google's "Direct Apply" badge.
Your internal job ID. Helps Google deduplicate across aggregators. Use name (your company) and value (the job ID).
Years of experience required. Text or structured with monthsOfExperience.
Minimum education level. Can use a text string or structured credential type with credentialCategory.
Required skills as a comma-separated string or multiple instances. Helps with semantic matching in AI-powered search results.
Perks and benefits. Increasingly surfaced in Google's AI Overviews for job searches when well-structured.
Full JobPosting Schema Code Example
Here is a complete, production-ready JobPosting JSON-LD block with all required and recommended properties. Copy this and replace the placeholder values with your own data.
// Add this inside <script type="application/ld+json"></script> in your page <head> { "@context": "https://schema.org", "@type": "JobPosting", // ─── Required Properties ─────────────────────────────────────── "title": "Senior Frontend Developer", "description": "<p>We are looking for a Senior Frontend Developer to join our product team. You will build responsive, high-performance web applications using React and TypeScript.</p><ul><li>5+ years of experience with React</li><li>Strong TypeScript skills</li><li>Experience with REST APIs and GraphQL</li></ul>", "hiringOrganization": { "@type": "Organization", "name": "Acme Corp", "sameAs": "https://www.acmecorp.com", "logo": "https://www.acmecorp.com/images/logo.png" }, "jobLocation": { "@type": "Place", "address": { "@type": "PostalAddress", "streetAddress": "123 Tech Street", "addressLocality": "San Francisco", "addressRegion": "CA", "postalCode": "94105", "addressCountry": "US" } }, // ─── Strongly Recommended ────────────────────────────────────── "datePosted": "2026-06-01", "validThrough": "2026-07-31T23:59:59", "employmentType": ["FULL_TIME"], "baseSalary": { "@type": "MonetaryAmount", "currency": "USD", "value": { "@type": "QuantitativeValue", "minValue": 120000, "maxValue": 160000, "unitText": "YEAR" } }, "identifier": { "@type": "PropertyValue", "name": "Acme Corp", "value": "JOB-2026-0042" }, // ─── Optional but Valuable ───────────────────────────────────── "directApply": true, "workHours": "Monday–Friday, 9am–5pm PST", "experienceRequirements": { "@type": "OccupationalExperienceRequirements", "monthsOfExperience": 60 }, "educationRequirements": { "@type": "EducationalOccupationalCredential", "credentialCategory": "bachelor degree" }, "skills": "React, TypeScript, GraphQL, REST API, CSS, HTML5, Git", "jobBenefits": "Health insurance, 401(k) matching, unlimited PTO, remote-friendly, annual learning stipend", "industry": "Software Development", "occupationalCategory": "15-1254.00" // O*NET code for Web Developers }
Remote Job Schema: The Right Way
Remote jobs have their own schema requirements. You cannot simply set jobLocation to "Remote" as a text string — Google will reject this. Use the pattern below.
Fully Remote (No Geographic Restriction)
{
"@context": "https://schema.org",
"@type": "JobPosting",
"title": "Content Strategist",
"jobLocationType": "TELECOMMUTE",
// No jobLocation required for fully remote roles with no restrictions
"applicantLocationRequirements": {
"@type": "Country",
"name": "US" // Hire anywhere in US
},
"hiringOrganization": {
"@type": "Organization",
"name": "Acme Corp",
"sameAs": "https://www.acmecorp.com"
},
"description": "...",
"datePosted": "2026-06-01",
"validThrough": "2026-08-01"
}
Hybrid (On-site + Remote Option)
{
"@context": "https://schema.org",
"@type": "JobPosting",
"title": "Product Manager",
"jobLocationType": "TELECOMMUTE",
"jobLocation": {
"@type": "Place",
"address": {
"@type": "PostalAddress",
"addressLocality": "Austin",
"addressRegion": "TX",
"addressCountry": "US"
}
},
// Both jobLocationType AND jobLocation = hybrid signal to Google
"hiringOrganization": { "..." },
"description": "..."
}
Salary Markup: Formats Google Accepts
Salary transparency is one of the most impactful things you can add to your schema. Google's research shows that listings with salary data get significantly more engagement in the Jobs panel. Here's every salary format Google supports.
| Salary Format | unitText Value | Example Schema Value | Notes |
|---|---|---|---|
| Hourly | HOUR |
minValue: 25, maxValue: 35 | Common for part-time / contractor |
| Weekly | WEEK |
minValue: 1000, maxValue: 1500 | Less common; prefer MONTH or YEAR |
| Monthly | MONTH |
minValue: 4000, maxValue: 6000 | Standard in many markets outside US |
| Annual | YEAR |
minValue: 80000, maxValue: 110000 | Most common for full-time roles |
Best practice: Always provide a salary range (minValue + maxValue) rather than a single value. Google's display format favors ranges and they correspond better to how candidates search. Never set an inflated max to attract attention — Google's spam team flags unrealistic ranges.
How to Implement JobPosting Schema
Option 1: Manual JSON-LD
If you're not using a CMS plugin, add the JSON-LD block directly to your job listing page template. Place it in the <head> or anywhere in the <body> before </body>.
Create the JSON-LD block
Start from the full example above and populate each field with your listing's real data. Don't use placeholder text — Google may flag descriptions with generic content.
Wrap in a script tag
Wrap your JSON object in <script type="application/ld+json"> … </script>. The type attribute is mandatory and must be exactly application/ld+json.
Add it to your page template
In a custom PHP template, echo the script block dynamically using your job's data fields. In static HTML, paste it directly into the page.
Test with Google Rich Results Test
Go to search.google.com/test/rich-results. Paste your URL or the raw code. It will tell you if you have a valid JobPosting rich result or show specific errors to fix.
Monitor in Google Search Console
After Google crawls your pages, go to Search Console → Enhancements → Job postings. You'll see the count of valid items and any warnings or errors requiring attention.
Option 2: WordPress Implementation
WordPress users have several options for adding JobPosting schema depending on their setup:
| Method | Best For | Schema Control | Difficulty |
|---|---|---|---|
| WP Job Manager plugin | Full job board sites | Automatic, highly configurable | Easy |
| Yoast SEO + Local SEO | Sites using Yoast already | Good but limited salary control | Easy |
| Rank Math Schema Builder | Power users wanting full control | Full property control per listing | Medium |
| Schema Pro plugin | Any WordPress site, rule-based | Full; bulk schema rules | Medium |
| WPNova Job Board Plugin | New job boards wanting best-in-class SEO | Native schema with all 2026 properties including salary, remote, directApply | Easy |
| Custom code (functions.php) | Developers who need full control | Total control | Advanced |
WordPress Custom Code (functions.php)
If you manage jobs as a custom post type, add schema dynamically using this pattern in your theme's functions.php or a custom plugin:
function wpnova_add_jobposting_schema() { if ( !is_singular('job_listing') ) return; $post_id = get_the_ID(); $title = get_the_title(); $desc = wp_strip_all_tags( get_the_content() ); $date = get_the_date( 'Y-m-d' ); $expiry = get_post_meta( $post_id, '_job_expires', true ); $schema = [ '@context' => 'https://schema.org', '@type' => 'JobPosting', 'title' => esc_html( $title ), 'description' => esc_html( $desc ), 'datePosted' => $date, 'validThrough' => $expiry ? $expiry . 'T23:59:59' : '', 'hiringOrganization' => [ '@type' => 'Organization', 'name' => get_bloginfo( 'name' ), 'sameAs' => get_bloginfo( 'url' ), ], 'jobLocation' => [ '@type' => 'Place', 'address' => [ '@type' => 'PostalAddress', 'addressLocality' => get_post_meta( $post_id, '_job_location', true ), 'addressCountry' => get_post_meta( $post_id, '_job_country', true ), ], ], ]; echo '<script type="application/ld+json">' . wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</script>'; } add_action( 'wp_head', 'wpnova_add_jobposting_schema' );
Common Errors and How to Fix Them
These are the most frequent issues flagged by the Rich Results Test and Google Search Console. Fixing them is usually straightforward once you know what Google expects.
| Error Message | Cause | Fix |
|---|---|---|
| "Missing field 'description'" | The description property is absent or empty | Add a substantive description with at least several sentences. Cannot be blank or a single word. |
| "Missing field 'hiringOrganization'" | Employer name not provided | Add hiringOrganization with at minimum @type: Organization and name. |
| "Invalid value for 'employmentType'" | Using non-standard values like "Full Time" (with a space) or "Permanent" | Use only Google's accepted enum values: FULL_TIME, PART_TIME, CONTRACTOR, etc. |
| "Either 'jobLocation' or 'jobLocationType' is required" | Both are missing; location is undefined | Add a physical jobLocation address, or set jobLocationType: TELECOMMUTE for remote jobs. |
| "Invalid date format for 'datePosted'" | Using a format like "June 1, 2026" or "01/06/2026" | Use ISO 8601 format: 2026-06-01 |
| "Salary value must be a number" | Using a string like "$80,000" instead of a numeric value | Set minValue and maxValue as plain numbers: 80000, not "$80,000". Currency goes in the currency field. |
| "Value of 'unitText' must be one of..." | Using "Annual" or "Per Year" instead of the accepted enum | Use uppercase enum: HOUR, WEEK, MONTH, or YEAR. |
| "Unparsable structured data" | Invalid JSON — usually a trailing comma or missing quote | Validate your JSON at jsonlint.com first, then re-test in Rich Results Test. |
Do's and Don'ts: Google's Content Policies
Google explicitly penalizes schema that doesn't match the visible page content, contains misleading information, or tries to manipulate the system. These do's and don'ts are drawn directly from Google's job posting guidelines.
- Use the actual job title — no keyword stuffing ("Developer (Great Pay! Remote!)")
- Match the salary in schema exactly to what's shown on the page
- Set a real
validThroughdate and remove expired listings - Write a genuine description with duties, requirements, and company info
- Use
directApply: trueonly when users actually apply on your page - Keep
datePostedas the original posting date — don't refresh it to game freshness - Ensure the job listing page is publicly accessible (no login required)
- Promote unrelated products or content in the job description
- Show a salary range in schema but hide it behind a click or login on the page
- Include hate speech, discriminatory language, or illegal requirements
- Use
validThroughdates far in the future to keep old listings visible - Add schema to pages that don't actually contain a job listing
- Copy-paste a description that's identical across many listings
- Block Googlebot from crawling your job listing pages
Frequently Asked Questions
jobLocationType: TELECOMMUTE for remote roles). Missing any one of these prevents the rich result entirely. Treating datePosted and validThrough as required is also strongly advised.wp_head hook as shown in the code example above.baseSalary property with a MonetaryAmount type containing: a currency (ISO 4217 code like USD, GBP, EUR), and a value of type QuantitativeValue with minValue, maxValue (both plain numbers — no $ symbols or commas), and unitText using one of: HOUR, WEEK, MONTH, or YEAR.jobLocation is a Place type with a PostalAddress specifying the physical office or work location. jobLocationType is used for remote jobs and must be set to TELECOMMUTE — it's the only accepted value. For hybrid roles, include both. For fully remote with geographic restrictions, use jobLocationType: TELECOMMUTE plus applicantLocationRequirements with a Country, State, or City type.validThrough date and remove or unpublish listings when the role is filled. Google actively removes expired listings from Google Jobs and penalises domains with consistently stale postings. Do not artificially refresh datePosted to game freshness signals.Official Resources & Tools
Use these authoritative tools and references to validate, monitor, and deepen your understanding of JobPosting schema.
Validate your JobPosting schema instantly. Test by URL or paste raw code. Shows errors, warnings, and preview.
Monitor job posting enhancements at scale. Shows valid count, errors, and which URLs are affected.
The full, authoritative property reference for the JobPosting type including all sub-types and examples.
Google's official developer documentation for job postings — required fields, content policies, and Search Console guidance.
Validates schema against the full schema.org spec (broader than Google's rich results checker — useful for completeness).
Check your JSON-LD for syntax errors before testing. Catches missing commas, unclosed brackets, and bad escapes.
WordPress job board plugin with native 2026-compliant JobPosting schema, salary fields, remote job support, and directApply integration.
More structured data guides: HowTo, FAQPage, Product, and LocalBusiness schema — all for WordPress practitioners.
Ready to Rank in Google Jobs?
The WPNova Job Board Plugin handles all JobPosting schema automatically — including salary, remote jobs, validThrough, and directApply. Zero manual coding required.