Job Posting Schema for Google for Jobs: The Complete Guide
JobPosting schema is one of the highest-leverage things you can add to a careers page: it's what makes your listings eligible for Google's dedicated job search experience, the filterable, high-intent interface that dominates the results for job-related queries, the same class of high-intent schema as Service markup for people ready to act. Get the markup right and your opening shows up with title, company, location, and salary, filterable by job seekers actively looking for exactly what you're offering. Get it wrong, or let it go stale, and you risk something worse than no rich result: a manual action.
This guide covers the required and recommended fields, salary and remote-job markup, the content policies Google actually enforces, and the part most guides underweight: what happens to your eligibility after the job closes. That last piece is where most job boards and career sites quietly get into trouble, and it's the section worth reading most carefully.
What does JobPosting schema actually get you?
JobPosting structured data makes an individual job posting eligible for Google's job search experience: a rich, filterable interface where job seekers narrow results by location, job title, and other criteria, and see your company logo, details, and (if you provide it) salary right in the result. It's a real rich result, not a maybe: Google's own documentation frames it as more interactive results, more motivated applicants (people can filter to exactly what they want), and more discovery and click-through than a standard blue link gets.
It's also one of the more heavily policed rich results Google runs. Job listings are time-sensitive by nature: a role gets filled, a posting expires, and Google enforces that directly through content policies with real consequences, which we'll get to. Understanding those policies isn't optional homework here; it's the difference between a career page that stays eligible and one that quietly picks up a manual action.
What are the required fields for JobPosting schema?
Google lists five required properties. Miss any one and the posting isn't eligible for the job search experience at all.
- title: the job title only ("Software Engineer," "Barista"). Not the posting title, not a job code, not the location or salary stuffed in. Google explicitly flags titles like "Apply now for IT job – FRENCH speaker in Bucharest" as not recommended; "Market Specialist, French speaker" is the right shape.
- description: the complete job description in HTML, covering responsibilities, qualifications, skills, hours, and education/experience requirements. It has to be a genuinely complete representation of the job, not a teaser, and it can't just repeat the title. Google's parser recognizes
<p>,<ul>, and<li>for structure; header and inline tags like<h1>or<strong>are safe to include but won't affect formatting. - hiringOrganization: the company doing the hiring, as an Organization, using the company's actual name (not the specific branch or location). If you're posting anonymously on behalf of an employer, a staffing agency, for instance, use the literal value "confidential" for
hiringOrganization.name. - jobLocation: the physical place the employee reports to work, as a Place with a PostalAddress.
addressCountryis required. For fully remote roles, this works differently, covered in its own section below. - datePosted: the original posting date, in ISO 8601 format ("2026-08-01" or with a full timestamp).
A complete, correctly formed example
Here's a well-formed JobPosting block covering the core required and recommended properties:
{
"@context": "https://schema.org/",
"@type": "JobPosting",
"title": "Senior Backend Engineer",
"description": "<p>We're looking for a Senior Backend Engineer to design and maintain the services powering our platform.</p><p>Responsibilities:</p><ul><li>Design and ship backend services in Go and Python</li><li>Own reliability and on-call for core systems</li><li>Mentor mid-level engineers</li></ul><p>Requirements: 5+ years backend experience, familiarity with distributed systems, bachelor's degree or equivalent experience.</p>",
"identifier": {
"@type": "PropertyValue",
"name": "Acme Software Co",
"value": "ENG-2026-114"
},
"datePosted": "2026-08-01",
"validThrough": "2026-10-01T00:00",
"employmentType": "FULL_TIME",
"hiringOrganization": {
"@type": "Organization",
"name": "Acme Software Co",
"sameAs": "https://acmesoftware.example",
"logo": "https://acmesoftware.example/logo.png"
},
"jobLocation": {
"@type": "Place",
"address": {
"@type": "PostalAddress",
"streetAddress": "400 Market Street",
"addressLocality": "Austin",
"addressRegion": "TX",
"postalCode": "78701",
"addressCountry": "US"
}
},
"baseSalary": {
"@type": "MonetaryAmount",
"currency": "USD",
"value": {
"@type": "QuantitativeValue",
"minValue": 140000,
"maxValue": 175000,
"unitText": "YEAR"
}
}
}A few things worth noting. identifier is your own internal job code, kept separate from the human-readable title. validThrough carries the expiration date, which we'll come back to, it's the single most consequential property in this whole guide. And the salary is expressed as a range using minValue/maxValue rather than a single value, which is the correct pattern when you're advertising a band rather than a fixed figure.
Recommended fields: what actually improves your listing
Google is direct about this one: users prefer job postings with explicitly stated salaries over those without, and richer postings rank better within the feature. So treat the recommended properties as close to mandatory in practice.
- baseSalary: the actual base salary the employer is offering, not an estimate. Only employers can supply this (a third-party job board scraping or guessing a figure isn't the intended use). Use
HOUR,DAY,WEEK,MONTH, orYEARforunitText, and useminValue/maxValuefor a range instead of a single value. - employmentType: one or more of
FULL_TIME,PART_TIME,CONTRACTOR,TEMPORARY,INTERN,VOLUNTEER,PER_DIEM, orOTHER. You can list more than one as an array if a role genuinely fits multiple categories. - validThrough: the expiration date. Technically "recommended," but Google's own note says it's required for job postings that have an expiration date, which is effectively every real job posting. Skip this at your peril; we're dedicating an entire section to why below.
- identifier: your internal job/req ID, as a PropertyValue.
- directApply: a boolean flagging whether the posting offers a genuinely short, direct application flow (complete on your site, no more than one sign-in, no unnecessary intermediate steps). Google is still developing how this is used, so don't expect a visible effect yet, but it costs nothing to add accurately.
- applicantLocationRequirements and jobLocationType: for remote roles, covered next.
There's also a beta set, educationRequirements, experienceRequirements, and experienceInPlaceOfEducation, that let you structure minimum education level and months of experience. They're genuinely useful for candidate matching even though Google says their current effect on display isn't guaranteed yet.
How do I mark up a remote or work-from-home job?
This is one of the more commonly botched parts of JobPosting schema, and Google has built a dedicated experience for it worth getting right.
Three properties work together:
- jobLocationType: set to
"TELECOMMUTE"for roles the employee may or must perform remotely 100% of the time. - applicantLocationRequirements: the country or countries an applicant must be located in to be eligible. At least one country is required when the role is fully remote.
- jobLocation: only needed if there's also a physical site associated with the role. If there's genuinely no physical location, omit it.
Fully remote, no physical office, US-only applicants:
"applicantLocationRequirements": {
"@type": "Country",
"name": "USA"
},
"jobLocationType": "TELECOMMUTE"Physical office in Detroit, or remote from anywhere in the US:
"jobLocation": {
"@type": "Place",
"address": {
"@type": "PostalAddress",
"addressLocality": "Detroit",
"addressRegion": "MI",
"addressCountry": "US"
}
},
"jobLocationType": "TELECOMMUTE"The rule that trips people up: TELECOMMUTE means fully remote. Don't apply it to a hybrid role, a job where remote work is a negotiable perk, or "occasional work from home." Google is explicit that these arrangements don't qualify, if the role isn't 100% remote, leave jobLocationType off and describe it through jobLocation alone. Mismarking a hybrid job as fully remote is a misrepresentation issue, not just a technical error.
Why does my JobPosting need validThrough, and what happens if I skip it?
Here's the section that matters most, because it's where most sites, job boards and single-company career pages alike, lose eligibility without realizing why.
Google does not allow expired job postings to remain live with JobPosting markup. If a role closes, you have exactly three legitimate options:
- Set
validThroughto a date in the past. - Remove the page entirely, so it returns a 404 or 410.
- Remove the JobPosting markup from the page while leaving it live.
Fail to do one of these and Google can issue a manual action for "JobPosting structured data on expired job," which doesn't just cost you the rich result on that one posting. A manual action means the structured data on the affected page gets ignored outright, and repeated violations put your broader job-posting eligibility at risk. This is explicitly called out in Google's job posting content policies as something they enforce, not a theoretical edge case.
The trap is subtler than it sounds. It's not just about forgetting to add validThrough in the first place, it's about maintenance over time. A posting that had a correct validThrough date when it launched is only compliant as long as that date, the live status of the page, and the actual hiring status agree. The moment a role is filled and nobody circles back to update the schema, or the date passes and the page silently stays live with a still-future-dated validThrough, you've drifted into violation territory, and Google rechecks this on every crawl. It's the same maintenance failure mode we cover for date-bound listings in our event schema guide: any schema type tied to a real-world expiration only stays compliant if something keeps checking it after launch day.
If a job genuinely has no expiration, which is rare, but happens with evergreen/rolling applications, the correct move is to omit validThrough entirely rather than fake a far-future date to game freshness. Don't include it if you don't know or don't have an expiration; that's explicit in Google's guidance.
This is exactly the kind of ongoing-accuracy problem that hand-maintained markup handles worst: someone updates the "Apply now" button to "Position filled" on the visible page but nobody touches the JSON-LD underneath, and the two drift out of sync for weeks before anyone notices in Search Console. AI Schema Gen generates JobPosting markup from your actual page content, so when a listing's live status changes on the page, the markup reflects it rather than sitting frozen at whatever state it was in on launch day, which is precisely the discipline that keeps job schema out of manual-action territory.
Job posting content policies you need to know
Beyond validThrough, Google enforces a specific set of content rules for job postings, and violating them risks the same manual-action consequences. The ones worth internalizing:
One job per page. JobPosting markup belongs only on a page describing a single job. Never place it on a listing or search-results page showing multiple openings, that's an explicit, commonly-cited violation ("A list page should not include structured data for individual jobs").
The markup must match the visible page. If your schema states a salary, a title, or a location that isn't genuinely reflected on the page a visitor sees, that's a policy violation, the same principle as review-snippet matching, applied here to job content.
No incomplete descriptions. A thin description that doesn't genuinely cover the role is disallowed outright.
No misrepresentation. This covers fake or non-existent jobs (including postings whose real purpose is harvesting applicant data rather than hiring), keyword-stuffed titles, false location data, and postings made on behalf of a company without authorization.
A real way to apply. Career-fair announcements with no actual application path, or postings that require a login just to read the description, are both disallowed. Job seekers must be able to see the full posting without signing in.
No payment required from applicants, and no soliciting resumes for roles you're not actually hiring for.
Basic editorial quality: proper grammar, no all-caps spam, no obstructive ads crowding the actual job content.
These aren't edge cases dreamed up for compliance theater, they map directly onto the messages you'll actually see in Search Console if you slip: "Content on pages found to be different than structured data on the page," "Can't submit application on job offer page," and so on.
Common mistakes to avoid
Letting validThrough drift stale. Covered above in depth because it's the single biggest risk. A filled position with markup that still looks open is a live manual-action liability, not just a missed opportunity.
Marking up a listings/search page instead of individual job pages. Each job gets its own page and its own JobPosting block; a page listing many openings should link out to them, not carry the markup itself.
Stuffing extra information into title. Job codes, locations, salary figures, and marketing language ("Apply now!!") don't belong in title, they belong in their own dedicated properties, and cramming them in risks a spam classification.
Marking TELECOMMUTE for hybrid roles. If the job isn't 100% remote, don't use jobLocationType: TELECOMMUTE. Describe it through jobLocation instead.
Salary in the markup but not on the page. If you state a baseSalary, that figure needs to be genuinely visible to a human reading the posting, not just present in the JSON-LD.
Requiring login to view the description. Google explicitly disallows gating the job description itself behind a sign-in wall.
Guessing at salary as a job board. baseSalary is meant to come from the employer's actual figure, not an estimated or scraped number.
Implementing this on WordPress
Most career pages and job boards on WordPress fall into one of two situations: a dedicated job-listings plugin that may already emit some JobPosting markup, or a hand-built careers section with no structured data at all.
If you're using a jobs plugin, view source on a live posting and check what it actually outputs, many plugins cover the five required fields but skip baseSalary, employmentType, and, critically, don't handle validThrough maintenance as roles close, which is exactly the gap that causes the manual-action risk above.
If you're hand-building it, the recurring challenge is the same one that runs through this whole guide: keeping the markup's live/expired status honest as roles open and close, potentially across dozens or hundreds of postings if you're a larger employer or job board. That's a lot of manual bookkeeping to get consistently right, indefinitely.
This is where generating schema from your actual content earns its keep. AI Schema Gen reads your job posting pages and produces complete JobPosting markup, required fields, salary, employment type, remote-job properties, and because it generates from your live page content rather than a static template written once at launch, a posting that changes status on the page is reflected in the markup rather than frozen at its original state. For a schema type where staleness carries real policy consequences, that automatic sync is the difference between a career page that stays compliant and one that quietly accumulates risk. It also weaves your hiring organization into your broader entity profile, so the company behind your job postings is a clearly established entity elsewhere on your site too, see our entity profile guide for the full picture.
Whatever route you take, the tests are the same: are all five required fields present and accurate; is salary genuinely visible where you claim it in markup; does jobLocationType correctly reflect whether the role is truly 100% remote; and, above everything else, does validThrough (or the page's live status) accurately reflect whether the job is still open, continuously, not just on launch day.
Validating your JobPosting schema
- Google's Rich Results Test confirms eligibility and syntax, and will show you a preview of how the posting appears in the job search experience, including parsed location, useful for catching an incorrect or unrecognized
jobLocationbefore it costs you display quality. - Search Console's job posting rich result report is where you'll see valid-versus-invalid counts across your site, and, importantly, the Manual Actions report, where an expired-job or misrepresentation violation would surface.
- The Schema Markup Validator at validator.schema.org checks broader schema.org syntax validity.
- A manual spot check on the page itself is worth doing periodically: does the salary, title, and location in the markup match what a human visitor genuinely sees? That's the check most likely to catch drift before Google's crawler does.
JobPosting and regional availability
One honest note that's easy to miss: Google's job search experience isn't available everywhere. It currently covers most of Asia, much of Europe, all of Latin America, the Middle East and North Africa, and all of North America and Sub-Saharan Africa, but coverage isn't universal, and it can change (Google updates this list from time to time, alongside the broader policy shifts we track in our running log of structured data changes). If your postings aren't showing up in the feature despite valid markup, checking current regional availability against your hiring locations is worth ruling out before assuming a technical error.
Frequently Asked Questions
Running job postings on WordPress? AI Schema Gen generates complete JobPosting schema from your live listings, required fields, salary, remote-job properties, and keeps it in sync as roles open and close, so your markup doesn't drift into manual-action territory. Start free at aischemagen.com.
Generate perfect schema in 30 seconds
AI Schema Gen handles everything automatically, free to start.
Get Started Free