JobPosting schema markup: example, required fields and WordPress setup
JobPosting schema describes one open role on its own job detail page: the title, full description, employer, location, posting date and, when you have them, salary and an expiry date. Google requires title, description, datePosted, hiringOrganization and jobLocation, with a separate set of fields for fully remote jobs.
Job markup has stricter rules than most types. The job must be real and open, the page must let people apply, and the markup must come off (or validThrough must pass) once the role is filled. Google also recommends notifying it through the Indexing API whenever a job page is added or removed, since job listings go stale fast.
JSON-LD example
Copy this into a <script type="application/ld+json"> tag and replace the values with your own.
{
"@context": "https://schema.org",
"@type": "JobPosting",
"title": "Senior Line Cook",
"description": "<p>Harbor Lane Kitchen is hiring a senior line cook to run the grill station during dinner service.</p><ul><li>3+ years of line experience</li><li>Evenings and weekends</li></ul>",
"identifier": {
"@type": "PropertyValue",
"name": "Harbor Lane Kitchen",
"value": "HLK-0412"
},
"datePosted": "2026-09-22",
"validThrough": "2026-10-31T23:59:00-04:00",
"employmentType": "FULL_TIME",
"directApply": true,
"hiringOrganization": {
"@type": "Organization",
"name": "Harbor Lane Kitchen",
"sameAs": "https://example.com",
"logo": "https://example.com/images/logo.png"
},
"jobLocation": {
"@type": "Place",
"address": {
"@type": "PostalAddress",
"streetAddress": "14 Harbor Lane",
"addressLocality": "Portland",
"addressRegion": "ME",
"postalCode": "04101",
"addressCountry": "US"
}
},
"baseSalary": {
"@type": "MonetaryAmount",
"currency": "USD",
"value": {
"@type": "QuantitativeValue",
"minValue": 24,
"maxValue": 29,
"unitText": "HOUR"
}
}
}
Properties
| Property | Status | What it is |
|---|---|---|
title | Required | The job title only, such as "Senior Line Cook". No salary, codes or "Apply now". |
description | Required | The full job description in HTML, matching what the page shows. |
datePosted | Required | The date the job was first posted, in ISO 8601. |
hiringOrganization | Required | The employer as an Organization with name, and ideally sameAs and logo. |
jobLocation | Required | A Place with a PostalAddress. Not required for fully remote jobs that use jobLocationType. |
validThrough | Recommended | When the posting expires, in ISO 8601. Required by Google if the job has an expiration date. |
baseSalary | Recommended | A MonetaryAmount with currency and a QuantitativeValue (value or minValue/maxValue, unitText such as HOUR or YEAR). |
employmentType | Recommended | FULL_TIME, PART_TIME, CONTRACTOR, TEMPORARY, INTERN, VOLUNTEER, PER_DIEM or OTHER. |
jobLocationType | Recommended | Set to TELECOMMUTE for fully remote jobs. |
applicantLocationRequirements | Recommended | Where remote applicants must be located, such as a Country or State. Needed with TELECOMMUTE. |
directApply | Recommended | true if the candidate can apply on this page without extra steps. |
identifier | Recommended | The employer's internal ID for the job. |
When to use it
Use JobPosting on a page that describes one job and lets people apply for it: a careers page entry, a job board detail page, an ATS-hosted listing on your domain.
Don't use it on:
- Job listing or search result pages that show many jobs. Only the detail page gets markup.
- Evergreen "we're always hiring" pages with no specific open role.
- Training courses, franchise offers or anything that charges the applicant. These are not job postings under Google's guidelines.
For remote roles, set jobLocationType to TELECOMMUTE and add applicantLocationRequirements. If the job is hybrid or has an office people can work from, include jobLocation too.
validThrough and the Indexing API
Expired jobs with live markup are the biggest problem on job sites. When a role closes, do one of these:
- Remove the JobPosting markup from the page.
- Remove the page, returning a 404 or 410.
- Make sure
validThroughis in the past.
Then tell Google. The Indexing API lets you notify Google directly when a job page is published or removed, so it crawls the change quickly instead of waiting for normal recrawling. Google supports the Indexing API only for pages with JobPosting or livestream (BroadcastEvent inside VideoObject) markup, and it requires a Google Cloud project and a service account verified as an owner in Search Console. A sitemap with accurate lastmod dates is the fallback.
Add it in WordPress
By hand. If each job is its own post or custom post type, print the JSON-LD from wp_head using the job's fields. Many job board plugins already output JobPosting, so check the page source first to avoid duplicates.
With Hydrogen SEO. JobPosting is not among the schema types Hydrogen SEO's docs list for the Schema screen or SEO Settings sheet. Developers can append a JobPosting block with the hydrogen_seo_schemas filter, which receives the full list of schema objects before they print.
One-off markup. The free schema generator in the Hydrogen SEO app at /seo/schema-generator builds JobPosting JSON-LD with employer, location, salary and validThrough from a short form.
Common mistakes
- Salary or location in the title. Keep
titleto the job title. - A description that differs from the page. The markup must match the visible posting.
- Leaving expired jobs marked up. This can lead Google to ignore your job markup.
- Remote jobs with no applicant location. Pair TELECOMMUTE with applicantLocationRequirements.
- Salary as free text. Use MonetaryAmount with a QuantitativeValue and a unitText.
See invalid structured data for help reading errors.
Test it
Paste your draft JobPosting markup into the JSON-LD validator to catch missing properties early; the published page still needs a pass through Google's Rich Results Test. Search Console has a job posting report that lists errors and warnings across all job pages. Check it after any template change, because one bad field in a template breaks every listing at once.
Common questions
Is baseSalary required?
No, it is recommended. Listings with salary information give job seekers more to go on, and some regions have their own pay transparency laws.
How do I mark up a fully remote job?
Set jobLocationType to TELECOMMUTE and add applicantLocationRequirements with the countries or states you can hire from. jobLocation is then optional.
Do I have to use the Indexing API?
No, but Google recommends it for job pages because it gets new and removed jobs crawled faster than a sitemap alone.
Can I mark up jobs on a listing page with many roles?
No. JobPosting markup belongs on each job's detail page, not on pages that list several jobs.