# VietnamWorks Jobs Scraper (`crawlerbros/vietnamworks-scraper`) Actor

Scrape live job listings from VietnamWorks.com, Vietnam's largest job board. Browse curated trending job feeds, run a real-time keyword search, or fetch full job details from listing URLs. No login, no cookies, no paid proxy required.

- **URL**: https://apify.com/crawlerbros/vietnamworks-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## VietnamWorks Jobs Scraper

Scrape live job listings from [VietnamWorks.com](https://www.vietnamworks.com), Vietnam's largest job board (part of Navigos Group). Browse curated trending job feeds, run a real-time keyword search, or fetch full job details from listing URLs. No login, no cookies, no paid proxy required.

### What this actor does

- **Three modes:** `trending` (curated feeds, most results), `search` (real-time keyword/location/industry search), and `byUrls` (direct listing fetch)
- **6 curated feeds** — best jobs, urgent hiring, featured, part-time, high-salary, headhunter/executive-search
- **63 provinces/cities and 157 industries** available as search filters
- **Filters:** minimum salary, location-contains (client-side, applied on top of any mode)
- **Rich fields per job:** company info, salary (when disclosed), skills, requirements, description
- **Empty fields are omitted** — every record only contains data actually present on the listing

### Output per job

- `jobId` — VietnamWorks' internal listing ID
- `title`, `companyName`, `companyId`, `companyLogoUrl`
- `locations[]`, `country`
- `jobLevel`, `jobLevelId`, `typeWorking`, `typeWorkingId` (mode=byUrls / detail-enriched only)
- `salaryText`, `salaryMin`, `salaryMax` (only when disclosed)
- `skills[]`, `benefits[]` (mode=byUrls only, best-effort — see FAQ)
- `descriptionSnippet` (trending/search modes) or `descriptionText` (byUrls, full text)
- `requirementText`
- `postedText`, `createdOn`, `expiredOn`/`expiredDate`
- `sourceUrl` — canonical listing URL
- `recordType: "job"`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | select | `trending` | `trending` / `search` / `byUrls` |
| `feedType` | select | `bestJobs` | Curated feed to pull (mode=trending): `bestJobs`, `urgentJobs`, `featuredJobs`, `partTimeJobs`, `highSalaryJobs`, `headhunterJobs` |
| `keyword` | string | `developer` | Free-text search query (mode=search) |
| `locationIds` | array (select) | – | One or more of 63 Vietnamese provinces/cities (mode=search) |
| `industryIds` | array (select) | – | One or more of 157 VietnamWorks industries (mode=search) |
| `locationContains` | string | – | Client-side filter: keep only jobs whose location text contains this |
| `minSalary` | integer | – | Drop jobs with disclosed salary below this (VND/month). Undisclosed-salary jobs always pass. |
| `fetchFullDetails` | boolean | `false` | Visit each job's detail page for the complete description, employer profile, skills, and benefits |
| `jobUrls` | array | – | VietnamWorks job listing URLs (mode=byUrls) |
| `maxItems` | integer | `30` | Hard cap on emitted records (1–500) |

#### Example: trending high-salary jobs in Ho Chi Minh City

```json
{
  "mode": "trending",
  "feedType": "highSalaryJobs",
  "locationContains": "Ho Chi Minh",
  "maxItems": 50
}
```

#### Example: keyword search

```json
{
  "mode": "search",
  "keyword": "accountant",
  "locationIds": ["29"],
  "maxItems": 5
}
```

#### Example: fetch specific listings

```json
{
  "mode": "byUrls",
  "jobUrls": [
    "https://www.vietnamworks.com/hcm-senior-recruitment-consultant-real-estate-2091602-jv"
  ]
}
```

### Use cases

- **Recruitment intelligence** — track live Vietnamese vacancies by industry/location
- **Salary benchmarking** — aggregate disclosed salary bands across roles
- **Labour-market research** — analyze demand by industry/city over time
- **Talent sourcing** — bulk-collect employer and job data for outreach

### FAQ

**What's VietnamWorks?** Vietnam's largest job board and recruitment platform, part of Navigos Group, with 11,000+ active listings across every province and industry. See [vietnamworks.com](https://www.vietnamworks.com).

**Is a login or API key required?** No — all three modes use fully public endpoints.

**Why does `mode=search` only return up to 5 results, even with a high `maxItems`?** VietnamWorks' real-time keyword search endpoint is the same one that powers a small "top results" widget on their homepage — it's genuinely capped at 5 results server-side for every query, including no filter at all. This is a real upstream limit, not a bug. For bulk data, use `mode=trending`, which returns up to ~75 real listings per curated feed with no such cap.

**Why is `descriptionSnippet` truncated in `trending`/`search` modes?** Both data sources are VietnamWorks' own homepage-widget feeds, which intentionally return a ~450-character preview rather than the full description (to keep their homepage payload light). Use `fetchFullDetails: true`, or `mode: byUrls` directly, to get the complete, un-truncated `descriptionText`.

**Why are `skills`/`benefits` sometimes missing from `byUrls` results?** VietnamWorks' detail pages are built with React Server Components; most job fields (title, company, salary, requirement text, dates) are embedded directly and always populate, but `skills` and `benefits` are occasionally streamed as further nested references this actor doesn't resolve. When that happens the fields are simply omitted — never fabricated.

**How does the `locationContains` filter work?** It's a case-insensitive substring match against each job's parsed location list, applied client-side on top of any mode (including `search`'s own server-side `locationIds` filter, which uses real Vietnamese city IDs).

**Why did my `mode=search` run with `locationIds`/`industryIds` return 0 jobs?** VietnamWorks' search API geo-fences those two filters to Vietnam-based request IPs — they silently return an empty result (not an error) for requests from outside Vietnam, which is where Apify's cloud infrastructure runs by default. The actor automatically retries once through `proxyConfiguration` (Vietnam-country RESIDENTIAL) when a location/industry-filtered search comes back empty — turn that input on if you plan to use either filter. Unfiltered or keyword-only searches are unaffected and never need a proxy.

**How fresh is the data?** Real-time — every run hits VietnamWorks' live homepage, search API, and detail pages directly.

# Actor input Schema

## `mode` (type: `string`):

What to fetch.

## `feedType` (type: `string`):

Which curated VietnamWorks homepage feed to pull.

## `keyword` (type: `string`):

Free-text job search query, e.g. `developer`, `accountant`, `sales manager`.

## `locationIds` (type: `array`):

Restrict search results to one or more Vietnamese provinces/cities. Empty = no location filter.

## `industryIds` (type: `array`):

Restrict search results to one or more VietnamWorks industry categories. Empty = no industry filter.

## `locationContains` (type: `string`):

Only keep jobs whose location text contains this (e.g. `Ho Chi Minh`, `Ha Noi`). Applied client-side on top of any mode.

## `minSalary` (type: `integer`):

Drop jobs whose disclosed salary is below this amount. Jobs with an undisclosed salary always pass through.

## `fetchFullDetails` (type: `boolean`):

Visit each job's detail page for the complete, un-truncated description, employer profile, skills and benefits. `trending`/`search` results already include a description snippet, so this roughly doubles run time.

## `jobUrls` (type: `array`):

VietnamWorks job listing URLs, e.g. `https://www.vietnamworks.com/hcm-senior-recruitment-consultant-real-estate-2091602-jv`.

## `maxItems` (type: `integer`):

Hard cap on emitted records.

## `proxyConfiguration` (type: `object`):

Not required for `trending` or `byUrls`, which always work proxy-free. For `mode=search` with `locationIds`/`industryIds` set, the upstream API's location/industry filter only returns results for Vietnam-based request IPs — the actor automatically retries through this proxy (Vietnam-country RESIDENTIAL, if enabled here) only when the direct, no-proxy attempt returns 0 results, so leaving this off costs nothing for keyword-only or unfiltered searches.

## Actor input object example

```json
{
  "mode": "trending",
  "feedType": "bestJobs",
  "keyword": "developer",
  "locationIds": [],
  "industryIds": [],
  "fetchFullDetails": false,
  "jobUrls": [],
  "maxItems": 30,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "VN"
  }
}
```

# Actor output Schema

## `jobs` (type: `string`):

Dataset containing all scraped VietnamWorks job listings.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "trending",
    "feedType": "bestJobs",
    "keyword": "developer",
    "locationIds": [],
    "industryIds": [],
    "fetchFullDetails": false,
    "jobUrls": [],
    "maxItems": 30,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "VN"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/vietnamworks-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "trending",
    "feedType": "bestJobs",
    "keyword": "developer",
    "locationIds": [],
    "industryIds": [],
    "fetchFullDetails": False,
    "jobUrls": [],
    "maxItems": 30,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "VN",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/vietnamworks-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "trending",
  "feedType": "bestJobs",
  "keyword": "developer",
  "locationIds": [],
  "industryIds": [],
  "fetchFullDetails": false,
  "jobUrls": [],
  "maxItems": 30,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "VN"
  }
}' |
apify call crawlerbros/vietnamworks-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawlerbros/vietnamworks-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/fVzgsvycsHDq2WLIt/builds/yUZ1ehleznqaocfjD/openapi.json
