A job posting, to Google Jobs, is one open role on its own page. Category pages listing several roles do not qualify — the markup describes a single JobPosting, and the reward is a listing in the jobs experience that sits above ordinary results for employment searches.

What is required — five properties, more than any other rich result

  • title — the name of the role, and only that. "Senior Accountant" is a title; "Senior Accountant – ₹8LPA – Apply Now!!" is a listing that gets filtered.
  • description — the full role description, in HTML. It must match what the page shows; a markup description longer than the visible one is a mismatch review looks for.
  • datePosted — ISO 8601. When the role was posted, not when the page was generated.
  • hiringOrganization — an Organization with a name, ideally with sameAs pointing at the employer's site. For agencies, this is the employer, not the agency.
  • jobLocation — a Place with a structured address. City and country minimum; the more structure, the better the listing is matched to searchers.

validThrough: recommended by label, required by consequence

Google's documentation lists validThrough — when the posting expires — as recommended. Treat it as required. Without it there is no way to tell a filled role from an open one, and a listing that outlives the vacancy is the commonest reason a jobs feed loses trust. When a role is filled early, remove the page or update the markup; Google acts against sites whose postings are routinely stale.

Remote roles have their own shape

A fully remote role declares jobLocationType: "TELECOMMUTE" and applicantLocationRequirements — the countries or regions the hire may work from — instead of relying on a physical address. A remote role carrying only an office address describes the wrong job.

Also worth declaring

  • baseSalary — a MonetaryAmount with currency and a value or range. Listings with salary information are materially more visible.
  • employmentType — FULL_TIME, PART_TIME, CONTRACTOR and friends.
  • identifier — your own reference for the role, so the same job across several feeds is recognised as one job.
  • directApply — whether applying completes on this page rather than bouncing the candidate to a third portal.

An example

{
  "@context": "https://schema.org",
  "@type": "JobPosting",
  "title": "Senior Laravel Developer",
  "description": "<p>We are hiring a senior developer to…</p>",
  "datePosted": "2026-08-18",
  "validThrough": "2026-09-30T23:59:59+05:30",
  "hiringOrganization": {"@type": "Organization", "name": "Example Ltd",
                         "sameAs": "https://example.com"},
  "jobLocation": {"@type": "Place",
                  "address": {"@type": "PostalAddress",
                              "addressLocality": "Coimbatore",
                              "addressRegion": "TN",
                              "addressCountry": "IN"}},
  "baseSalary": {"@type": "MonetaryAmount", "currency": "INR",
                 "value": {"@type": "QuantitativeValue",
                           "minValue": 1200000, "maxValue": 1800000,
                           "unitText": "YEAR"}},
  "employmentType": "FULL_TIME"
}

The mistakes that get feeds removed

Expired roles left live. The one that costs whole sites their jobs presence, not just one listing.

Marking up a list page. One posting, one page. The markup on a search results page of twenty roles describes none of them.

A title that is not a job title. Salary, location, "urgent" and exclamation marks belong in their own properties or nowhere.

Checking your page

Validate the block with the structured data validator, then run the complete page audit — a posting also needs to be indexable and fetchable to appear in Google Jobs at all, and those are page-level questions the markup cannot answer for itself.