
What JobPosting schema is and where it shows up
JobPosting is the schema.org type that describes a single open role: what the job is, who is hiring, where it is, what it pays, and when the listing expires. Google reads it to populate its job search experience, the prominent jobs module that appears when someone searches "marketing manager jobs near me" or similar. That interface aggregates listings from career sites, ATS platforms, and job boards, and the entry ticket is valid JobPosting markup on an indexable page.
Here is the part that stings if you run a careers section without it: the jobs module sits above classic organic results for hiring queries. Your beautifully written job page can rank number one organically and still sit below a stack of Indeed and LinkedIn entries pulled into that module, because they have the markup and you do not. I have seen mid-size companies get more qualified applicants from fixing schema on 40 job pages than from a quarter of paid job board spend. Not a promise, but a pattern.
One structural rule people miss: the markup belongs on the individual job detail page, one JobPosting per canonical URL. Google explicitly does not want the schema on list pages that show multiple postings, and marking up a category page with an array of jobs gets you nothing.
The properties that actually matter
Google's documentation splits properties into required and recommended, and the difference has teeth. Miss a required property and the page is ineligible. Skimp on recommended ones, especially salary and remote attributes, and you lose filters and placement inside the jobs interface.
| Property | Status | Practitioner notes |
|---|---|---|
title | Required | The job title only. "Senior Data Engineer", not "Senior Data Engineer!! Remote!! Apply NOW". Stuffed titles are a documented reason for removal. |
description | Required | Full HTML description of the role. Must match what is on the page, not a teaser sentence. |
datePosted | Required | ISO 8601 date. Do not fake-refresh it to look recent; reposting games get noticed. |
hiringOrganization | Required | The employer, with name and ideally logo and sameAs URL. Staffing agencies list themselves, not the end client, unless disclosed. |
jobLocation | Required* | A Place with a real postal address. For remote roles, swap in jobLocationType: "TELECOMMUTE" plus applicantLocationRequirements. |
validThrough | Strongly recommended | Expiry datetime. Required in practice for expiration handling; leaving filled jobs live past this is the fastest route to a manual action. |
baseSalary | Recommended | MonetaryAmount with currency and unitText. Feeds the salary filter; several US states and EU pay transparency rules push you here anyway. |
employmentType | Recommended | FULL_TIME, PART_TIME, CONTRACTOR and friends. Cheap to add, powers filters. |
directApply | Recommended | Boolean signaling the user can complete an application on your page rather than bouncing through redirects. |
How a job page gets into the jobs experience
How to detect the gap
- Rich Results Test: paste a live job URL. It reports whether JobPosting was detected and lists every missing or invalid property with severity.
- Search Console: the Job postings enhancement report under Enhancements shows valid, warning, and error counts across the site, plus which URLs share each problem. If the report does not exist, Google has not detected any markup at all, which is your answer.
- Screaming Frog: enable structured data extraction and validation in the crawl config, crawl the careers section, and filter the Structured Data tab for missing JobPosting on URLs matching your job page pattern. This catches template-level gaps at scale.
- Server logs: if Googlebot rarely hits new job URLs before they expire, markup alone will not save you. Crawl frequency is the other half of this problem.
How to implement it properly
- Generate JSON-LD server-side per job page from the same database record that renders the visible content. Hand-written schema drifts out of sync with reality within a month; templated schema cannot.
- Cover the required set first: title, description, datePosted, hiringOrganization, and jobLocation (or TELECOMMUTE plus applicantLocationRequirements for remote roles).
- Add validThrough and baseSalary. Salary ranges are fine using minValue and maxValue. If your ATS does not expose salary, escalate it; the listing competes against ones that show pay.
- Wire up expiration handling. The moment a role is filled or expired: either return 404 or 410, or keep the page and ensure validThrough is in the past. Advertising dead jobs violates the guidelines and Google has issued manual actions for exactly this.
- Use the Indexing API for job URLs. This is one of the two official use cases (job postings and livestream events). Ping URL_UPDATED on publish and URL_DELETED on close, so short-lived postings actually spend their lifespan indexed.
- Validate, then monitor. Rich Results Test on the template, then watch the Search Console enhancement report weekly. ATS template updates have a talent for silently breaking one property.
DO and DON'T
- Put one JobPosting per indexable job detail page, generated from live data
- Include salary, employmentType, and honest remote attributes
- Expire markup and page together the day a role closes
- Use the Indexing API for publish and delete events
- Keep the schema description in sync with the visible posting
- Mark up list or category pages with multiple JobPosting objects
- Stuff titles with locations, salaries, or ALL CAPS bait
- Leave filled jobs live with a future validThrough
- Mark up roles you are not actually hiring for to farm traffic
- Block job pages behind login or noindex and expect the module to find them
FAQ
Do I still need this if our jobs are already on Indeed and LinkedIn?
How do I mark up a fully remote position?
jobLocationType to TELECOMMUTE and add applicantLocationRequirements naming the countries or states you can hire in. If the role is remote but restricted, say remote US only, the location requirements property is what communicates that. Only include jobLocation as well if there is a genuine office option.Why does Search Console show valid items but my jobs still do not appear?
Can staffing agencies and job boards use JobPosting markup?
JSON-LD or microdata for this?
I validate structured data eligibility, indexing coverage, and crawl behavior across your whole site in an Advanced SEO Audit, with a fix list your developers can execute in order.
Claude Vincent is a technical SEO consultant focused on crawlability, rendering, and AI-search visibility. He writes the field guides and case studies at SEO ProCheck, with a bias toward the durable, unglamorous work that decides whether search engines and AI answer engines can actually read and cite a site.
About SEO ProCheck
Technical SEO consulting and GEO strategy with 20 years of enterprise experience. Case studies, resources, and tools for search and AI visibility.
Work With Me
Technical SEO audits, GEO strategy, site migrations, and international SEO. Hourly consulting for teams who need hands-on support, not just reports.
Subscribe to our newsletter!
Recent Posts
- Can AI Crawlers Actually Read Your Site? I Measured 400 of the Biggest September 5, 2026
- The Pre-Publish Quality Gate for AI-Assisted Content August 6, 2026
- AGENTS.md vs llms.txt vs llms-full.txt: Which Agent File Does What July 18, 2026







