Table of contents
Google for Jobs appears for over 60% of job search queries in Vietnam. This box appears just below the search bar with the company logo, position name, salary, and address — occupying the entire top section of the results page. Businesses without JobPosting schema simply do not appear here, even if their job listing page has been indexed.
With vs. Without JobPosting Schema
| Without schema | With full JobPosting schema |
|---|---|
| Only appears in organic results (if indexed) | Appears in Google for Jobs carousel |
| No logo, salary, or address on SERP | Displays logo + salary + location + Apply button |
| AI does not recognize this as a job listing | AI summarizes requirements and benefits when asked |
| Competes equally with all web pages | Filtered into the group of employers with schema |
| Cannot track through Employer Center | Trackable through Google Search Console |
Complete JSON-LD JobPosting
{
"@context": "https://schema.org",
"@type": "JobPosting",
"@id": "https://example.vn/vi/tuyen-dung/senior-digital-marketing-manager-hcm",
"title": "Senior Digital Marketing Manager",
"description": "Chúng tôi tìm kiếm Senior Digital Marketing Manager để dẫn dắt đội ngũ 5 người, quản lý ngân sách marketing 500 triệu/tháng và xây dựng chiến lược omnichannel cho thương hiệu FMCG hàng đầu tại Việt Nam.\n\nCông việc hàng ngày:\n- Lập kế hoạch và triển khai chiến dịch Google Ads, Meta Ads, TikTok Ads\n- Phân tích dữ liệu GA4, Meta Business Suite và báo cáo ROI hàng tuần\n- Quản lý và mentoring team marketing 5 người\n- Phối hợp với product team và creative team cho các campaign lớn\n- Tối ưu landing page và conversion funnel\n\nYêu cầu:\n- 4+ năm kinh nghiệm performance marketing\n- Thành thạo Google Ads, Meta Ads, TikTok for Business\n- Kinh nghiệm quản lý budget từ 200 triệu/tháng trở lên\n- Google Ads Certification là lợi thế\n\nQuyền lợi:\n- Lương cạnh tranh 30-45 triệu/tháng\n- Thưởng KPI hàng quý\n- Bảo hiểm sức khỏe cao cấp cho nhân viên và gia đình\n- Laptop MacBook được cung cấp\n- Work from home 2 ngày/tuần",
"datePosted": "2026-08-11",
"validThrough": "2026-09-11T23:59:59+07:00",
"employmentType": ["FULL_TIME"],
"hiringOrganization": {
"@type": "Organization",
"@id": "https://example.vn",
"name": "Công ty TNHH ABC Vietnam",
"url": "https://example.vn",
"logo": "https://example.vn/images/logo.png",
"sameAs": [
"https://www.linkedin.com/company/abc-vietnam",
"https://www.facebook.com/abcvietnam"
]
},
"jobLocation": {
"@type": "Place",
"address": {
"@type": "PostalAddress",
"streetAddress": "15 Lê Thánh Tôn, Bến Nghé",
"addressLocality": "Quận 1",
"addressRegion": "Hồ Chí Minh",
"addressCountry": "VN",
"postalCode": "700000"
}
},
"baseSalary": {
"@type": "MonetaryAmount",
"currency": "VND",
"value": {
"@type": "QuantitativeValue",
"minValue": 30000000,
"maxValue": 45000000,
"unitText": "MONTH"
}
},
"experienceRequirements": {
"@type": "OccupationalExperienceRequirements",
"monthsOfExperience": 48
},
"educationalRequirements": {
"@type": "EducationalOccupationalCredential",
"credentialCategory": "bachelor degree"
},
"skills": "Google Ads, Meta Ads, TikTok Ads, Google Analytics 4, Performance Marketing, Budget Management",
"industry": "Marketing và Quảng cáo",
"occupationalCategory": "11-2021.00",
"applicationContact": {
"@type": "ContactPoint",
"email": "careers@example.vn",
"contactType": "HR"
},
"directApply": true
}
Remote Job
{
"@context": "https://schema.org",
"@type": "JobPosting",
"title": "Senior Full-Stack Developer (Remote)",
"jobLocationType": "TELECOMMUTE",
"applicantLocationRequirements": {
"@type": "Country",
"name": "Vietnam"
},
"datePosted": "2026-08-11",
"validThrough": "2026-09-11",
"hiringOrganization": {
"@type": "Organization",
"name": "TechStartup VN",
"url": "https://techstartup.vn"
},
"baseSalary": {
"@type": "MonetaryAmount",
"currency": "USD",
"value": {
"@type": "QuantitativeValue",
"minValue": 2000,
"maxValue": 3500,
"unitText": "MONTH"
}
},
"description": "..."
}
Next.js Implementation for Multiple Positions
// app/[lang]/tuyen-dung/[slug]/page.tsx
import type { WithContext, JobPosting } from "schema-dts";
export default async function JobPage({ params }) {
const job = await getJob(params.slug);
const jsonLd: WithContext<JobPosting> = {
"@context": "https://schema.org",
"@type": "JobPosting",
title: job.title,
description: job.fullDescription,
datePosted: job.postedAt.toISOString().split("T")[0],
validThrough: job.expiresAt.toISOString(),
employmentType: job.type === "full-time" ? "FULL_TIME" : "PART_TIME",
hiringOrganization: {
"@type": "Organization",
name: "Công ty ABC",
url: "https://example.vn",
logo: "https://example.vn/logo.png",
},
jobLocation: {
"@type": "Place",
address: {
"@type": "PostalAddress",
streetAddress: job.address,
addressLocality: job.district,
addressRegion: job.city,
addressCountry: "VN",
},
},
...(job.salaryMin && {
baseSalary: {
"@type": "MonetaryAmount",
currency: "VND",
value: {
"@type": "QuantitativeValue",
minValue: job.salaryMin,
maxValue: job.salaryMax,
unitText: "MONTH",
},
},
}),
...(job.isRemote && { jobLocationType: "TELECOMMUTE" }),
directApply: true,
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(jsonLd).replace(/</g, "\\u003c"),
}}
/>
{/* Job page content */}
</>
);
}
Common JobPosting Errors
| Error | Consequence | Fix |
|---|---|---|
| description too short | Google for Jobs rejection | Minimum 200 words with specific requirements and benefits |
| No datePosted | Ranked lower | Always set the actual posting date |
| Expired listing without removing schema | Search Console errors | Set validThrough, remove schema after deadline |
| Salary declared incorrectly (VND as a small number instead of full value) | Incorrect display on Google for Jobs | Use full integer VND: 25000000 not 25 |
| jobLocation missing addressCountry | Invalid schema | Always include addressCountry: "VN" |
| directApply: true but no apply URL | Users cannot apply | Add applicationContact or apply URL |
JobPosting schema is one of the few schemas with directly measurable ROI — number of applicants, CV quality, and recruitment costs compared to job boards like TopCV or VietnamWorks. Businesses that post directly on their website with complete schema can reduce dependence on job boards by 40-60%.
Frequently asked questions
What is Google for Jobs and how does JobPosting schema help?
Google for Jobs is a feature that appears when users search for employment (e.g., 'marketing jobs hanoi', 'software engineer jobs ho chi minh city'). Google displays a special box at the top of the SERP with a list of positions, company logos, salary, location, and an apply button. JobPosting schema is a mandatory requirement to appear in this box — without schema, a job listing is not filtered into Google for Jobs even if the page is indexed. Schema also helps AI summarize job requirements when candidates ask AI about career opportunities.
Which fields are required in JobPosting schema?
Google requires at minimum: (1) title — position name, (2) description — a sufficiently detailed description of the role and requirements (not a tagline), (3) hiringOrganization — company name and url, (4) jobLocation — complete address (or applicantLocationRequirements for remote), (5) datePosted — posting date. Recommended additions: baseSalary (specific salary significantly increases CTR), validThrough (application deadline), employmentType, experienceRequirements, educationRequirements. Listings without datePosted are considered outdated and deprioritized by Google for Jobs.
How should salary be declared in JobPosting schema?
Use baseSalary with MonetaryAmount: value (a number or range), currency (VND or USD), unitText (MONTH, YEAR, HOUR). Example fixed salary: value: 25000000, currency: VND, unitText: MONTH. Example salary range: use QuantitativeValue with minValue: 20000000, maxValue: 35000000, unitText: MONTH. Google prominently displays the salary in Google for Jobs — listings with specific salaries have 30-40% higher CTR than listings marked 'negotiable'. If salary is genuinely negotiable: omit baseSalary rather than entering 0 or 'negotiable'.
How should remote and hybrid jobs be declared?
Fully remote: add jobLocationType: TELECOMMUTE and applicantLocationRequirements with @type Country, name: Vietnam (or leave empty for global). Hybrid: declare jobLocation normally (office) and add jobLocationType: TELECOMMUTE — Google will display it as hybrid. On-site: only jobLocation is needed, no jobLocationType. Important: if remote is declared but the role is actually on-site, Google for Jobs may reject it after verification.
After a position is filled, does the JobPosting schema need to be removed?
Yes — and this is the most common mistake. When a position has been filled: (1) Add validThrough with a past expiry date, OR (2) Return HTTP 404 for that page, OR (3) Remove the JSON-LD script from the page. An expired job listing with schema still present will be flagged as an error by Google for Jobs and may affect the trustworthiness of the entire domain. With ATS systems: set validThrough automatically using the closing date.
AEO Saigon
An Answer Engine Optimization agency in Ho Chi Minh City — helping business websites get cited by AI. About AEO Saigon →
Want your website to be cited by AI like this?
Free Audit