AEO Saigon

JobPosting Schema: Job Listings in Google for Jobs and AI Search

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 schemaWith full JobPosting schema
Only appears in organic results (if indexed)Appears in Google for Jobs carousel
No logo, salary, or address on SERPDisplays logo + salary + location + Apply button
AI does not recognize this as a job listingAI summarizes requirements and benefits when asked
Competes equally with all web pagesFiltered into the group of employers with schema
Cannot track through Employer CenterTrackable 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

ErrorConsequenceFix
description too shortGoogle for Jobs rejectionMinimum 200 words with specific requirements and benefits
No datePostedRanked lowerAlways set the actual posting date
Expired listing without removing schemaSearch Console errorsSet validThrough, remove schema after deadline
Salary declared incorrectly (VND as a small number instead of full value)Incorrect display on Google for JobsUse full integer VND: 25000000 not 25
jobLocation missing addressCountryInvalid schemaAlways include addressCountry: "VN"
directApply: true but no apply URLUsers cannot applyAdd 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