# Karir.com Scraper (`zucchini_gopher_m2v/karir-com-scraper`) Actor

Search Indonesian job listings on Karir.com by keyword and pull full job details (requirements, responsibilities, salary range, company info) -- no account or API key needed.

- **URL**: https://apify.com/zucchini\_gopher\_m2v/karir-com-scraper.md
- **Developed by:** [Faisal Ahdan naufal](https://apify.com/zucchini_gopher_m2v) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 results

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/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

## Karir.com Scraper

Search Indonesian job listings on Karir.com by keyword and get back full job details — requirements, responsibilities, salary range, and company info.

### Why use this actor

- No account, login, or API key required
- Full job requirements and responsibilities text, not just a title and a link
- Salary range when the employer discloses it
- Company profile (industry, description, subscriber count) included with every job
- Automatic pagination up to the number of results you ask for
- Also works the other way: give it specific job links or IDs and it fetches full detail for exactly those

### How it works

Pick a `mode`:

1. **`search`** — searches job titles by keyword and returns full job details for each match, with pagination handled automatically.
2. **`detail`** — fetches full detail for one or more specific job IDs/URLs you already have.

You don't need to manage any browsers or automatic retries — just provide a keyword or a list of job links.

### Input

**Keyword search (default mode):**

```json
{
  "mode": "search",
  "keyword": "marketing",
  "sortOrder": "newest",
  "maxResults": 50,
  "fetchFullDetails": true,
  "maxConcurrency": 4
}
```

**Job detail by URL/ID:**

```json
{
  "mode": "detail",
  "opportunityId": "https://karir.com/opportunities/1401468"
}
```

| Field | Type | Description |
|---|---|---|
| `mode` | string | `"search"` (default) or `"detail"`. |
| `keyword` | string | `search` mode — searches job titles (e.g. `"marketing"`, `"sales"`, `"programmer"`). Leave empty to list all currently active jobs. |
| `sortOrder` | string | `search` mode — `newest` (default), `priority`, `popular`, or `highest_salary`. |
| `maxResults` | integer | `search` mode — max jobs to return, pagination followed automatically. Default 50. |
| `fetchFullDetails` | boolean | `search` mode — when on (default), each result includes full requirements/responsibilities/salary/company data (one extra request per job). Turn off for a faster run that only needs title, company, location, salary range, and posted date. |
| `minSalary` / `maxSalary` | integer | `search` mode — optional salary filter (IDR). |
| `level` / `workplace` | string | `search` mode — optional filters, as shown in Karir.com's own search filters. |
| `locationIds` / `jobFunctionIds` / `industryIds` / `degreeIds` / `companyIds` | array | `search` mode — optional advanced filters using Karir.com's own numeric filter IDs. |
| `opportunityId` / `opportunityIds` | string / array | `detail` mode — a job's numeric ID or its full `karir.com/opportunities/...` URL. |
| `maxConcurrency` | integer | How many detail requests run in parallel. Default 4. |
| `proxyConfiguration` | object | Apify Proxy settings. Residential is on by default. |

### Output

**Search mode (`fetchFullDetails: true`, default)** — real output, truncated for readability:

```json
{
  "_input": "keyword:marketing",
  "_source": "S1-detail-api",
  "_scrapedAt": "2026-08-26T02:28:09Z",
  "id": 1401222,
  "job_position": "Venture Capital Officer (Marketing)",
  "location": "Denpasar",
  "address": "Jl. Tukad Badung No 126A, Renon Denpasar Selatan, Bali 80226",
  "requirements": "<ul><li>Pendidikan Minimal D3 Semua Jurusan</li><li>Memiliki Pengalaman Bekerja Minimal 2 Tahun sebagai Account Officer...</li></ul>",
  "responsibilities": "<p></p><ul><li>Membuat rencana pemasaran pembiayaan harian untuk pencapaian target</li>...</ul>",
  "salary_info": "LABEL_COMPETITIVE_SALARY",
  "salary_lower": 4000000,
  "salary_upper": 5500000,
  "degrees": ["Bachelor's Degree", "Associate Degree"],
  "job_functions": ["Sales and Marketing", "Banking Operations"],
  "job_levels": ["Entry Level / Staff"],
  "work_experience": 2,
  "company_name": "PT PNM Venture Capital",
  "confidential": false,
  "posted_at": "2026-08-30T03:30:00Z",
  "expires_at": "2026-09-08T00:00:00Z",
  "visited_count": 1266,
  "opportunities_link": "https://karir.com//opportunities/1401222",
  "company": {
    "id": 8920,
    "name": "PT PNM Venture Capital",
    "industry_name": "Keuangan / Bank",
    "description": "PT PNM Venture Capital adalah badan usaha Perseroan Terbatas yang melakukan kegiatan usaha Modal Ventura...",
    "subscribers": 8,
    "benefits": []
  }
}
```

**Search mode (`fetchFullDetails: false`)** — compact result, one extra field per job saved:

```json
{
  "_input": "keyword:sales",
  "_source": "S1-search-api",
  "id": 1401465,
  "job_position": "Field Sales Force",
  "description": "Prov. DKI-Jakarta",
  "company_name": "PT Bina Talenta",
  "company_logo_url": "https://karir-production.nos.jkt-1.neo.id/logos/....png",
  "salary_lower": 4500000,
  "salary_upper": 6000000,
  "salary_info": "LABEL_FULL_SALARY",
  "is_urgent": false,
  "posted_at": "2026-08-24T04:28:00Z"
}
```

| Field | Type | Description |
|---|---|---|
| `job_position` | string | Job title. |
| `location` / `description` | string | City/province — `location` on full-detail records, `description` on compact search records. |
| `requirements` / `responsibilities` | string | HTML-formatted job details (full-detail records only). |
| `salary_lower` / `salary_upper` | integer | Monthly salary range in IDR — may be missing when the employer doesn't disclose it. |
| `salary_info` | string | A label such as "competitive salary" when no figure is disclosed. |
| `company_name` | string | Employer name — may read as a placeholder when the employer chose to stay anonymous. |
| `company` | object | Full-detail records — employer profile: industry, description, subscriber count, benefits. |
| `posted_at` / `updated_at` / `expires_at` | string | ISO 8601 timestamps. |
| `opportunities_link` | string | Canonical job URL. |

A record with `_error` instead means that lookup couldn't complete (e.g. `"_error": "not_found"` for an expired/nonexistent job ID).

### Known limits

- **Location/industry/job-function filters need numeric IDs, not city or industry names.** Free-text `keyword` search (which matches job titles) works for everything else and is the primary way to narrow results.
- **Result counts reflect the site's actual current job postings** — a job board naturally has fewer live postings at any moment than, say, a news archive.
- **Salary is frequently undisclosed** by the employer — `salary_lower`/`salary_upper` will be missing in that case, with `salary_info` carrying a "competitive salary"-style label instead.

# Actor input Schema

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

"search" runs a keyword job search and (by default) hydrates each result into a full job record. "detail" fetches full detail for specific opportunity ID(s)/URL(s) directly.

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

"search" mode -- searches job titles (e.g. "marketing", "sales", "programmer"). Leave empty to list all currently active opportunities.

## `sortOrder` (type: `string`):

"search" mode -- order to return results in.

## `maxResults` (type: `integer`):

"search" mode -- maximum number of job listings to return. Pagination is followed automatically (20 results/page) until this cap or the end of the result set.

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

"search" mode -- when on (default), each search result is hydrated into a full job record (requirements, responsibilities, salary, company info) with one extra request per job. Turn off for faster/cheaper runs that only need the compact search-result fields (title, company, location, salary range, posted date).

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

"search" mode -- optional advanced filter, passed through to the API as-is.

## `maxSalary` (type: `integer`):

"search" mode -- optional advanced filter, passed through to the API as-is.

## `level` (type: `string`):

"search" mode -- optional advanced filter, passed through to the API as-is (as shown in karir.com's own filter UI, e.g. a job-level label).

## `workplace` (type: `string`):

"search" mode -- optional advanced filter (e.g. remote/on-site), passed through to the API as-is.

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

"search" mode -- optional advanced filter: numeric location IDs from karir.com's own filter UI. Not name-resolved by this actor (see README's Known limits).

## `jobFunctionIds` (type: `array`):

"search" mode -- optional advanced filter: numeric job-function IDs from karir.com's own filter UI.

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

"search" mode -- optional advanced filter: numeric industry IDs from karir.com's own filter UI.

## `degreeIds` (type: `array`):

"search" mode -- optional advanced filter: numeric degree IDs from karir.com's own filter UI.

## `companyIds` (type: `array`):

"search" mode -- optional advanced filter: numeric company IDs (see a company's karir.com/companies/<id> URL).

## `opportunityId` (type: `string`):

"detail" mode -- a single job's numeric ID or the full karir.com/opportunities/<id> URL.

## `opportunityIds` (type: `array`):

"detail" mode only -- multiple job IDs/URLs to fetch full detail for in one run.

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

How many detail requests to run in parallel (used in "search" mode when hydrating results, and in "detail" mode across multiple IDs).

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

Apify Proxy configuration. Residential is on by default.

## Actor input object example

```json
{
  "mode": "search",
  "keyword": "marketing",
  "sortOrder": "newest",
  "maxResults": 50,
  "fetchFullDetails": true,
  "opportunityId": "https://karir.com/opportunities/1401468",
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

All scraped job search / detail records produced by this run.

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "mode": "search",
    "keyword": "marketing",
    "sortOrder": "newest",
    "maxResults": 50,
    "fetchFullDetails": true,
    "opportunityId": "https://karir.com/opportunities/1401468",
    "maxConcurrency": 4,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "mode": "search",
    "keyword": "marketing",
    "sortOrder": "newest",
    "maxResults": 50,
    "fetchFullDetails": True,
    "opportunityId": "https://karir.com/opportunities/1401468",
    "maxConcurrency": 4,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

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

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

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

```

## CLI example

```bash
echo '{
  "mode": "search",
  "keyword": "marketing",
  "sortOrder": "newest",
  "maxResults": 50,
  "fetchFullDetails": true,
  "opportunityId": "https://karir.com/opportunities/1401468",
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call zucchini_gopher_m2v/karir-com-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zucchini_gopher_m2v/karir-com-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/3j9CDI3nIGOvpYtFw/builds/P9A70JIxkpBAVNiW4/openapi.json
