# KitaLulus Jobs Scraper Indonesia (`produkdigitalali/kitalulus-jobs-scraper-indonesia`) Actor

Unofficial KitaLulus jobs scraper for salary, education, location, recruiter requirements, skills, benefits, and recurring hiring intelligence across Indonesia.

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

## Pricing

from $4.00 / 1,000 kitalulus job results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

## KitaLulus Jobs Scraper Indonesia

> **Unofficial and independent.** This Actor is not affiliated with, endorsed by, sponsored by, or operated by KitaLulus.

### What is KitaLulus Jobs Scraper Indonesia?

KitaLulus Jobs Scraper Indonesia is an Apify Actor for extracting public KitaLulus job listings into structured, analysis-ready data.

Use it for:

- job aggregation and recruitment data pipelines,
- salary and regional labour-market research,
- mass-recruitment and hiring-signal analysis,
- company and recruiter intelligence,
- recurring job monitoring,
- dashboards, APIs, automations, and AI workflows.

Start with the default KitaLulus jobs URL for a broad run, or provide supported listing, company, location, or direct job-detail URLs.

Each result represents one public vacancy and can be exported from Apify as JSON, CSV, or Excel, or consumed through the Apify API.

### Why use this Actor?

- Extract public KitaLulus jobs without manually copying listings.
- Capture salary ranges and negotiable salary signals.
- Collect company, location, education, employment type, and work mode.
- Extract recruiter requirements, skills, benefits, schedules, and company information.
- Track repeat runs as `NEW`, `UPDATED`, `UNCHANGED`, and verified `EXPIRED`.
- Use stable job IDs and meaningful fingerprints for incremental monitoring.
- Feed structured job data into scheduled workflows, dashboards, databases, and AI applications.

### What data can you extract?

| Field | Meaning |
|---|---|
| `jobId` | Stable Actor-generated job identifier |
| `jobSlug` | Public KitaLulus job slug |
| `title` | Job title |
| `companyName` | Employer name |
| `companyUrl` | Public KitaLulus company profile when available |
| `jobUrl` | Canonical public KitaLulus job URL |
| `sourceListUrl` | Listing/company/location URL where the job was discovered |
| `location` | City/regency and province text |
| `minimumEducation` | Public minimum education requirement |
| `employmentType` | Full-Time, Contract, Freelance, Internship, etc. |
| `workMode` | On-site, Hybrid, Remote, or Fieldwork when public |
| `salaryMin`, `salaryMax` | Normalized public salary range |
| `salaryCurrency` | Salary currency, normally IDR for Rp listings |
| `salaryNegotiable` | Whether salary is shown as negotiable |
| `promoted` | Whether the listing is publicly marked `Dipromosikan` |
| `updatedText` | Public last-updated text |
| `status` | Normalized vacancy status when determinable |
| `description` | Public job description |
| `recruiterRequirements` | Public recruiter requirements |
| `skills` | Public required skills |
| `benefits` | Public benefits |
| `workDays`, `workHours` | Public working schedule when available |
| `industry` | Public company industry |
| `companyDescription` | Public company description from the job page or public company profile |
| `changeType` | Monitoring state: `NEW`, `UPDATED`, `UNCHANGED`, or `EXPIRED` |
| `observedAt` | UTC observation timestamp |
| `scrapedAt` | UTC scrape timestamp |
| `fingerprint` | Hash of meaningful normalized fields used for change detection |
| `rawCardText` | Normalized listing-card text retained for diagnostics |

Fields can be `null` or empty when the public source does not provide that value for a vacancy.

### How to scrape KitaLulus jobs

1. Keep the default `https://www.kitalulus.com/lowongan` URL or provide another supported public KitaLulus URL.
2. Set **Maximum jobs** to the number of unique vacancies you want.
3. Keep **Fetch full job details** enabled for the richest dataset.
4. Keep browser fallback enabled when you need additional load-more discovery.
5. Run the Actor.
6. Open the default dataset or export the results as JSON, CSV, or Excel.

#### Quick-start input

