# JobBKK Jobs Search Scraper (`jobsapi/jobbkk-jobs-search-scraper`) Actor

Extracts complete, source-backed JobBKK job records from public search and detail pages.

- **URL**: https://apify.com/jobsapi/jobbkk-jobs-search-scraper.md
- **Developed by:** [Jobs API](https://apify.com/jobsapi) (community)
- **Security:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 jobbkk job records

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## JobBKK Jobs Search Scraper

Extract complete public job records from [JobBKK](https://www.jobbkk.com/) search and detail pages. The Actor uses lightweight HTTP requests and source-provided JobPosting JSON-LD; it does not require credentials or bypass access controls.

### Modes

- search: discover detail URLs from startUrl, bounded by maxPages.
- single: fetch one public detail URL.
- multiple: fetch public detail URLs.
- startUrls: accept a mixture of listing and detail URLs.

maxItems counts only complete, unique records. Incomplete or mismatched candidates are excluded from the dataset and documented in DIAGNOSTICS. Search mode examines a bounded surplus of candidates so one incomplete posting does not silently reduce a healthy run. Cloud runs use Apify Proxy when available, while local runs may fall back to direct public access. maxRequests and maxResponseBytes bound network cost and response size.

### Input example

```json
{
  "mode": "search",
  "startUrl": "https://www.jobbkk.com/jobs/lists",
  "maxItems": 10,
  "maxPages": 2,
  "maxConcurrency": 3,
  "requestTimeoutSecs": 30,
  "includeRawJsonLd": false
}
```

Keep maxPages, maxItems, and concurrency conservative. Direct public access currently works; no proxy is required by default.

### Output

Every dataset row contains a stable JobBKK ID, canonical URL, title, employer, plain-text description of at least 80 characters, non-empty location, and source-backed application URL. Available dates, salary, employment type, category, company URL, and logo are included without invented placeholders. Rows are marked complete, detail-verified, and include a content hash, field coverage, and cloud/proxy quality metadata. Request receipts and diagnostics are stored in KVS, never as dataset rows.

The platform lifecycle is separate from the collection outcome. `platformStatus` is only `SUCCEEDED` or `FAILED`; `resultStatus` is `COMPLETE`, `LIMITED`, `SKIPPED`, `DEFERRED`, `NO_DATA`, or `FAILED`. Explicit HTTP 401/403/429/451 or visible source-authored denial/challenge evidence stops scheduling, cancels active detail requests, discards staged rows, and reports `SUCCEEDED`/`SKIPPED`. Generic network/tool/transport errors report `DEFERRED`; if rows were stored before a later non-block error, the run remains `SUCCEEDED`/`LIMITED`. Fatal zero-row errors call `Actor.fail()` and report `FAILED`/`FAILED`. Dataset counts advance only after a successful `Actor.pushData()`.

Run evidence is stored separately:

- RUN_SUMMARY: status, counts, quality metrics, timing, and local/cloud provenance.
- DIAGNOSTICS: failed requests and rejected candidates.
- SOURCE_RECEIPTS: safe URL/status/attempt/timing evidence without headers, cookies, or bodies.

### Local development

```powershell
npm test
npm run lint
apify run --purge --input-file test/fixtures/smoke-input.json
npm run validate:dataset
```

Deploy with apify push, then run a small cloud input and compare its dataset and KVS receipts with the local smoke run.

# Actor input Schema

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

search discovers detail pages; single and multiple fetch supplied detail URLs; startUrls accepts either kind.

## `startUrl` (type: `string`):

Public JobBKK search/listing URL, or the single detail URL for single mode.

## `startUrls` (type: `array`):

Public JobBKK listing and/or detail URLs. Used by startUrls mode.

## `url` (type: `string`):

A public JobBKK /jobs/detail... URL used by single mode.

## `urls` (type: `array`):

Public JobBKK detail URLs used by multiple mode.

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

Maximum number of complete unique records to save.

## `maxPages` (type: `integer`):

Maximum number of search pages to fetch.

## `maxRequests` (type: `integer`):

Hard upper bound on listing and detail network requests, including retries.

## `maxResponseBytes` (type: `integer`):

Reject public pages larger than this many bytes.

## `maxConcurrency` (type: `integer`):

Maximum simultaneous detail requests.

## `requestTimeoutSecs` (type: `integer`):

Timeout for each public HTTP request.

## `includeRawJsonLd` (type: `boolean`):

Adds the source JobPosting object to each record for audit use.

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

Apify Proxy configuration applied to public JobBKK requests. The default uses Apify Proxy when available.

## Actor input object example

```json
{
  "mode": "search",
  "startUrl": "https://www.jobbkk.com/jobs/lists",
  "startUrls": [],
  "urls": [],
  "maxItems": 20,
  "maxPages": 2,
  "maxRequests": 120,
  "maxResponseBytes": 10485760,
  "maxConcurrency": 3,
  "requestTimeoutSecs": 30,
  "includeRawJsonLd": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `runSummary` (type: `string`):

No description

## `diagnostics` (type: `string`):

No description

## `sourceReceipts` (type: `string`):

No description

## `health` (type: `string`):

No description

## `metadata` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("jobsapi/jobbkk-jobs-search-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("jobsapi/jobbkk-jobs-search-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 '{}' |
apify call jobsapi/jobbkk-jobs-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jobsapi/jobbkk-jobs-search-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/uF6bb3ByX9HMF5XzU/builds/gyYgUiVlKfBVPH8jd/openapi.json
