# JobsDB Hong Kong & Thailand Jobs Scraper (`automation-lab/jobsdb-hk-th-job-listings-retry`) Actor

Search JobsDB Hong Kong and Thailand by keyword and location, then export normalized vacancy records for recruiting and hiring-market monitoring.

- **URL**: https://apify.com/automation-lab/jobsdb-hk-th-job-listings-retry.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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

## JobsDB Hong Kong & Thailand Jobs Scraper

Export current **jobsdb jobs** from Hong Kong and Thailand as normalized vacancy records. Search by role or skill, narrow the search by location, and send structured results to spreadsheets, recruiting workflows, dashboards, or hiring-market monitoring systems.

The Actor uses the anonymous JobsDB search feed. It does not need a JobsDB account and does not open every detail page. This listing-focused scope keeps runs fast while preserving the fields most useful for vacancy discovery and recurring market analysis.

### What can this JobsDB jobs scraper do?

- Search Hong Kong, Thailand, or both country feeds.
- Apply a keyword and an optional location such as `Bangkok`, `Central`, or `Kowloon`.
- Paginate through current public search results up to a user-set limit.
- Export stable job IDs, titles, employers, locations, salary labels, work types, classifications, highlights, dates, and links.
- Deduplicate records by source country and JobsDB job ID.
- Produce a consistent default dataset for API, spreadsheet, webhook, and scheduled-run workflows.

The Actor extracts search-result vacancy data only. Full job-description enrichment, login-only data, saved jobs, applications, and employer account features are outside this version's scope.

### Who is it for?

#### Recruiters and talent teams

Build focused lists of open roles, identify active employers, and compare hiring demand between Hong Kong and Thailand.

#### Labor-market and strategy analysts

Schedule the same query repeatedly and compare datasets to monitor new listings, salary signals, location patterns, or category changes.

#### Job aggregators and data engineers

Feed normalized JobsDB records into a warehouse, spreadsheet, CRM, search index, or internal vacancy product without maintaining browser automation.

#### Researchers and journalists

Collect a reproducible snapshot of public vacancy search results for a defined keyword and geography.

### Why use this Actor?

The public JobsDB search pages can be protected by browser checks. This Actor uses the underlying anonymous search-data operation instead of downloading detail pages or rendering a browser. That provides:

- lower memory and runtime than full-page browser crawling;
- exact pagination totals and stable source IDs;
- consistent field names across Hong Kong and Thailand;
- no automatic residential proxy charges;
- explicit errors when the upstream response is invalid rather than silent empty output.

### What JobsDB data is extracted?

| Field | Meaning |
| --- | --- |
| `jobId` | Stable JobsDB vacancy ID |
| `title` | Vacancy title |
| `employer` | Employer or advertiser name, when disclosed |
| `country` | `HK` or `TH` source feed |
| `location` | JobsDB location label |
| `workTypes` | Employment-type labels |
| `workArrangement` | Remote, hybrid, or on-site label when available |
| `classification` | Top-level job category |
| `subclassification` | More specific category |
| `salary` | Displayed salary text when disclosed |
| `benefits` | Search-card highlights and benefits |
| `summary` | Short listing teaser |
| `listedAt` | Source listing timestamp |
| `jobUrl` | Canonical public vacancy URL |
| `applyUrl` | JobsDB application route |
| `searchQuery` | Query that produced the row |
| `scrapedAt` | Extraction timestamp |

JobsDB does not disclose every optional value on every listing. Missing optional values are returned as `null` or an empty array rather than invented.

### How to run the scraper

1. Open the Actor in Apify Console.
2. Enter a role or skill in **Job keywords**.
3. Optionally enter a JobsDB location.
4. Select Hong Kong, Thailand, or both.
5. Set the maximum number of records.
6. Click **Start**.
7. Open the **Job listings** dataset view or export it as JSON, CSV, Excel, XML, or RSS.

A useful first input is:

```json
{
  "query": "data analyst",
  "countries": ["HK", "TH"],
  "maxItems": 50
}
```