```json
{
  "startUrls": [
    {
      "url": "https://www.kitalulus.com/lowongan"
    }
  ],
  "maxItems": 30,
  "fetchDetails": true,
  "useBrowserForMore": true,
  "maxLoadMoreClicks": 20,
  "concurrency": 8,
  "monitorMode": false,
  "emitChangesOnly": false,
  "detectExpired": false,
  "maxExpiredChecks": 50,
  "monitorKey": "default",
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "requestTimeoutSeconds": 30,
  "maxRetries": 3
}
```

### Monitoring and incremental job tracking

Enable `monitorMode` to compare repeat runs using stable job IDs and meaningful fingerprints.

- `NEW` — first observation in the selected monitoring scope.
- `UPDATED` — a meaningful job field changed.
- `UNCHANGED` — the meaningful fingerprint is unchanged.
- `EXPIRED` — a previously seen vacancy disappeared and its detail URL was independently verified as closed or unavailable.

Relative labels such as `Terakhir diperbarui hari ini` are retained in the output but excluded from the meaningful fingerprint, so the passage of time alone does not create false `UPDATED` events.

With `emitChangesOnly: true`, `UNCHANGED` jobs are not written to the dataset. A successful monitoring run can therefore return **zero dataset items**. Check `RUN_SUMMARY` for comparison counts and monitoring diagnostics.

A job disappearing from the current listing does **not** automatically become `EXPIRED`. The Actor verifies expiry candidates and uses repeated confirmation to reduce false-closure and false-expiry signals caused by transient public-site responses.

### Output example

```json
{
  "jobId": "stable-id",
  "title": "Backend Engineer - Short-term Contract (KLI-75)",
  "companyName": "KitaLulus",
  "location": "Jakarta Selatan, DKI Jakarta",
  "minimumEducation": "Minimal S1",
  "employmentType": "Full-Time",
  "workMode": "On-site",
  "salaryMin": 15000000,
  "salaryMax": 20000000,
  "salaryCurrency": "IDR",
  "salaryNegotiable": false,
  "status": "Open",
  "skills": [
    "Java",
    "PostgreSQL"
  ],
  "changeType": null,
  "jobUrl": "https://www.kitalulus.com/lowongan/detail/...",
  "scrapedAt": "2026-08-19T07:03:57Z"
}
```

### Pricing

This Actor uses transparent pay-per-result pricing.

- **Job result:** `$0.004` per result
- **Equivalent:** `$4.00 per 1,000 results`
- **Actor start:** `$0.00005` per run
- **Platform usage:** included in the Actor price

Examples, excluding only the tiny Actor-start charge:

| Results | Result charge |
|---:|---:|
| 100 | $0.40 |
| 500 | $2.00 |
| 1,000 | $4.00 |

You are charged for results written to the default dataset. With monitoring and `emitChangesOnly: true`, unchanged jobs are not emitted, so repeat monitoring runs can produce few or even zero charged result items.

### Reliability and scale

The Actor has been validated with representative cloud runs at 100, 500, and larger-volume job counts, followed by a final 200-job production validation.

The scraper includes:

- HTTP retries for temporary network failures,
- browser-assisted load-more discovery,
- retry handling for transient load-more failures,
- stable deduplication by canonical job URL,
- repeated confirmation before accepting a closed-job response,
- safe expiry verification that does not convert inconclusive network failures into `EXPIRED`.

Actual runtime and maximum reachable result count depend on current KitaLulus inventory and public-site responsiveness.

### API, schedules, and automation

The Actor can be called through the Apify API or run on a schedule.

Typical recurring workflows include:

- regional hiring dashboards,
- job aggregation,
- salary-market tracking,
- mass-recruitment monitoring,
- company hiring-signal research,
- recruitment intelligence,
- data feeds for analytics and AI applications.

For incremental monitoring, keep the same `monitorKey` and comparable start URLs across repeat runs.

### Limitations

- Only public KitaLulus pages are supported.
- Available fields depend on what KitaLulus publishes for each vacancy.
- Public website markup, pagination, and response behavior can change.
- Relative update labels are preserved as source text rather than converted into invented timestamps.
- Large result targets can require browser-assisted load-more discovery and therefore take longer.
- A requested `maxItems` value is a ceiling, not a guarantee that the public site currently exposes that many reachable jobs.

