# Saramin Jobs Scraper (`crawlerbros/saramin-jobs-scraper`) Actor

Scrape job listings from Saramin (saramin.co.kr), South Korea's largest job board. Search by keyword, browse by region or job category, filter by experience level, employment type and education level, and sort results, with full Korean-Hangul support.

- **URL**: https://apify.com/crawlerbros/saramin-jobs-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Automation, Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Saramin Jobs Scraper

Scrape job listings from [Saramin](https://www.saramin.co.kr) — South Korea's largest job board. Search by keyword, browse by region, or browse by job category/industry, with experience-level and employment-type filters. Every field is fully UTF-8/Korean-Hangul safe. No login, no API key, no proxy required.

### What this actor does

- **Three modes:** `search` (keyword), `byRegion` (province/metro browse), `byCategory` (job category/industry browse)
- **Filters:** experience level (entry-level / experienced / no-experience-required), career-years min/max (1–20 years, experienced postings), employment type (14 real-world values — full-time, contract, internship, gig/part-time, dispatch, freelance, part-time shift, commissioned, full-time faculty, military-service-exemption R\&D, trainee, overseas employment, fixed-term, specialized research personnel), education level (no requirement / high school+ / associate+ / bachelor's+ / master's+ / doctorate+), company type/scale (large corp, startup, foreign-invested, KOSDAQ/KOSPI-listed, public agency, etc. — 25 values), minimum annual salary (16 breakpoints from 2,400만원 to 1억원+), remote-work-only, and weekly work-day pattern (5-day, 6-day, flexible, etc.) — apply on top of any mode
- **Exclude keyword** — drop listings matching an unwanted term (e.g. exclude `영업` from a broad region browse)
- **Sort order** — relevance (default), accuracy, registration date, last-modified date, closing date, applicant count, or company employee count
- **Pagination** — the actor automatically pages through Saramin's results (100 listings per request) to fill `maxItems`, up to 1000 per run
- **Full Korean-Hangul support** — job titles, company names, locations, and category tags are returned exactly as published, with no mojibake
- **Empty fields are omitted** — only fields Saramin actually published for a listing are included

### Output per job

- `title` — job posting title
- `company` — hiring company name
- `location` — region/city (e.g. `서울 강남구`, or multiple locations for multi-branch postings)
- `employmentType` — e.g. `정규직` (full-time), `계약직` (contract), `인턴직` (internship)
- `experienceLevel` — e.g. `신입` (entry-level), `경력 3~10년` (3–10 years), `경력무관` (no experience required)
- `educationLevel` — e.g. `대졸↑` (bachelor's+), `학력무관` (no requirement)
- `salary` — advertised salary, when published
- `postedDate` — ISO date the listing was registered/updated on Saramin
- `applicationDeadline` — deadline text as published (e.g. `~ 08/20(목)`, `상시채용` for always-hiring, `내일마감` for closing tomorrow)
- `jobCategoryTags[]` — skill/category tags attached to the listing
- `companyUrl` — link to the company's Saramin profile page
- `recId` — Saramin's internal listing ID
- `url` — canonical link to the job posting
- `recordType: "job"`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` / `byRegion` / `byCategory` |
| `searchQuery` | string | `python` | Keyword (mode=search) — Korean or English |
| `region` | string | `101000` (Seoul) | Top-level region (mode=byRegion) |
| `jobCategory` | string | `2` (IT개발·데이터) | Top-level job category/industry (mode=byCategory) |
| `experienceLevel` | string | `any` | `any` / `entryLevel` / `experienced` / `noExperienceRequired` |
| `employmentType` | string | `any` | `any` / `정규직` / `계약직` / `인턴직` / `아르바이트` / `파견직` / `프리랜서` / `파트` / `위촉직` / `전임` / `병역특례` / `교육생` / `해외취업` / `기간제` / `전문연구요원` |
| `educationLevel` | string | `any` | `any` / `noRequirement` / `highSchool` / `associate` / `bachelor` / `master` / `doctorate` |
| `excludeKeyword` | string | *(none)* | Optional keyword to exclude from results (all modes) |
| `careerYearsMin` | string | *(any)* | Minimum required career years for `경력` (experienced) postings, `1`-`20`. Setting this auto-restricts to experienced postings server-side. |
| `careerYearsMax` | string | *(any)* | Maximum required career years for `경력` (experienced) postings, `1`-`20`. Combine with `careerYearsMin` for a range (e.g. 3–5 years). |
| `companyType` | array | `[]` | Zero or more of Saramin's 25 company type/scale codes (large corp, startup, foreign-invested, KOSDAQ/KOSPI-listed, public agency, etc.) — applied server-side by Saramin itself. Empty = no filter. |
| `minAnnualSalary` | string | *(any)* | Minimum advertised annual salary — one of Saramin's 15 breakpoints from `8` (2,400만원+) to `22` (1억원+). Only affects postings with a published salary. |
| `remoteOnly` | boolean | `false` | Only show postings that allow remote work (재택근무 가능). |
| `workDays` | array | `[]` | Zero or more weekly work-day patterns (5-day/Mon-Fri, 6-day/Mon-Sat, flexible, etc. — 7 values). Empty = no filter. |
| `sortBy` | string | `relation` | `relation` / `accuracy` / `reg_dt` / `edit_dt` / `closing_dt` / `apply_cnt` / `employ_cnt` |
| `maxItems` | int | `40` | Hard cap on emitted records (1–1000). The actor pages through Saramin's results (100 per request) to fill this cap. |

#### Example: keyword search with experience filter

```json
{
  "mode": "search",
  "searchQuery": "백엔드 개발자",
  "experienceLevel": "entryLevel",
  "maxItems": 40
}
```

#### Example: browse jobs in Busan

```json
{
  "mode": "byRegion",
  "region": "106000",
  "employmentType": "정규직"
}
```

#### Example: browse the design category

```json
{
  "mode": "byCategory",
  "jobCategory": "15"
}
```

#### Example: remote-friendly startup jobs paying 5,000만원+

```json
{
  "mode": "search",
  "searchQuery": "백엔드 개발자",
  "companyType": ["scale005"],
  "minAnnualSalary": "17",
  "remoteOnly": true
}
```

#### Example: experienced roles requiring 3–5 years

```json
{
  "mode": "search",
  "searchQuery": "데이터 분석가",
  "careerYearsMin": "3",
  "careerYearsMax": "5"
}
```

### Use cases

- **Job aggregation** — pull fresh Korean job postings into your own board or app
- **Market research** — track hiring demand by region, category, or employment type
- **Recruiting intelligence** — monitor competitor hiring activity by company
- **Salary benchmarking** — collect published salary ranges by role/category
- **HR tech / ATS integrations** — feed structured Korean job data into downstream tools

### FAQ

**Do I need a Saramin account or API key?** No. This actor scrapes publicly visible listing pages — no login, cookies, or API key required.

**How many results does one run return?** Up to `maxItems` (max 1000). The actor pages through Saramin's results automatically; a run naturally stops earlier if the query/filter combination has fewer total matches than `maxItems`. Run the actor with different keywords/regions/categories to cover more ground beyond 1000.

**Why might a run return fewer results than expected?** Filters (experience level, employment type, education level) are applied after fetching each page of listings — a very restrictive filter combination can legitimately reduce the count, including to zero for an unmatched combination. A small `maxItems` combined with a narrow filter can also legitimately return fewer records than requested if the early pages don't contain enough matches.

**What does the `sortBy` option change?** It controls the order Saramin returns listings in — relevance (default), accuracy, registration date, last-modified date, closing date, applicant count, or company employee count. It does not change which listings match, only their order (which matters when `maxItems` is smaller than the total match count).

**Is the data real-time?** Yes — each run fetches Saramin's live listing pages at request time.

**Are Korean characters returned correctly?** Yes — every text field (titles, company names, locations, category tags) is UTF-8 and decoded/unescaped exactly as published, with no encoding artifacts.

**What do the region and job-category codes mean?** `region` covers South Korea's 16 top-level provinces/metro cities (Seoul, Gyeonggi, Busan, etc.). `jobCategory` covers Saramin's 21 top-level job categories/industries (IT development, accounting, sales, design, etc.) — see the dropdown descriptions in each field for the full list.

**Do `companyType`, `minAnnualSalary`, `remoteOnly`, and `workDays` change which listings match, or just add post-filtering?** Saramin itself narrows the result set for these filters, the same as `region`/`jobCategory`/`searchQuery`. They can be combined freely with each other and with `experienceLevel`/`employmentType`/`educationLevel`.

**What happens if my keyword or filter combination has no real matches?** You get zero records, not unrelated jobs. Saramin's own site sometimes shows a generic "no results" panel of recommended postings unrelated to your search instead of a blank page — this actor recognizes that panel and excludes it, so a very restrictive combination (e.g. a narrow salary + company-type + career-years combo) cleanly returns an empty result.

**Does `mode=byCategory` return only listings that belong exclusively to that category?** Mostly, but Saramin's own category-browse pages include some multi-department postings (e.g. a single "various positions" ad cross-listed under Design, IT, and Sales at once) — the actor mirrors Saramin's browse results as-is. For strict category relevance, post-filter `jobCategoryTags`/`title`, or use `mode=search` with a category-specific keyword instead.

**Does `mode=byRegion` return only listings physically located in that region?** Almost always, but Saramin occasionally cross-lists a posting under a region's browse page even though the listing's own `location` names a different city/province. Post-filter the `location` field client-side if you need strict accuracy.

**Can I filter by district/neighborhood or subway line instead of just province?** Not currently. Saramin's district and subway-line browsing use large, frequently-changing code lists rather than a small stable set, so they aren't exposed here. Combine `region` (16 top-level provinces/metro cities) with `searchQuery` for finer targeting.

**Is there an industry-sector filter (업종) separate from job category?** No — `jobCategory` (직무/job function) is supported, but Saramin's separate industry-sector taxonomy (업종, e.g. "IT·웹·통신") isn't exposed, since it doesn't reliably narrow results as a standalone filter. Use `jobCategory` or `searchQuery` instead.

**Is there an employee-benefits filter (복리후생)?** No — Saramin publishes roughly 165 benefit codes, too many for a reliable dropdown and a narrower use case than the other filters, so it isn't included.

**Will `companyUrl` always work?** Almost always, but it can occasionally 404 if the employer has since removed their Saramin company page. The job listing `url` itself remains reliable.

**How is `employmentType` matched against a listing?** By the employment type actually displayed on the listing itself, so it reliably covers all 14 real-world values Saramin publishes — including some not present in Saramin's own on-site filter list, like `기간제` (fixed-term) and `전문연구요원` (specialized research personnel).

# Actor input Schema

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

What to fetch.

## `searchQuery` (type: `string`):

Free-text keyword to search job titles/descriptions (mode=search). Korean or English, e.g. `python`, `개발자`, `마케팅`.

## `region` (type: `string`):

Top-level South Korean region/province to browse job listings from.

## `jobCategory` (type: `string`):

Top-level Saramin job-category/industry to browse.

## `experienceLevel` (type: `string`):

Filter results by career/experience requirement. Applies to all modes.

## `employmentType` (type: `string`):

Filter results by employment type. Applies to all modes.

## `educationLevel` (type: `string`):

Filter results by minimum education requirement. Applies to all modes.

## `excludeKeyword` (type: `string`):

Optional keyword to exclude from results (Saramin's own `exc_keyword` filter). Applies to all modes, e.g. exclude `영업` (sales) from a broad region/category browse.

## `careerYearsMin` (type: `string`):

Restrict results to `경력` (experienced) postings requiring at least this many years (Saramin's own `exp_cd=2`+`exp_min` filter). Setting this automatically restricts to experienced postings server-side, same as `experienceLevel=experienced`. Applies to all modes.

## `careerYearsMax` (type: `string`):

Restrict results to `경력` (experienced) postings requiring at most this many years (Saramin's own `exp_cd=2`+`exp_max` filter). Setting this automatically restricts to experienced postings server-side, same as `experienceLevel=experienced`. Applies to all modes.

## `companyType` (type: `array`):

Restrict results to one or more company types/scales (Saramin's own `company_type[]` filter). Applies to all modes. Empty = no filter.

## `minAnnualSalary` (type: `string`):

Restrict results to postings advertising at least this annual salary (Saramin's own `sal_min` filter). Applies to all modes. Only postings with a published salary are affected by this filter server-side.

## `remoteOnly` (type: `boolean`):

Only show postings that allow remote work (Saramin's own `work_type=1` filter, 재택근무 가능). Applies to all modes.

## `workDays` (type: `array`):

Restrict results to one or more weekly work-day patterns (Saramin's own `workday[]` filter). Applies to all modes. Empty = no filter.

## `sortBy` (type: `string`):

How Saramin orders the listing page. Applies to all modes.

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

Hard cap on emitted records. The actor paginates Saramin's listing pages (up to 100 per page) to fill this cap; results may be fewer if filters are restrictive or the query's total match count is smaller.

## Actor input object example

```json
{
  "mode": "search",
  "searchQuery": "python",
  "region": "101000",
  "jobCategory": "2",
  "experienceLevel": "any",
  "employmentType": "any",
  "educationLevel": "any",
  "excludeKeyword": "",
  "careerYearsMin": "",
  "careerYearsMax": "",
  "companyType": [],
  "minAnnualSalary": "",
  "remoteOnly": false,
  "workDays": [],
  "sortBy": "relation",
  "maxItems": 40
}
```

# Actor output Schema

## `jobs` (type: `string`):

Dataset containing all scraped Saramin job listings.

# 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",
    "searchQuery": "python",
    "region": "101000",
    "jobCategory": "2",
    "experienceLevel": "any",
    "employmentType": "any",
    "educationLevel": "any",
    "excludeKeyword": "",
    "careerYearsMin": "",
    "careerYearsMax": "",
    "companyType": [],
    "minAnnualSalary": "",
    "remoteOnly": false,
    "workDays": [],
    "sortBy": "relation",
    "maxItems": 40
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/saramin-jobs-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",
    "searchQuery": "python",
    "region": "101000",
    "jobCategory": "2",
    "experienceLevel": "any",
    "employmentType": "any",
    "educationLevel": "any",
    "excludeKeyword": "",
    "careerYearsMin": "",
    "careerYearsMax": "",
    "companyType": [],
    "minAnnualSalary": "",
    "remoteOnly": False,
    "workDays": [],
    "sortBy": "relation",
    "maxItems": 40,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/saramin-jobs-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",
  "searchQuery": "python",
  "region": "101000",
  "jobCategory": "2",
  "experienceLevel": "any",
  "employmentType": "any",
  "educationLevel": "any",
  "excludeKeyword": "",
  "careerYearsMin": "",
  "careerYearsMax": "",
  "companyType": [],
  "minAnnualSalary": "",
  "remoteOnly": false,
  "workDays": [],
  "sortBy": "relation",
  "maxItems": 40
}' |
apify call crawlerbros/saramin-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawlerbros/saramin-jobs-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/t3neBr5mE5hzfbv0C/builds/G4y8hL07j6aqPui4E/openapi.json
