SchemaValid
ENDE
Structured Data

Job Posting Schema Markup: What Google for Jobs Actually Requires

Published: 7 min read
Contents

A job posting can pass every validator, carry a perfect @type: JobPosting block, and still never show up in Google's job search results — or worse, show up for months after the role was filled. Google's own structured data documentation, last updated September 8, 2026, is specific about why: five properties get you eligible, one easy-to-skip property decides whether the listing behaves once it's live, and a separate list of policy reasons can pull it regardless of how clean the markup is.

The five properties that decide eligibility

Google's JobPosting structured data documentation lists a short required set, and it's stricter about what each one should not contain than most people expect. title means the job title alone — not the location, not a req code, not the salary bolted on to grab attention. hiringOrganization names the employer, nested as its own Organization object with at least a name, not folded into the job title as plain text.

PropertyStatusWhat it holds
titleRequiredJob title only
descriptionRequiredFull job details, HTML formatting allowed
datePostedRequiredISO 8601 publish date
hiringOrganizationRequiredNested Organization object with at least a name
jobLocation or jobLocationTypeRequired (one of the two)A physical address, or TELECOMMUTE for a fully remote role

That last row is the one people trip over. A remote-only posting doesn't need a fake office address — set jobLocationType to TELECOMMUTE instead and skip jobLocation entirely. Publishing an address for a role nobody actually works from is worse than publishing none, because it's the kind of mismatch between markup and page content that shows up later in Google's removal reasons.

Why validThrough outranks every required field in practice

Here's the part the required/recommended split doesn't communicate well: validThrough sits in the recommended column, but Google's own wording about it reads like a requirement with the label filed off.

Google's own line on this

"We don't allow expired job postings. Ideally you should remove expired job postings from your website." A listing without validThrough carries no expiration signal at all — which doesn't mean Google leaves it up forever, but it does mean you've handed over that decision instead of making it yourself.

Google gives three ways to retire a posting once it's filled. Set validThrough to a past date. Delete the page outright, so it returns a 404 or 410. Or strip the structured data while leaving the page live. All three work. Doing none of them is how a role that closed in March is still generating applications from confused candidates in September — not a hypothetical, just what "no expiration signal" looks like from the applicant's side.

employmentType and baseSalary are both listed as recommended, not required, which undersells how much they change what a job seeker sees. employmentType accepts a fixed, case-sensitive set of values — get the casing wrong and the property is silently ignored rather than flagged.

Accepted employmentType values
FULL_TIME
PART_TIME
CONTRACTOR
TEMPORARY
INTERN
VOLUNTEER
PER_DIEM
OTHER

PER_DIEM made the list. INTERNSHIP didn't — the value is INTERN. Schema.org's enum wasn't written with a copywriter in the room, and a value that reads perfectly reasonably in English can still fail to match anything Google's parser recognizes.

baseSalary is the property worth treating as required even though it technically isn't. Google's guidance is direct here: any figure entered has to reflect what the employer actually pays. No ranges pulled from a market-rate estimate. No "up to" numbers designed to look better than the real offer. That constraint is probably why so many postings leave the field out rather than get it wrong.

Leaving it out is still the worse call. Pay is one of the first filters a job seeker applies, and the same principle showed up already when we covered Product schema's price field: a property the spec marks optional can still be the one thing that decides whether a listing performs, not just whether it validates. A posting without a salary competes at a disadvantage against every listing next to it that has one.

The removal reasons that have nothing to do with your JSON-LD

A posting can be structurally flawless and still get pulled, because Google's policy list for job postings covers the page's actual content, not just the markup describing it. The reasons on record: incomplete job descriptions, listings for roles that don't exist or are misrepresented, no way to actually apply, page content that contradicts what the schema claims, profanity, and keyword-stuffed titles.

That last one has a concrete before-and-after in Google's own guidance.

Same role, two titles, one gets flagged

"Apply now for IT job—FRENCH speaker in Bucharest" gets flagged. "Market Specialist, French speaker" doesn't. Same role, same requirement — the difference is entirely about whether the title reads like a job title or like an ad written to win a search results page. If your listings still read like a 2009 Craigslist post, that's not a schema problem. The validator will pass it every time.

Job Posting Schema GeneratorGenerate Job Posting JSON-LD to get your job openings into Google for Jobs.Open the tool

Checking a listing before it goes live

None of this shows up as a validation error. A JobPosting block missing validThrough, using INTERNSHIP instead of INTERN, or sitting on a page with a vague, three-sentence description will still parse as valid JSON-LD. Validity and eligibility are two different checks, and only one of them is about syntax.

Run the finished markup through our free Schema Validator to catch structural errors first. Then read back through the property table above for the things a syntax check can't see: the enum values, the salary honesty rule, and whether validThrough is set to anything at all.

Schema Markup ValidatorValidate a live URL or JSON-LD code: SEO score, errors, warnings and rich-results readiness.Open the tool

Frequently asked questions

Is validThrough actually required for JobPosting schema?
Not in Google's required-property list, but functionally close to it. Google states it doesn't allow expired job postings, and without validThrough a listing carries no expiration signal — so a filled role can keep appearing with no automatic cutoff.
Can I use a fake office address for a fully remote job?
No, and you don't need to. Set jobLocationType to TELECOMMUTE instead of supplying jobLocation. Publishing an address for a role with no physical location creates a mismatch between the markup and the actual job, which is one of the things that gets postings removed.
What happens if I use INTERNSHIP instead of INTERN for employmentType?
The value won't match Google's accepted enum, so it's effectively ignored rather than flagged as an error. The accepted values are FULL_TIME, PART_TIME, CONTRACTOR, TEMPORARY, INTERN, VOLUNTEER, PER_DIEM, and OTHER — spelled and cased exactly that way.
Do I have to publish an exact salary in baseSalary?
If you include it, yes — Google's guidance requires the figure to reflect what the employer actually offers, not an estimated market rate. baseSalary itself stays optional, but leaving it out puts a listing at a disadvantage against competing postings that do show pay.
Why would Google remove a job posting that passes a structured data validator?
Validators check whether the JSON-LD is syntactically correct, not whether the posting follows Google's job posting policies. Incomplete descriptions, missing application methods, content that contradicts the markup, and keyword-stuffed titles are all policy violations a validator has no way to catch.
What's the fastest way to remove an expired job posting from Google for Jobs?
Google lists three options that all work: set validThrough to a date in the past, delete the page so it returns a 404 or 410, or remove the structured data while leaving the page itself live.

Related articles