### Input parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `query` | string | `data analyst` | Required role, skill, or phrase; 2–200 characters |
| `location` | string | none | Optional location text understood by JobsDB |
| `countries` | array | `["HK", "TH"]` | One or both supported country feeds |
| `maxItems` | integer | `50` | Maximum unique records across selected feeds; 1–1,000 |

When both feeds are selected, the Actor reserves a fair share of the limit for each country. The final feed can use capacity left by an earlier naturally sparse search.

### Output example

A current run returns rows shaped like this:

```json
{
  "jobId": "94071473",
  "title": "Software Engineer (Node.js & React.js)",
  "employer": "AVA Intelligent Partners Limited",
  "country": "HK",
  "location": "Wan Chai, Wan Chai District",
  "workTypes": ["Full time"],
  "workArrangement": null,
  "classification": "Information & Communication Technology",
  "subclassification": "Developers/Programmers",
  "salary": null,
  "benefits": [
    "Solid experience in React, Node.js, TypeScript",
    "2-4 years relevant experience in programming"
  ],
  "summary": "Build scalable web apps with Node.js & React.js.",
  "listedAt": "2026-08-19T04:33:51.000Z",
  "jobUrl": "https://hk.jobsdb.com/job/94071473",
  "applyUrl": "https://hk.jobsdb.com/job/94071473/apply",
  "searchQuery": "software engineer",
  "scrapedAt": "2026-08-19T20:40:00.000Z"
}
```

The example demonstrates the schema; live listings can change or expire after a run.

### How much does it cost to extract JobsDB job listings?

The Actor uses pay-per-event pricing: one small start event per run and one `item` event per unique vacancy saved. Failed, duplicate, or rejected source rows are not charged as items.

Your exact item rate depends on your Apify pricing tier and is shown before starting a run. At the Bronze rate of $0.000792 per item plus the $0.005 start event:

| Saved vacancies | Example total |
| ---: | ---: |
| 10 | $0.01292 |
| 100 | $0.08420 |
| 1,000 | $0.79700 |

These examples describe Actor event charges. Apify plan terms may separately apply. Check the live pricing panel for your tier before a large run.

### Recurring monitoring workflow

1. Create a task with a stable query, countries, location, and limit.
2. Add a daily or weekly schedule.
3. Send completed-run data to your webhook, integration, or warehouse.
4. Compare `country + jobId` with the previous snapshot.
5. Flag new IDs, removed IDs, changed salary labels, or shifts in employer activity.

The Actor returns snapshots. It does not retain historical changes or send alerts itself; those are downstream workflow decisions.

### Spreadsheet and data-pipeline exports

Use the dataset export menu for CSV or Excel. For an automated pipeline, read the run's default dataset through the Apify API. Stable field names make it straightforward to load records into BigQuery, Snowflake, PostgreSQL, Airtable, Google Sheets, or a search index.

For deduplication outside one run, use the composite key `country + jobId`. The same numeric ID should not be assumed globally unique across unrelated sources.

### Run with the Apify API

Replace `YOUR_TOKEN` with your Apify token.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~jobsdb-hk-th-job-listings-retry/runs?token=YOUR_TOKEN&waitForFinish=120" \
  -H "Content-Type: application/json" \
  -d '{"query":"cybersecurity","countries":["HK","TH"],"maxItems":50}'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/jobsdb-hk-th-job-listings-retry').call({
  query: 'cybersecurity',
  countries: ['HK', 'TH'],
  maxItems: 50,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("automation-lab/jobsdb-hk-th-job-listings-retry").call(
    run_input={
        "query": "data analyst",
        "location": "Bangkok",
        "countries": ["TH"],
        "maxItems": 50,
    }
)
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use with Apify MCP

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/jobsdb-hk-th-job-listings-retry"
```

#### Claude Desktop, Cursor, and VS Code MCP setup

Claude Desktop, Cursor, and VS Code can use the same remote MCP server configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/jobsdb-hk-th-job-listings-retry"
    }
  }
}
```

Example prompts:

