If you've ever tried to get "rich results" for job listings on Google Search — the ones with the salary, location, and posted-date pills right in the SERP — you've probably run into Google's JobPosting schema docs and immediately wanted to close the tab. It's one of the more verbose schema types out there, with a long list of "recommended" properties that all seem mandatory until you actually read the fine print.
Here's what I learned implementing it across listing pages on a live job board, and why I ended up going with a minimal JobPosting + ItemList combination instead of the full schema Google's examples suggest.
The problem with going "full schema"
The naive approach is to take Google's sample JobPosting JSON-LD and fill in every property: baseSalary, employmentType, jobLocation, hiringOrganization, validThrough, applicantLocationRequirements, jobLocationType... the list goes on.
Two problems show up fast when you try this on real listing data:
Data completeness. Not every job posting has a clean, structured salary. A lot of listings say things like "Salary: Best in Industry" or "Negotiable" — which isn't a valid baseSalary value, and stuffing a fake number in there to satisfy the schema is a fast way to get flagged for spam or misleading markup.
Maintenance overhead. Every optional property is a property you now have to keep accurate. validThrough in particular is unforgiving — if a listing expires and you don't update or remove the markup, Google will eventually stop trusting your JobPosting data altogether across the whole domain.
What I went with instead
For each listing page, I output:
One ItemList at the page level, listing all jobs shown on that page (this maps naturally to how Google wants paginated listing pages structured)
A minimal JobPosting block per job — only the properties I could guarantee were accurate at time of publish and would stay accurate without manual upkeep
json{
"@context": "https://schema.org",
"@type": "ItemList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"url": "https://example.com/jobs/senior-backend-developer-ahmedabad"
},
{
"@type": "ListItem",
"position": 2,
"url": "https://example.com/jobs/hr-executive-rajkot"
}
]
}
And on each individual job page, something closer to this:
json{
"@context": "https://schema.org",
"@type": "JobPosting",
"title": "Senior Backend Developer",
"description": "...",
"datePosted": "2026-06-15",
"hiringOrganization": {
"@type": "Organization",
"name": "Example Pvt Ltd",
"sameAs": "https://example.com"
},
"jobLocation": {
"@type": "Place",
"address": {
"@type": "PostalAddress",
"addressLocality": "Ahmedabad",
"addressRegion": "Gujarat",
"addressCountry": "IN"
}
},
"employmentType": "FULL_TIME"
}
Notice what's missing: no baseSalary, no validThrough. I only added those two properties on listings where the source data was actually structured (a real numeric salary range, a real expiry date) rather than backfilling them everywhere for "completeness."
What broke, and what Google actually indexed
A few things I didn't expect going in:
Rich results didn't show up immediately, even on valid pages. Google Search Console's "Enhancement" reports lag behind actual crawling by days, sometimes longer. Don't panic-debug valid markup just because GSC hasn't caught up yet.
Listings without validThrough still got indexed, but Google's docs are explicit that missing it may shorten how long a posting is eligible for the job-specific rich result, worth knowing before you decide to omit it broadly like I did.
Duplicate or thin description fields caused more validation warnings than any structural schema error. The schema was syntactically fine; Google just didn't think the underlying content was worth surfacing. Rich snippets can't fix thin content.
The ItemList at the page level had zero impact on individual job rich results — it's a separate signal, mainly useful for how Google understands the listing/collection page itself, not the jobs within it.
The takeaway
If you're adding JobPosting schema to a board with real, imperfect data (which is most of them), resist the urge to fill in every optional property Google's documentation shows in the example. A smaller set of properties you can actually keep accurate will outperform a "complete" schema block full of soft-filled or stale data over time, both for trust with Google and for your own sanity maintaining it.
I implemented this on JobGrin, an Indian job portal, happy to answer questions if you're working through the same thing.
Top comments (0)