DEV Community

Cover image for Implementing JobPosting Schema for a Job Board: What I Learned
sandy S.
sandy S.

Posted on

Implementing JobPosting Schema for a Job Board: What I Learned

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"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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)