### Responsible use and support

Use this Actor responsibly and only for publicly available information. Users are responsible for ensuring that their collection and use of data complies with applicable laws, contractual obligations, and platform requirements.

**This Actor is unofficial and independent. It is not affiliated with, endorsed by, sponsored by, or operated by KitaLulus.**

If extraction quality degrades because the public site changes, open an Actor issue and include the run ID, input, affected public job URL, and a short description of the unexpected output.

# Actor input Schema

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

One or more public KitaLulus listing, company, location, or direct job-detail URLs. Keep the default for a broad first run.

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

Maximum number of unique jobs selected across all start URLs. The default is intentionally small for a quick smoke test.

## `fetchDetails` (type: `boolean`):

Fetch public detail pages for employment type, work mode, education, salary, recruiter requirements, skills, benefits, description, and company information.

## `useBrowserForMore` (type: `boolean`):

Use Playwright only when the initial HTML does not expose enough jobs and the KitaLulus load-more control is needed.

## `maxLoadMoreClicks` (type: `integer`):

Safety limit for Lebih Banyak / Tampilkan Lebih Banyak interactions per listing URL.

## `concurrency` (type: `integer`):

Maximum number of public job-detail HTTP requests processed at the same time.

## `monitorMode` (type: `boolean`):

Persist state and classify repeat-run jobs as NEW, UPDATED, or UNCHANGED. Confirmed unavailable jobs can be emitted as EXPIRED when expiry verification is enabled.

## `emitChangesOnly` (type: `boolean`):

With monitor mode enabled, emit only NEW, UPDATED, and EXPIRED records. A successful run can have an empty dataset when all jobs are UNCHANGED.

## `detectExpired` (type: `boolean`):

Verify missing previously seen jobs before classifying them as EXPIRED. Missing from a listing alone is never sufficient.

## `maxExpiredChecks` (type: `integer`):

Maximum number of missing prior jobs whose detail URLs can be verified in a single run.

## `monitorKey` (type: `string`):

Optional label that isolates persistent state for different recurring workflows. Keep the same key and start URLs for comparable repeat runs.

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

Optional Apify Proxy or custom proxy settings. Direct connections are the default; enable proxying only when needed.

## `requestTimeoutSeconds` (type: `integer`):

Maximum duration of a listing or detail HTTP request before retry handling.

## `maxRetries` (type: `integer`):

Retries for temporary network errors, timeouts, and retryable HTTP responses.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.kitalulus.com/lowongan"
    }
  ],
  "maxItems": 30,
  "fetchDetails": true,
  "useBrowserForMore": true,
  "maxLoadMoreClicks": 20,
  "concurrency": 8,
  "monitorMode": false,
  "emitChangesOnly": false,
  "detectExpired": false,
  "maxExpiredChecks": 50,
  "monitorKey": "default",
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "requestTimeoutSeconds": 30,
  "maxRetries": 3
}
```

# Actor output Schema

## `results` (type: `string`):

Structured public job records. With emitChangesOnly enabled this can be empty when all seen jobs are UNCHANGED.

## `summary` (type: `string`):

Discovery count, emitted results, monitoring counts, state sizes, expiry diagnostics, and warnings.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.kitalulus.com/lowongan"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("produkdigitalali/kitalulus-jobs-scraper-indonesia").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 = { "startUrls": [{ "url": "https://www.kitalulus.com/lowongan" }] }

# Run the Actor and wait for it to finish
run = client.actor("produkdigitalali/kitalulus-jobs-scraper-indonesia").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 '{
  "startUrls": [
    {
      "url": "https://www.kitalulus.com/lowongan"
    }
  ]
}' |
apify call produkdigitalali/kitalulus-jobs-scraper-indonesia --silent --output-dataset

```

## MCP server setup

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

```

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/pdcd57MwlkgRenMpW/builds/jzR5W1VyBcySgkgFr/openapi.json