- “Search JobsDB for 30 data analyst vacancies in Bangkok and summarize the hiring companies.”
- “Export 100 cybersecurity vacancies from Hong Kong and Thailand and group them by classification.”
- “Run my JobsDB monitoring task and identify job IDs not present in the previous dataset.”

### Limits and failure behavior

- Only anonymous public JobsDB Hong Kong and Thailand search feeds are supported.
- `maxItems` is capped at 1,000 per run.
- Location matching follows JobsDB's own search interpretation and is not a custom geocoder.
- Thailand fields can contain Thai-language labels.
- Salary, work arrangement, benefits, and some employer fields are optional at the source.
- Listings can expire or be removed after extraction.
- Full detail descriptions are intentionally excluded.
- The Actor retries temporary upstream failures three times, then fails non-zero instead of returning a misleading successful empty dataset.

### Performance tips

- Use a specific role phrase instead of a broad single word.
- Add a location when you need a city or district rather than country-wide results.
- Start with a modest `maxItems` while designing a downstream workflow.
- Use scheduled tasks for repeated inputs rather than manually recreating runs.
- Avoid assuming a displayed salary label uses one currency; interpret it with the `country` field.

### Legality and responsible use

JobsDB content may be subject to website terms, database rights, copyright, privacy rules, and local law. Extract only public information you have a lawful reason to process. Do not use the Actor for spam, discrimination, harassment, automated applications, or attempts to access private accounts. Minimize retention and protect exported data. You are responsible for your inputs and downstream use.

### FAQ and troubleshooting

#### Why did my run return fewer results than requested?

The source may have fewer matching vacancies, filters may narrow the feed, or duplicate IDs may occur across pagination. Check the query and location, then inspect the run log's source totals.

#### Why is a salary or work arrangement missing?

JobsDB does not disclose these values for every search result. Missing source values remain `null`; the Actor does not infer compensation or remote status.

#### Why did the run fail instead of returning zero rows?

An invalid or challenged upstream response is different from a valid no-result search. The Actor fails after bounded retries so scheduled workflows can alert on a source problem rather than accepting a false empty snapshot.

#### Does this Actor apply for jobs?

No. It exports public vacancy search records and URLs. Applications, saved jobs, login-only features, and employer workflows are not supported.

#### Does it scrape full descriptions?

No. V1 deliberately uses search-result records, including the source summary and highlights. This makes regional monitoring faster and less expensive than visiting every detail page.

### Related Actors

For broader recruiting automation, explore other public job-feed Actors from [automation-lab on Apify](https://apify.com/automation-lab). Choose a source-specific Actor when you need another job board; combine datasets downstream using stable source and job identifiers.

### Support

If a valid public JobsDB query repeatedly fails, open an Actor issue with the input (remove secrets), run ID, expected country feed, and the relevant log excerpt. Do not include Apify tokens or private candidate data.

# Actor input Schema

## `query` (type: `string`):

Role, skill, or phrase to search for, such as data analyst or software engineer.

## `location` (type: `string`):

Optional location text understood by JobsDB, such as Bangkok, Central, or Kowloon.

## `countries` (type: `array`):

JobsDB country feeds to search. Results identify their source country.

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

Maximum unique vacancy records saved across all selected country feeds.

## Actor input object example

```json
{
  "query": "data analyst",
  "countries": [
    "HK",
    "TH"
  ],
  "maxItems": 20
}
```

# Actor output Schema

## `dataset` (type: `string`):

Default dataset containing all extracted JobsDB vacancy records.

# 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 = {
    "query": "data analyst",
    "countries": [
        "HK",
        "TH"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/jobsdb-hk-th-job-listings-retry").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 = {
    "query": "data analyst",
    "countries": [
        "HK",
        "TH",
    ],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/jobsdb-hk-th-job-listings-retry").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 '{
  "query": "data analyst",
  "countries": [
    "HK",
    "TH"
  ],
  "maxItems": 20
}' |
apify call automation-lab/jobsdb-hk-th-job-listings-retry --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/jobsdb-hk-th-job-listings-retry"
        }
    }
}

```

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/GQwXs6L6LQ3ZenZne/builds/So0eApKBnWWsvwJmD/openapi.json
