{
  "data": {
    "id": "rIESg6oUkusHumoXj",
    "actId": "d1RKTdJH42al1BenR",
    "userId": "NL4ThgItAwaoBlffQ",
    "startedAt": "2026-11-12T09:22:10.147Z",
    "finishedAt": "2026-11-12T09:22:19.377Z",
    "status": "SUCCEEDED",
    "meta": {
      "origin": "CLI"
    },
    "stats": {
      "durationMillis": 9150,
      "runTimeSecs": 9.15,
      "computeUnits": 0.010166666666666666,
      "imageSizeBytes": 361446912
    },
    "options": {
      "useCache": true,
      "betaPackages": false,
      "memoryMbytes": 4096,
      "diskMbytes": 8192
    },
    "createdByOrganizationMemberUserId": "6GwdnIcc2aWR7LzCS",
    "exitCode": 0,
    "inputSchema": "{\n  \"title\": \"JobsDB Scraper Input\",\n  \"description\": \"Search JobsDB job listings, browse by category or location, or pull the newest postings across Hong Kong and Thailand.\",\n  \"type\": \"object\",\n  \"schemaVersion\": 1,\n  \"properties\": {\n    \"mode\": {\n      \"title\": \"Mode\",\n      \"type\": \"string\",\n      \"description\": \"What to fetch.\",\n      \"editor\": \"select\",\n      \"enum\": [\n        \"searchJobs\",\n        \"browseByCategory\",\n        \"browseByLocation\",\n        \"browseByCompany\",\n        \"latestJobs\",\n        \"byJobId\",\n        \"byUrls\"\n      ],\n      \"enumTitles\": [\n        \"Search jobs by keyword\",\n        \"Browse jobs by category\",\n        \"Browse jobs by location\",\n        \"Browse jobs by company (advertiserId)\",\n        \"Latest jobs (newest first, site-wide)\",\n        \"Look up job(s) by jobId\",\n        \"Look up job URLs\"\n      ],\n      \"default\": \"searchJobs\",\n      \"prefill\": \"searchJobs\"\n    },\n    \"country\": {\n      \"title\": \"Country\",\n      \"type\": \"string\",\n      \"description\": \"Which JobsDB market to scrape.\",\n      \"editor\": \"select\",\n      \"enum\": [\n        \"HK\",\n        \"TH\"\n      ],\n      \"enumTitles\": [\n        \"Hong Kong (hk.jobsdb.com)\",\n        \"Thailand (th.jobsdb.com)\"\n      ],\n      \"default\": \"HK\",\n      \"prefill\": \"HK\"\n    },\n    \"keywords\": {\n      \"title\": \"Keywords\",\n      \"type\": \"string\",\n      \"description\": \"Job title, skill, or company to search for. Leave empty to browse every job in the selected mode; an explicit value narrows the results.\",\n      \"editor\": \"textfield\",\n      \"default\": \"\",\n      \"prefill\": \"engineer\"\n    },\n    \"location\": {\n      \"title\": \"Location\",\n      \"type\": \"string\",\n      \"description\": \"Free-text location/area to browse or filter by, e.g. `Central`, `Kowloon`, `Bangkok`. Required for mode=browseByLocation.\",\n      \"editor\": \"textfield\",\n      \"default\": \"\",\n      \"prefill\": \"\"\n    },\n    \"classification\": {\n      \"title\": \"Category\",\n      \"type\": \"array\",\n      \"description\": \"Job categor(y/ies). Pick one or more (OR match) — required for mode=browseByCategory; optional filter for other modes. Leave empty for any category.\",\n      \"editor\": \"select\",\n      \"items\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"1200\",\n          \"1203\",\n          \"1204\",\n          \"1206\",\n          \"1209\",\n          \"1210\",\n          \"1211\",\n          \"1212\",\n          \"1214\",\n          \"1216\",\n          \"1220\",\n          \"1223\",\n          \"1225\",\n          \"6008\",\n          \"6043\",\n          \"6058\",\n          \"6076\",\n          \"6092\",\n          \"6123\",\n          \"6163\",\n          \"6205\",\n          \"6246\",\n          \"6251\",\n          \"6263\",\n          \"6281\",\n          \"6304\",\n          \"6317\",\n          \"6362\",\n          \"7019\"\n        ],\n        \"enumTitles\": [\n          \"Accounting\",\n          \"Banking & Financial Services\",\n          \"Call Centre & Customer Service\",\n          \"Construction\",\n          \"Engineering\",\n          \"Government & Defence\",\n          \"Healthcare & Medical\",\n          \"Hospitality & Tourism\",\n          \"Insurance & Superannuation\",\n          \"Legal\",\n          \"Real Estate & Property\",\n          \"Science & Technology\",\n          \"Trades & Services\",\n          \"Marketing & Communications\",\n          \"Retail & Consumer Products\",\n          \"Mining, Resources & Energy\",\n          \"Consulting & Strategy\",\n          \"Manufacturing, Transport & Logistics\",\n          \"Education & Training\",\n          \"Community Services & Development\",\n          \"Farming, Animals & Conservation\",\n          \"Sport & Recreation\",\n          \"Administration & Office Support\",\n          \"Design & Architecture\",\n          \"Information & Communication Technology\",\n          \"Advertising, Arts & Media\",\n          \"Human Resources & Recruitment\",\n          \"Sales\",\n          \"CEO & General Management\"\n        ]\n      },\n      \"uniqueItems\": true,\n      \"default\": [],\n      \"prefill\": []\n    },\n    \"subclassification\": {\n      \"title\": \"Sub-category ID(s)\",\n      \"type\": \"string\",\n      \"description\": \"One or more numeric JobsDB sub-category IDs (comma-separated for OR match) to narrow a `classification` further (e.g. `6038` = Project Engineering under Engineering). Get valid IDs from the `subCategoryId` field on a previously scraped job. Optional filter for any mode.\",\n      \"editor\": \"textfield\",\n      \"default\": \"\",\n      \"prefill\": \"\"\n    },\n    \"workType\": {\n      \"title\": \"Work type\",\n      \"type\": \"array\",\n      \"description\": \"Filter by employment type. Pick one or more (OR match). Leave empty for any work type.\",\n      \"editor\": \"select\",\n      \"items\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"242\",\n          \"243\",\n          \"244\",\n          \"245\"\n        ],\n        \"enumTitles\": [\n          \"Full time\",\n          \"Part time\",\n          \"Contract/Temp\",\n          \"Casual/Vacation\"\n        ]\n      },\n      \"uniqueItems\": true,\n      \"default\": [],\n      \"prefill\": []\n    },\n    \"workArrangement\": {\n      \"title\": \"Work arrangement\",\n      \"type\": \"array\",\n      \"description\": \"Filter by on-site/hybrid/remote work arrangement. Pick one or more (OR match). Leave empty for any arrangement.\",\n      \"editor\": \"select\",\n      \"items\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"1\",\n          \"2\",\n          \"3\"\n        ],\n        \"enumTitles\": [\n          \"On-site\",\n          \"Hybrid\",\n          \"Remote\"\n        ]\n      },\n      \"uniqueItems\": true,\n      \"default\": [],\n      \"prefill\": []\n    },\n    \"advertiserId\": {\n      \"title\": \"Company / advertiser ID(s)\",\n      \"type\": \"string\",\n      \"description\": \"One or more numeric JobsDB advertiser IDs (comma-separated for OR match) to browse all current listings from one or more employers/agencies. Required for mode=browseByCompany; get an ID from the `advertiserId` field on any previously scraped job. Also works as an optional filter in other modes.\",\n      \"editor\": \"textfield\",\n      \"default\": \"\",\n      \"prefill\": \"\"\n    },\n    \"jobId\": {\n      \"title\": \"Job ID(s)\",\n      \"type\": \"string\",\n      \"description\": \"One or more numeric JobsDB job IDs (comma-separated, or pass an array) to look up specific listings directly. Required for mode=byJobId; get an ID from the `jobId` field on any previously scraped job. Ignored in other modes.\",\n      \"editor\": \"textfield\",\n      \"default\": \"\",\n      \"prefill\": \"\"\n    },\n    \"urls\": {\n      \"title\": \"Job URLs or IDs\",\n      \"type\": \"array\",\n      \"description\": \"One or more JobsDB job links (hk.jobsdb.com/job/… or th.jobsdb.com/job/…) or numeric job IDs to look up directly. Required for mode=byUrls; the market is taken from each link.\",\n      \"editor\": \"stringList\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"default\": [],\n      \"prefill\": []\n    },\n    \"includeDetails\": {\n      \"title\": \"Include full job details\",\n      \"type\": \"boolean\",\n      \"description\": \"Fetch each listing's full description, expiry status, applicant-demand signals, screening questions, badges and company profile. Adds one request per job; turn off for the fastest, cheapest run.\",\n      \"default\": true,\n      \"prefill\": true\n    },\n    \"descriptionFormat\": {\n      \"title\": \"Description format\",\n      \"type\": \"string\",\n      \"description\": \"How the full job description is returned when details are included.\",\n      \"editor\": \"select\",\n      \"enum\": [\n        \"text\",\n        \"html\",\n        \"both\"\n      ],\n      \"enumTitles\": [\n        \"Plain text\",\n        \"HTML\",\n        \"Both\"\n      ],\n      \"default\": \"text\",\n      \"prefill\": \"text\"\n    },\n    \"salaryMin\": {\n      \"title\": \"Salary min\",\n      \"type\": \"integer\",\n      \"description\": \"Minimum salary (in the local currency, per `salaryType` period). Leave unset for no lower bound.\",\n      \"minimum\": 0,\n      \"maximum\": 10000000\n    },\n    \"salaryMax\": {\n      \"title\": \"Salary max\",\n      \"type\": \"integer\",\n      \"description\": \"Maximum salary (in the local currency, per `salaryType` period). Leave unset for no upper bound.\",\n      \"minimum\": 0,\n      \"maximum\": 10000000\n    },\n    \"salaryType\": {\n      \"title\": \"Salary period\",\n      \"type\": \"string\",\n      \"description\": \"Period the salary min/max values are expressed in.\",\n      \"editor\": \"select\",\n      \"enum\": [\n        \"annual\",\n        \"monthly\",\n        \"hourly\"\n      ],\n      \"enumTitles\": [\n        \"Annual\",\n        \"Monthly\",\n        \"Hourly\"\n      ],\n      \"default\": \"annual\",\n      \"prefill\": \"annual\"\n    },\n    \"dateRange\": {\n      \"title\": \"Date posted\",\n      \"type\": \"string\",\n      \"description\": \"Only include jobs posted within this window.\",\n      \"editor\": \"select\",\n      \"enum\": [\n        \"\",\n        \"1\",\n        \"3\",\n        \"7\",\n        \"14\",\n        \"31\"\n      ],\n      \"enumTitles\": [\n        \"Any time\",\n        \"Past 24 hours\",\n        \"Past 3 days\",\n        \"Past 7 days\",\n        \"Past 14 days\",\n        \"Past 31 days\"\n      ],\n      \"default\": \"\",\n      \"prefill\": \"\"\n    },\n    \"sortMode\": {\n      \"title\": \"Sort by\",\n      \"type\": \"string\",\n      \"description\": \"Result ordering.\",\n      \"editor\": \"select\",\n      \"enum\": [\n        \"KeywordRelevance\",\n        \"ListedDate\"\n      ],\n      \"enumTitles\": [\n        \"Relevance\",\n        \"Date (newest first)\"\n      ],\n      \"default\": \"KeywordRelevance\",\n      \"prefill\": \"KeywordRelevance\"\n    },\n    \"maxItems\": {\n      \"title\": \"Max items\",\n      \"type\": \"integer\",\n      \"description\": \"Hard cap on emitted records.\",\n      \"minimum\": 1,\n      \"maximum\": 1000,\n      \"default\": 50,\n      \"prefill\": 3\n    }\n  },\n  \"required\": [\n    \"mode\",\n    \"country\"\n  ]\n}",
    "readme": "# JobsDB Hong Kong & Thailand Job Listings Extractor\n\nJobsDB is the leading job board in Hong Kong and Thailand, part of the SEEK employment network. This actor queries its live search across seven modes — keyword search, category browse, location browse, company browse, latest postings, direct job-ID lookup, or pasted job URLs — and returns one structured record per listing, optionally enriched with the full ad body, applicant-demand signals and the employer's company profile. No login, cookies, or API key required.\n\n## What this actor does\n\n- **Seven modes:** `searchJobs`, `browseByCategory`, `browseByLocation`, `browseByCompany`, `latestJobs`, `byJobId`, `byUrls`\n- **Two markets:** Hong Kong (`hk.jobsdb.com`) and Thailand (`th.jobsdb.com`); `byUrls` reads the market from each link, so HK and TH links can be mixed in one run\n- **Search filters:** category (29 industries, multi-select), sub-category IDs, work type (multi-select), work arrangement (on-site/hybrid/remote, multi-select), company/advertiser ID (multiple), salary range + period, date posted, sort order\n- **Detail enrichment:** full description as plain text, HTML, or both; abstract; expiry/status; applicant count, volume and resume/cover-letter percentages; screening questions; badges; company industry, size, website, description, rating/review count and perks\n- **Empty fields are omitted** — every record only contains data that was actually found\n\n## Output per job\n\n- `jobId` — unique JobsDB listing ID\n- `title`, `teaser`, `abstract`, `bulletPoints[]` — headline and job highlights\n- `descriptionText`, `descriptionHtml` — full ad body in the chosen `descriptionFormat`\n- `companyName`, `companyUrl`, `logoUrl`, `coverImageUrl`\n- `advertiserId`, `advertiserName`, `advertiserVerified`, `employerId`, `companyId` — internal JobsDB identifiers for the employer/advertiser account (feed `advertiserId` back into `mode=browseByCompany` to pull every current listing from that employer; `companyId` is the ID embedded in `companyUrl`)\n- `category`, `categoryId`, `subCategory`, `subCategoryId`\n- `location`, `locations[]`, `countryCode`; `locationCountry`, `locationCountryCode`, `locationRegion`, `locationState`, `locationSuburb`, `locationPostcode` when the ad carries a structured address\n- `workTypes[]` — e.g. `Full time`, `Part time`, `Contract/Temp`, `Casual/Vacation`\n- `workArrangements[]` — e.g. `On-site`, `Hybrid`, `Remote`\n- `salaryLabel` — as displayed on the listing (when the advertiser disclosed one)\n- `salaryMinValue`, `salaryMaxValue`, `salaryPeriod` (`hourly`/`monthly`/`annual`), `salaryCurrency` (`HKD`/`THB`) — structured numbers parsed from `salaryLabel` when it's a clear numeric range (omitted for non-numeric labels like `Negotiable` or `Competitive`)\n- `listingDate`, `listingDateDisplay`, `postedAt`, `postedDate`, `updatedAt`, `expiresAt`, `expiredAt`\n- `isFeatured`, `isExpired`, `isLinkOut`, `isProfileApply`, `isVerified`, `jobStatus`\n- `tags[]` — urgency/status badges shown on the listing, e.g. `Urgently hiring`, `Expiring soon`, `Be an early applicant`\n- `badges[]`, `displayTags[]` — work-arrangement and campaign badges from the ad\n- `screeningQuestions[]` — questions the employer asks in the application form\n- `applicantCount`, `applicantCountLabel`, `applicantVolumeLabel`, `applicantMessage`, `resumePercentage`, `coverLetterPercentage` — applicant-demand signals when JobsDB exposes them\n- `companyIndustry`, `companySize`, `companyWebsite`, `companyDescription`, `companyRating`, `companyReviewCount`, `companyPerks[]`, `companyProfileId` — structured employer profile when one exists\n- `normalizedRoleTitle`, `normalizedLocation`, `broaderLocationName`, `normalizedCompanyName` — SEEK's normalized taxonomy values\n- `roleId`, `country`, `applyUrl`, `jobUrl`\n- `recordType: \"job\"`, `scrapedAt`\n\n## Input\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `mode` | string | `searchJobs` | `searchJobs` / `browseByCategory` / `browseByLocation` / `browseByCompany` / `latestJobs` / `byJobId` / `byUrls` |\n| `country` | string | `HK` | `HK` (Hong Kong) or `TH` (Thailand); `byUrls` uses each link's host instead |\n| `keywords` | string | – | Job title / skill / company keyword; leave empty to browse every job in the selected mode |\n| `location` | string | – | Area/city, e.g. `Central`, `Kowloon`, `Bangkok` (required for `browseByLocation`) |\n| `classification` | array | – | One or more job category IDs (required for `browseByCategory`) — 29 categories available in the UI multi-select; picking several matches ANY of them |\n| `subclassification` | string | – | One or more numeric sub-category IDs, comma-separated, to narrow `classification` further, e.g. `6038` = Project Engineering under Engineering — find one via the `subCategoryId` output field on any scraped job |\n| `workType` | array | – | Multi-select: `242` Full time / `243` Part time / `244` Contract/Temp / `245` Casual/Vacation |\n| `workArrangement` | array | – | Multi-select: `1` On-site / `2` Hybrid / `3` Remote |\n| `advertiserId` | string | – | One or more numeric JobsDB company/advertiser IDs, comma-separated (required for `browseByCompany`) — find one via the `advertiserId` output field on any scraped job |\n| `jobId` | string | – | One or more numeric JobsDB job IDs, comma-separated or as an array (required for `byJobId`) — find one via the `jobId` output field on any previously scraped job |\n| `urls` | array | – | JobsDB job links or numeric IDs (required for `byUrls`); HK and TH links can be mixed |\n| `includeDetails` | boolean | `true` | Fetch full description, expiry, applicant signals, screening questions, badges and company profile (one extra request per job) |\n| `descriptionFormat` | string | `text` | `text` / `html` / `both` |\n| `salaryMin` | int | – | Minimum salary |\n| `salaryMax` | int | – | Maximum salary |\n| `salaryType` | string | `annual` | `annual` / `monthly` / `hourly` — period `salaryMin`/`salaryMax` are expressed in |\n| `dateRange` | string | – | Only jobs posted in the last `1`/`3`/`7`/`14`/`31` days |\n| `sortMode` | string | `KeywordRelevance` | `KeywordRelevance` or `ListedDate` (newest first) |\n| `maxItems` | int | `50` | Hard cap on emitted records (1–1000) |\n\n### Example: search with filters and full details\n\n```json\n{\n  \"mode\": \"searchJobs\",\n  \"country\": \"HK\",\n  \"keywords\": \"data analyst\",\n  \"workType\": [\"242\"],\n  \"salaryMin\": 20000,\n  \"salaryMax\": 40000,\n  \"salaryType\": \"monthly\",\n  \"dateRange\": \"7\",\n  \"sortMode\": \"ListedDate\",\n  \"includeDetails\": true,\n  \"descriptionFormat\": \"both\",\n  \"maxItems\": 50\n}\n```\n\n### Example: browse a category\n\n```json\n{\n  \"mode\": \"browseByCategory\",\n  \"country\": \"TH\",\n  \"classification\": [\"6281\"],\n  \"maxItems\": 100\n}\n```\n\n### Example: newest jobs in a location\n\n```json\n{\n  \"mode\": \"browseByLocation\",\n  \"country\": \"HK\",\n  \"location\": \"Kowloon\",\n  \"maxItems\": 50\n}\n```\n\n### Example: all current listings from one company\n\n```json\n{\n  \"mode\": \"browseByCompany\",\n  \"country\": \"HK\",\n  \"advertiserId\": \"61282529\",\n  \"maxItems\": 50\n}\n```\n\n### Example: remote jobs only\n\n```json\n{\n  \"mode\": \"searchJobs\",\n  \"country\": \"HK\",\n  \"keywords\": \"developer\",\n  \"workArrangement\": [\"3\"],\n  \"maxItems\": 50\n}\n```\n\n### Example: look up specific jobs by ID\n\n```json\n{\n  \"mode\": \"byJobId\",\n  \"country\": \"HK\",\n  \"jobId\": \"93889825,93902058\"\n}\n```\n\n### Example: paste job links from both markets\n\n```json\n{\n  \"mode\": \"byUrls\",\n  \"urls\": [\n    \"https://hk.jobsdb.com/job/95849989\",\n    \"https://th.jobsdb.com/job/95856128\"\n  ],\n  \"includeDetails\": true,\n  \"descriptionFormat\": \"text\"\n}\n```\n\n## Use cases\n\n- **Job seekers & career sites** — aggregate fresh listings by role, category, location, or remote/hybrid arrangement, with the full ad body\n- **Recruiters & staffing agencies** — monitor competitor job postings, screening questions, and applicant demand\n- **Labor market research** — track hiring demand and posted salary ranges across categories and regions in HK/TH\n- **HR tech / salary benchmarking tools** — collect structured salary ranges plus company industry, size and perks\n- **Lead generation** — find companies actively hiring in a given industry\n- **Competitor/company tracking** — pull every current listing from a specific employer with `mode=browseByCompany`\n- **Shortlist refresh** — re-check saved job URLs for expiry, updated content and current applicant volume\n\n## FAQ\n\n**What's JobsDB?** A leading job board across Hong Kong and Thailand, part of the SEEK employment network. See [jobsdb.com](https://hk.jobsdb.com).\n\n**Do I need to log in or provide cookies?** No. This actor uses JobsDB's public job-search and job-detail endpoints — no account, API key, or proxy required.\n\n**What does `includeDetails` add?** One extra request per job fills the full description, abstract, expiry/status, applicant-demand signals, screening questions, badges, structured location and company profile. Turn it off to scrape more listings faster at a lower cost.\n\n**Why is `descriptionText` missing on some jobs?** A small number of advertisers publish only an external apply link. When the ad has no body, the field is omitted rather than filled with a placeholder.\n\n**Why is `salaryLabel` missing on some jobs?** Not every advertiser discloses a salary. The field is only included when JobsDB displays one.\n\n**Why is `salaryMinValue`/`salaryMaxValue` missing even though `salaryLabel` is present?** A handful of advertisers enter free text instead of a number (e.g. `Negotiable`, `Competitive`, `Attractive Salary Package`). The structured fields are only added when `salaryLabel` is confidently a numeric amount or range — never a guessed value.\n\n**Why are applicant signals missing on some jobs?** JobsDB only exposes them for jobs with enough application activity, and external-apply jobs omit them entirely. The field is absent rather than zeroed when the source has no signal.\n\n**What's the difference between `category` and `subCategory`?** JobsDB classifies each job into a broad category (e.g. `Information & Communication Technology`) and a more specific sub-category (e.g. `Developers/Programmers`).\n\n**How fresh is the data?** Real-time — every run queries JobsDB's live APIs directly.\n\n**Why does `jobUrl` sometimes fail a `curl`/HEAD check?** JobsDB's listing pages sit behind bot-detection that blocks non-browser HTTP clients (no cookies, no JS) while allowing real browsers through normally. The URLs themselves are valid, live links — opening them in a browser works fine. `logoUrl` and `coverImageUrl` point at JobsDB's image CDN and are directly fetchable without a browser.\n\n**Can I use Thai-script place names for `location` in Thailand?** Yes. `location: \"กรุงเทพ\"` and `location: \"Bangkok\"` both work for `country: \"TH\"` — the actor automatically detects Thai-script input and queries JobsDB's Thai-language endpoint so the place name resolves correctly. `category`, `workTypes`, and `workArrangements` are always normalized back to English in the output either way, so filtering/grouping by those fields stays consistent regardless of which locale a record was fetched under. `teaser`, `descriptionText`, `bulletPoints`, `screeningQuestions`, and `subCategory` remain in whatever language the advertiser/JobsDB used (Thai for Thai-script location lookups), since those are free text with no fixed English translation to fall back on.\n\n**Which markets are supported?** Hong Kong and Thailand, JobsDB's two active markets. Other former JobsDB markets (Singapore, Indonesia, Philippines) have migrated to the sister JobStreet/Jora brands and are out of scope for this actor.\n\n**How do I get an `advertiserId` to use with `mode=browseByCompany`?** Run any other mode first and copy the `advertiserId` field from a job posted by the company you're interested in, then feed it back in as input.\n\n**How do I use `subclassification`?** It narrows a `classification` down to a specific sub-category (e.g. only `Developers/Programmers` inside `Information & Communication Technology`). Copy a value from the `subCategoryId` output field on a job you've already scraped in the category you want, then pass it as input on a later run.\n\n**Can I filter by more than one category, work type, or work arrangement at once?** Yes. `classification` and `workType` and `workArrangement` are multi-select — pick as many as you want and a job matching ANY of your selections is returned. `subclassification` and `advertiserId` accept multiple comma-separated IDs the same way, e.g. `advertiserId: \"61282529,61359613\"` pulls current listings from both companies in one run.\n\n**How do I re-fetch a specific job I already scraped?** Use `mode: \"byJobId\"` with the `jobId` values, or `mode: \"byUrls\"` with the saved `jobUrl` links — comma-separated arrays are fine. Useful for refreshing a shortlist or checking whether a listing is still live.\n",
    "actorDefinition": {
      "actorSpecification": 1,
      "name": "jobsdb-hong-kong-and-thailand-job-listings",
      "title": "JobsDB Hong Kong & Thailand Job Listings Extractor",
      "description": "Pull job listings from JobsDB's Hong Kong and Thailand markets by keyword, category, location, or company, with salary, work-type, work-arrangement, and date-posted filters.",
      "version": "1.0",
      "buildTag": "latest",
      "dockerfile": "./Dockerfile",
      "readme": "# JobsDB Hong Kong & Thailand Job Listings Extractor\n\nJobsDB is the leading job board in Hong Kong and Thailand, part of the SEEK employment network. This actor queries its live search across seven modes — keyword search, category browse, location browse, company browse, latest postings, direct job-ID lookup, or pasted job URLs — and returns one structured record per listing, optionally enriched with the full ad body, applicant-demand signals and the employer's company profile. No login, cookies, or API key required.\n\n## What this actor does\n\n- **Seven modes:** `searchJobs`, `browseByCategory`, `browseByLocation`, `browseByCompany`, `latestJobs`, `byJobId`, `byUrls`\n- **Two markets:** Hong Kong (`hk.jobsdb.com`) and Thailand (`th.jobsdb.com`); `byUrls` reads the market from each link, so HK and TH links can be mixed in one run\n- **Search filters:** category (29 industries, multi-select), sub-category IDs, work type (multi-select), work arrangement (on-site/hybrid/remote, multi-select), company/advertiser ID (multiple), salary range + period, date posted, sort order\n- **Detail enrichment:** full description as plain text, HTML, or both; abstract; expiry/status; applicant count, volume and resume/cover-letter percentages; screening questions; badges; company industry, size, website, description, rating/review count and perks\n- **Empty fields are omitted** — every record only contains data that was actually found\n\n## Output per job\n\n- `jobId` — unique JobsDB listing ID\n- `title`, `teaser`, `abstract`, `bulletPoints[]` — headline and job highlights\n- `descriptionText`, `descriptionHtml` — full ad body in the chosen `descriptionFormat`\n- `companyName`, `companyUrl`, `logoUrl`, `coverImageUrl`\n- `advertiserId`, `advertiserName`, `advertiserVerified`, `employerId`, `companyId` — internal JobsDB identifiers for the employer/advertiser account (feed `advertiserId` back into `mode=browseByCompany` to pull every current listing from that employer; `companyId` is the ID embedded in `companyUrl`)\n- `category`, `categoryId`, `subCategory`, `subCategoryId`\n- `location`, `locations[]`, `countryCode`; `locationCountry`, `locationCountryCode`, `locationRegion`, `locationState`, `locationSuburb`, `locationPostcode` when the ad carries a structured address\n- `workTypes[]` — e.g. `Full time`, `Part time`, `Contract/Temp`, `Casual/Vacation`\n- `workArrangements[]` — e.g. `On-site`, `Hybrid`, `Remote`\n- `salaryLabel` — as displayed on the listing (when the advertiser disclosed one)\n- `salaryMinValue`, `salaryMaxValue`, `salaryPeriod` (`hourly`/`monthly`/`annual`), `salaryCurrency` (`HKD`/`THB`) — structured numbers parsed from `salaryLabel` when it's a clear numeric range (omitted for non-numeric labels like `Negotiable` or `Competitive`)\n- `listingDate`, `listingDateDisplay`, `postedAt`, `postedDate`, `updatedAt`, `expiresAt`, `expiredAt`\n- `isFeatured`, `isExpired`, `isLinkOut`, `isProfileApply`, `isVerified`, `jobStatus`\n- `tags[]` — urgency/status badges shown on the listing, e.g. `Urgently hiring`, `Expiring soon`, `Be an early applicant`\n- `badges[]`, `displayTags[]` — work-arrangement and campaign badges from the ad\n- `screeningQuestions[]` — questions the employer asks in the application form\n- `applicantCount`, `applicantCountLabel`, `applicantVolumeLabel`, `applicantMessage`, `resumePercentage`, `coverLetterPercentage` — applicant-demand signals when JobsDB exposes them\n- `companyIndustry`, `companySize`, `companyWebsite`, `companyDescription`, `companyRating`, `companyReviewCount`, `companyPerks[]`, `companyProfileId` — structured employer profile when one exists\n- `normalizedRoleTitle`, `normalizedLocation`, `broaderLocationName`, `normalizedCompanyName` — SEEK's normalized taxonomy values\n- `roleId`, `country`, `applyUrl`, `jobUrl`\n- `recordType: \"job\"`, `scrapedAt`\n\n## Input\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `mode` | string | `searchJobs` | `searchJobs` / `browseByCategory` / `browseByLocation` / `browseByCompany` / `latestJobs` / `byJobId` / `byUrls` |\n| `country` | string | `HK` | `HK` (Hong Kong) or `TH` (Thailand); `byUrls` uses each link's host instead |\n| `keywords` | string | – | Job title / skill / company keyword; leave empty to browse every job in the selected mode |\n| `location` | string | – | Area/city, e.g. `Central`, `Kowloon`, `Bangkok` (required for `browseByLocation`) |\n| `classification` | array | – | One or more job category IDs (required for `browseByCategory`) — 29 categories available in the UI multi-select; picking several matches ANY of them |\n| `subclassification` | string | – | One or more numeric sub-category IDs, comma-separated, to narrow `classification` further, e.g. `6038` = Project Engineering under Engineering — find one via the `subCategoryId` output field on any scraped job |\n| `workType` | array | – | Multi-select: `242` Full time / `243` Part time / `244` Contract/Temp / `245` Casual/Vacation |\n| `workArrangement` | array | – | Multi-select: `1` On-site / `2` Hybrid / `3` Remote |\n| `advertiserId` | string | – | One or more numeric JobsDB company/advertiser IDs, comma-separated (required for `browseByCompany`) — find one via the `advertiserId` output field on any scraped job |\n| `jobId` | string | – | One or more numeric JobsDB job IDs, comma-separated or as an array (required for `byJobId`) — find one via the `jobId` output field on any previously scraped job |\n| `urls` | array | – | JobsDB job links or numeric IDs (required for `byUrls`); HK and TH links can be mixed |\n| `includeDetails` | boolean | `true` | Fetch full description, expiry, applicant signals, screening questions, badges and company profile (one extra request per job) |\n| `descriptionFormat` | string | `text` | `text` / `html` / `both` |\n| `salaryMin` | int | – | Minimum salary |\n| `salaryMax` | int | – | Maximum salary |\n| `salaryType` | string | `annual` | `annual` / `monthly` / `hourly` — period `salaryMin`/`salaryMax` are expressed in |\n| `dateRange` | string | – | Only jobs posted in the last `1`/`3`/`7`/`14`/`31` days |\n| `sortMode` | string | `KeywordRelevance` | `KeywordRelevance` or `ListedDate` (newest first) |\n| `maxItems` | int | `50` | Hard cap on emitted records (1–1000) |\n\n### Example: search with filters and full details\n\n```json\n{\n  \"mode\": \"searchJobs\",\n  \"country\": \"HK\",\n  \"keywords\": \"data analyst\",\n  \"workType\": [\"242\"],\n  \"salaryMin\": 20000,\n  \"salaryMax\": 40000,\n  \"salaryType\": \"monthly\",\n  \"dateRange\": \"7\",\n  \"sortMode\": \"ListedDate\",\n  \"includeDetails\": true,\n  \"descriptionFormat\": \"both\",\n  \"maxItems\": 50\n}\n```\n\n### Example: browse a category\n\n```json\n{\n  \"mode\": \"browseByCategory\",\n  \"country\": \"TH\",\n  \"classification\": [\"6281\"],\n  \"maxItems\": 100\n}\n```\n\n### Example: newest jobs in a location\n\n```json\n{\n  \"mode\": \"browseByLocation\",\n  \"country\": \"HK\",\n  \"location\": \"Kowloon\",\n  \"maxItems\": 50\n}\n```\n\n### Example: all current listings from one company\n\n```json\n{\n  \"mode\": \"browseByCompany\",\n  \"country\": \"HK\",\n  \"advertiserId\": \"61282529\",\n  \"maxItems\": 50\n}\n```\n\n### Example: remote jobs only\n\n```json\n{\n  \"mode\": \"searchJobs\",\n  \"country\": \"HK\",\n  \"keywords\": \"developer\",\n  \"workArrangement\": [\"3\"],\n  \"maxItems\": 50\n}\n```\n\n### Example: look up specific jobs by ID\n\n```json\n{\n  \"mode\": \"byJobId\",\n  \"country\": \"HK\",\n  \"jobId\": \"93889825,93902058\"\n}\n```\n\n### Example: paste job links from both markets\n\n```json\n{\n  \"mode\": \"byUrls\",\n  \"urls\": [\n    \"https://hk.jobsdb.com/job/95849989\",\n    \"https://th.jobsdb.com/job/95856128\"\n  ],\n  \"includeDetails\": true,\n  \"descriptionFormat\": \"text\"\n}\n```\n\n## Use cases\n\n- **Job seekers & career sites** — aggregate fresh listings by role, category, location, or remote/hybrid arrangement, with the full ad body\n- **Recruiters & staffing agencies** — monitor competitor job postings, screening questions, and applicant demand\n- **Labor market research** — track hiring demand and posted salary ranges across categories and regions in HK/TH\n- **HR tech / salary benchmarking tools** — collect structured salary ranges plus company industry, size and perks\n- **Lead generation** — find companies actively hiring in a given industry\n- **Competitor/company tracking** — pull every current listing from a specific employer with `mode=browseByCompany`\n- **Shortlist refresh** — re-check saved job URLs for expiry, updated content and current applicant volume\n\n## FAQ\n\n**What's JobsDB?** A leading job board across Hong Kong and Thailand, part of the SEEK employment network. See [jobsdb.com](https://hk.jobsdb.com).\n\n**Do I need to log in or provide cookies?** No. This actor uses JobsDB's public job-search and job-detail endpoints — no account, API key, or proxy required.\n\n**What does `includeDetails` add?** One extra request per job fills the full description, abstract, expiry/status, applicant-demand signals, screening questions, badges, structured location and company profile. Turn it off to scrape more listings faster at a lower cost.\n\n**Why is `descriptionText` missing on some jobs?** A small number of advertisers publish only an external apply link. When the ad has no body, the field is omitted rather than filled with a placeholder.\n\n**Why is `salaryLabel` missing on some jobs?** Not every advertiser discloses a salary. The field is only included when JobsDB displays one.\n\n**Why is `salaryMinValue`/`salaryMaxValue` missing even though `salaryLabel` is present?** A handful of advertisers enter free text instead of a number (e.g. `Negotiable`, `Competitive`, `Attractive Salary Package`). The structured fields are only added when `salaryLabel` is confidently a numeric amount or range — never a guessed value.\n\n**Why are applicant signals missing on some jobs?** JobsDB only exposes them for jobs with enough application activity, and external-apply jobs omit them entirely. The field is absent rather than zeroed when the source has no signal.\n\n**What's the difference between `category` and `subCategory`?** JobsDB classifies each job into a broad category (e.g. `Information & Communication Technology`) and a more specific sub-category (e.g. `Developers/Programmers`).\n\n**How fresh is the data?** Real-time — every run queries JobsDB's live APIs directly.\n\n**Why does `jobUrl` sometimes fail a `curl`/HEAD check?** JobsDB's listing pages sit behind bot-detection that blocks non-browser HTTP clients (no cookies, no JS) while allowing real browsers through normally. The URLs themselves are valid, live links — opening them in a browser works fine. `logoUrl` and `coverImageUrl` point at JobsDB's image CDN and are directly fetchable without a browser.\n\n**Can I use Thai-script place names for `location` in Thailand?** Yes. `location: \"กรุงเทพ\"` and `location: \"Bangkok\"` both work for `country: \"TH\"` — the actor automatically detects Thai-script input and queries JobsDB's Thai-language endpoint so the place name resolves correctly. `category`, `workTypes`, and `workArrangements` are always normalized back to English in the output either way, so filtering/grouping by those fields stays consistent regardless of which locale a record was fetched under. `teaser`, `descriptionText`, `bulletPoints`, `screeningQuestions`, and `subCategory` remain in whatever language the advertiser/JobsDB used (Thai for Thai-script location lookups), since those are free text with no fixed English translation to fall back on.\n\n**Which markets are supported?** Hong Kong and Thailand, JobsDB's two active markets. Other former JobsDB markets (Singapore, Indonesia, Philippines) have migrated to the sister JobStreet/Jora brands and are out of scope for this actor.\n\n**How do I get an `advertiserId` to use with `mode=browseByCompany`?** Run any other mode first and copy the `advertiserId` field from a job posted by the company you're interested in, then feed it back in as input.\n\n**How do I use `subclassification`?** It narrows a `classification` down to a specific sub-category (e.g. only `Developers/Programmers` inside `Information & Communication Technology`). Copy a value from the `subCategoryId` output field on a job you've already scraped in the category you want, then pass it as input on a later run.\n\n**Can I filter by more than one category, work type, or work arrangement at once?** Yes. `classification` and `workType` and `workArrangement` are multi-select — pick as many as you want and a job matching ANY of your selections is returned. `subclassification` and `advertiserId` accept multiple comma-separated IDs the same way, e.g. `advertiserId: \"61282529,61359613\"` pulls current listings from both companies in one run.\n\n**How do I re-fetch a specific job I already scraped?** Use `mode: \"byJobId\"` with the `jobId` values, or `mode: \"byUrls\"` with the saved `jobUrl` links — comma-separated arrays are fine. Useful for refreshing a shortlist or checking whether a listing is still live.\n",
      "input": {
        "title": "JobsDB Scraper Input",
        "description": "Search JobsDB job listings, browse by category or location, or pull the newest postings across Hong Kong and Thailand.",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "mode": {
            "title": "Mode",
            "type": "string",
            "description": "What to fetch.",
            "editor": "select",
            "enum": [
              "searchJobs",
              "browseByCategory",
              "browseByLocation",
              "browseByCompany",
              "latestJobs",
              "byJobId",
              "byUrls"
            ],
            "enumTitles": [
              "Search jobs by keyword",
              "Browse jobs by category",
              "Browse jobs by location",
              "Browse jobs by company (advertiserId)",
              "Latest jobs (newest first, site-wide)",
              "Look up job(s) by jobId",
              "Look up job URLs"
            ],
            "default": "searchJobs",
            "prefill": "searchJobs"
          },
          "country": {
            "title": "Country",
            "type": "string",
            "description": "Which JobsDB market to scrape.",
            "editor": "select",
            "enum": [
              "HK",
              "TH"
            ],
            "enumTitles": [
              "Hong Kong (hk.jobsdb.com)",
              "Thailand (th.jobsdb.com)"
            ],
            "default": "HK",
            "prefill": "HK"
          },
          "keywords": {
            "title": "Keywords",
            "type": "string",
            "description": "Job title, skill, or company to search for. Leave empty to browse every job in the selected mode; an explicit value narrows the results.",
            "editor": "textfield",
            "default": "",
            "prefill": "engineer"
          },
          "location": {
            "title": "Location",
            "type": "string",
            "description": "Free-text location/area to browse or filter by, e.g. `Central`, `Kowloon`, `Bangkok`. Required for mode=browseByLocation.",
            "editor": "textfield",
            "default": "",
            "prefill": ""
          },
          "classification": {
            "title": "Category",
            "type": "array",
            "description": "Job categor(y/ies). Pick one or more (OR match) — required for mode=browseByCategory; optional filter for other modes. Leave empty for any category.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "1200",
                "1203",
                "1204",
                "1206",
                "1209",
                "1210",
                "1211",
                "1212",
                "1214",
                "1216",
                "1220",
                "1223",
                "1225",
                "6008",
                "6043",
                "6058",
                "6076",
                "6092",
                "6123",
                "6163",
                "6205",
                "6246",
                "6251",
                "6263",
                "6281",
                "6304",
                "6317",
                "6362",
                "7019"
              ],
              "enumTitles": [
                "Accounting",
                "Banking & Financial Services",
                "Call Centre & Customer Service",
                "Construction",
                "Engineering",
                "Government & Defence",
                "Healthcare & Medical",
                "Hospitality & Tourism",
                "Insurance & Superannuation",
                "Legal",
                "Real Estate & Property",
                "Science & Technology",
                "Trades & Services",
                "Marketing & Communications",
                "Retail & Consumer Products",
                "Mining, Resources & Energy",
                "Consulting & Strategy",
                "Manufacturing, Transport & Logistics",
                "Education & Training",
                "Community Services & Development",
                "Farming, Animals & Conservation",
                "Sport & Recreation",
                "Administration & Office Support",
                "Design & Architecture",
                "Information & Communication Technology",
                "Advertising, Arts & Media",
                "Human Resources & Recruitment",
                "Sales",
                "CEO & General Management"
              ]
            },
            "uniqueItems": true,
            "default": [],
            "prefill": []
          },
          "subclassification": {
            "title": "Sub-category ID(s)",
            "type": "string",
            "description": "One or more numeric JobsDB sub-category IDs (comma-separated for OR match) to narrow a `classification` further (e.g. `6038` = Project Engineering under Engineering). Get valid IDs from the `subCategoryId` field on a previously scraped job. Optional filter for any mode.",
            "editor": "textfield",
            "default": "",
            "prefill": ""
          },
          "workType": {
            "title": "Work type",
            "type": "array",
            "description": "Filter by employment type. Pick one or more (OR match). Leave empty for any work type.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "242",
                "243",
                "244",
                "245"
              ],
              "enumTitles": [
                "Full time",
                "Part time",
                "Contract/Temp",
                "Casual/Vacation"
              ]
            },
            "uniqueItems": true,
            "default": [],
            "prefill": []
          },
          "workArrangement": {
            "title": "Work arrangement",
            "type": "array",
            "description": "Filter by on-site/hybrid/remote work arrangement. Pick one or more (OR match). Leave empty for any arrangement.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "1",
                "2",
                "3"
              ],
              "enumTitles": [
                "On-site",
                "Hybrid",
                "Remote"
              ]
            },
            "uniqueItems": true,
            "default": [],
            "prefill": []
          },
          "advertiserId": {
            "title": "Company / advertiser ID(s)",
            "type": "string",
            "description": "One or more numeric JobsDB advertiser IDs (comma-separated for OR match) to browse all current listings from one or more employers/agencies. Required for mode=browseByCompany; get an ID from the `advertiserId` field on any previously scraped job. Also works as an optional filter in other modes.",
            "editor": "textfield",
            "default": "",
            "prefill": ""
          },
          "jobId": {
            "title": "Job ID(s)",
            "type": "string",
            "description": "One or more numeric JobsDB job IDs (comma-separated, or pass an array) to look up specific listings directly. Required for mode=byJobId; get an ID from the `jobId` field on any previously scraped job. Ignored in other modes.",
            "editor": "textfield",
            "default": "",
            "prefill": ""
          },
          "urls": {
            "title": "Job URLs or IDs",
            "type": "array",
            "description": "One or more JobsDB job links (hk.jobsdb.com/job/… or th.jobsdb.com/job/…) or numeric job IDs to look up directly. Required for mode=byUrls; the market is taken from each link.",
            "editor": "stringList",
            "items": {
              "type": "string"
            },
            "default": [],
            "prefill": []
          },
          "includeDetails": {
            "title": "Include full job details",
            "type": "boolean",
            "description": "Fetch each listing's full description, expiry status, applicant-demand signals, screening questions, badges and company profile. Adds one request per job; turn off for the fastest, cheapest run.",
            "default": true,
            "prefill": true
          },
          "descriptionFormat": {
            "title": "Description format",
            "type": "string",
            "description": "How the full job description is returned when details are included.",
            "editor": "select",
            "enum": [
              "text",
              "html",
              "both"
            ],
            "enumTitles": [
              "Plain text",
              "HTML",
              "Both"
            ],
            "default": "text",
            "prefill": "text"
          },
          "salaryMin": {
            "title": "Salary min",
            "type": "integer",
            "description": "Minimum salary (in the local currency, per `salaryType` period). Leave unset for no lower bound.",
            "minimum": 0,
            "maximum": 10000000
          },
          "salaryMax": {
            "title": "Salary max",
            "type": "integer",
            "description": "Maximum salary (in the local currency, per `salaryType` period). Leave unset for no upper bound.",
            "minimum": 0,
            "maximum": 10000000
          },
          "salaryType": {
            "title": "Salary period",
            "type": "string",
            "description": "Period the salary min/max values are expressed in.",
            "editor": "select",
            "enum": [
              "annual",
              "monthly",
              "hourly"
            ],
            "enumTitles": [
              "Annual",
              "Monthly",
              "Hourly"
            ],
            "default": "annual",
            "prefill": "annual"
          },
          "dateRange": {
            "title": "Date posted",
            "type": "string",
            "description": "Only include jobs posted within this window.",
            "editor": "select",
            "enum": [
              "",
              "1",
              "3",
              "7",
              "14",
              "31"
            ],
            "enumTitles": [
              "Any time",
              "Past 24 hours",
              "Past 3 days",
              "Past 7 days",
              "Past 14 days",
              "Past 31 days"
            ],
            "default": "",
            "prefill": ""
          },
          "sortMode": {
            "title": "Sort by",
            "type": "string",
            "description": "Result ordering.",
            "editor": "select",
            "enum": [
              "KeywordRelevance",
              "ListedDate"
            ],
            "enumTitles": [
              "Relevance",
              "Date (newest first)"
            ],
            "default": "KeywordRelevance",
            "prefill": "KeywordRelevance"
          },
          "maxItems": {
            "title": "Max items",
            "type": "integer",
            "description": "Hard cap on emitted records.",
            "minimum": 1,
            "maximum": 1000,
            "default": 50,
            "prefill": 3
          }
        },
        "required": [
          "mode",
          "country"
        ]
      },
      "output": {
        "actorOutputSchemaVersion": 1,
        "title": "JobsDB Hong Kong & Thailand Job Listings Extractor Output",
        "description": "Job listings from JobsDB's Hong Kong and Thailand markets, with title, company, location, salary, category, work type, and posting date.",
        "properties": {
          "jobs": {
            "type": "string",
            "title": "JobsDB job listings",
            "description": "Dataset containing all scraped JobsDB job listings.",
            "template": "{{links.apiDefaultDatasetUrl}}/items"
          }
        }
      },
      "storages": {
        "dataset": {
          "actorSpecification": 1,
          "views": {
            "overview": {
              "title": "Jobs",
              "transformation": {
                "fields": [
                  "title",
                  "companyName",
                  "location",
                  "salaryLabel",
                  "workTypes",
                  "listingDateDisplay",
                  "jobUrl"
                ]
              },
              "display": {
                "component": "table",
                "properties": {}
              }
            }
          }
        }
      },
      "environmentVariables": {}
    },
    "buildNumber": "1.0.2",
    "imageDigest": "e6828951c62e5fec033b257ff6da208c130d94d935b2f54dceda72cd273e1868",
    "actVersion": {
      "sourceType": "SOURCE_FILES",
      "buildTag": "latest",
      "versionNumber": "1.0"
    },
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0.010166666666666666
    }
  }
}