# 51job Scraper 前程无忧: China Jobs, Salary Data & Percentile Stats (`getascraper/51job-scraper`) Actor

China jobs and salary data scraper for 51job (前程无忧). Get monthly CNY and USD pay, salary percentiles by keyword and city, English benefit tags, recruiter reply scores, and a monitor for new, changed and expired postings across 65 cities. No login or API key needed.

- **URL**: https://apify.com/getascraper/51job-scraper.md
- **Developed by:** [GetAScraper](https://apify.com/getascraper) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Features:** Limited permissions
- **Stats:** 52 total users, 14 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.90 / 1,000 job postings

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?

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

## 51job Scraper 前程无忧: China Jobs, Salary Data & Percentile Stats

<table width="100%" style="display:table;width:100%;border-collapse:separate;border-spacing:0;min-width:100%;table-layout:fixed;margin:0;margin-bottom:0">
<tbody>
<tr style="width:100%">
<td colspan="4" style="border:none;padding:14px 18px;background:#ecfdf5;border-top:3px solid #0f766e;border-left:1px solid #D6D3D1;border-right:1px solid #D6D3D1;border-radius:10px 10px 0 0;color:#1C1917">
<span style="font-size:16px;font-weight:700;color:#1C1917">Build a current China salary and hiring dataset from public 51job searches.</span> <span style="font-size:15px;color:#57534E">Search by keyword and city to get deduplicated jobs with monthly CNY pay, optional US dollar pay and salary percentiles per keyword and city. Each job carries a recruiter responsiveness score, with a monitoring mode for recurring hiring changes.</span>
</td>
</tr>
<tr style="width:100%">
<td colspan="4" style="border:none;padding:10px 14px;background:#0f766e;border-left:1px solid #D6D3D1;border-right:1px solid #D6D3D1;color:#FFFFFF">
<span style="color:#FFFFFF;font-size:14px;font-weight:700;letter-spacing:0.5px">China jobs source comparison</span>
<span style="color:#FBD5D5;font-size:13px">&nbsp;&nbsp;&bull;&nbsp;&nbsp;Zhaopin, 51Job, Liepin, and ChinaHR listings side by side</span>
</td>
</tr>
<tr style="width:100%">
<td width="25%" style="border:none;width:25%;padding:10px 12px;border-left:1px solid #D6D3D1;border-bottom:1px solid #D6D3D1;vertical-align:top;background:#FFFFFF;color:#1C1917">
<span style="overflow-wrap:break-word;word-break:break-word"><img src="https://apify-image-uploads-prod.s3.us-east-1.amazonaws.com/jNDbFabbVxMhnQNb4-actor-y4gjuGAC4xGshlm6m-lb3TrznfpI-CompressJPEG.Online_img%28512x512%29_%288%29.jpg" width="20" height="20" style="vertical-align:middle;border-radius:4px"> &nbsp;<a href="https://apify.com/getascraper/zhaopin-jobs-scraper" style="color:#1C1917;text-decoration:none;font-weight:700;font-size:13px">Zhaopin</a></span><br>
<span style="color:#57534E;font-size:11px">Jobs, salary &amp; hiring-change monitoring</span>
</td>
<td width="25%" style="border:none;width:25%;padding:10px 12px;border-left:1px solid #D6D3D1;border-bottom:1px solid #D6D3D1;vertical-align:top;background:#ecfdf5;color:#1C1917">
<span style="overflow-wrap:break-word;word-break:break-word"><img src="https://apify-image-uploads-prod.s3.us-east-1.amazonaws.com/jNDbFabbVxMhnQNb4-actor-e2mxWFMIH10tIVN0f-yhHOLwlfsM-CompressJPEG.Online_img%28512x512%29_%289%29.jpg" width="20" height="20" style="vertical-align:middle;border-radius:4px"> &nbsp;<a href="https://apify.com/getascraper/51job-scraper" style="color:#0f766e;text-decoration:none;font-weight:700;font-size:13px">51job</a></span><br>
<span style="color:#0f766e;font-size:11px;font-weight:700">&#10148; You are here</span>
</td>
<td width="25%" style="border:none;width:25%;padding:10px 12px;border-left:1px solid #D6D3D1;border-bottom:1px solid #D6D3D1;vertical-align:top;background:#FFFFFF;color:#1C1917">
<span style="overflow-wrap:break-word;word-break:break-word"><img src="https://apify-image-uploads-prod.s3.us-east-1.amazonaws.com/jNDbFabbVxMhnQNb4-actor-BuC7iEzhhplgO8ZX4-l5qn04Im5u-CompressJPEG.Online_img%28512x512%29_%2811%29.jpg" width="20" height="20" style="vertical-align:middle;border-radius:4px"> &nbsp;<a href="https://apify.com/getascraper/liepin-scraper" style="color:#1C1917;text-decoration:none;font-weight:700;font-size:13px">Liepin</a></span><br>
<span style="color:#57534E;font-size:11px">White-collar roles &amp; salary data</span>
</td>
<td width="25%" style="border:none;width:25%;padding:10px 12px;border-left:1px solid #D6D3D1;border-right:1px solid #D6D3D1;border-bottom:1px solid #D6D3D1;vertical-align:top;background:#FFFFFF;color:#1C1917">
<span style="overflow-wrap:break-word;word-break:break-word"><img src="https://apify-image-uploads-prod.s3.us-east-1.amazonaws.com/jNDbFabbVxMhnQNb4-actor-EhSPkTwTCROda2Czz-DG8Q4VPIkU-1631301125523.jpeg" width="20" height="20" style="vertical-align:middle;border-radius:4px"> &nbsp;<a href="https://apify.com/getascraper/chinahr-jobs-scraper" style="color:#1C1917;text-decoration:none;font-weight:700;font-size:13px">ChinaHR</a></span><br>
<span style="color:#57534E;font-size:11px">Job titles, salary &amp; employer data</span>
</td>
</tr>
</tbody>
<tr>
<td colspan="4" style="border:none;padding:10px 14px;background:#FFFFFF;border-left:1px solid #D6D3D1;border-right:1px solid #D6D3D1;border-bottom:1px solid #D6D3D1;text-align:center;color:#1C1917">
<span style="color:#57534E;font-size:13px"><img src="https://apify-image-uploads-prod.s3.us-east-1.amazonaws.com/jNDbFabbVxMhnQNb4-actor-1pJSp13lO8ggYNu80-RKIXMxSZ9k-images_%2872%29.jpeg" width="18" height="18" style="vertical-align:middle;border-radius:4px">&nbsp; More than one job board? <a href="https://apify.com/getascraper/china-jobs-feed" style="color:#0f766e;text-decoration:none;font-weight:700">China Jobs Feed</a> covers the main ones in one run.</span><br>
<span lang="zh" style="color:#78716C;font-size:12px"><span style="white-space:nowrap">要查多个招聘网站？</span> <span style="white-space:nowrap">China Jobs Feed</span> <span style="white-space:nowrap">一次运行覆盖主要平台。</span></span>
</td>
</tr>
</table>
<table width="100%" style="display:table;width:100%;border-collapse:separate;border-spacing:0;margin-top:0;margin-bottom:0">
<tr>
<td style="padding:24px 28px;background:#ecfdf5;border:1px solid #99f6e4;border-top:none;color:#1C1917">
<span style="font-size:23px;font-weight:800;color:#1c1917;line-height:1.3">Public 51job listings turned into China salary data you can compare by city</span><br>
<span style="font-size:15px;color:#475569;line-height:1.6">Search by keyword and city to get deduplicated jobs with normalized monthly CNY salary, an optional US dollar view, and a salary statistics record with the median and 25th and 75th percentile per keyword and city. Every job also includes a 0-100 recruiter responsiveness score and English benefit tags.</span>
</td>
</tr>
</table>
<table width="100%" style="display:table;width:100%;border-collapse:separate;border-spacing:0;margin-top:0">
<tr>
<td style="padding:14px 12px;width:25%;background:#FFFFFF;border:1px solid #99f6e4;vertical-align:top;border-top:none;border-radius:0 0 0 10px;color:#1C1917">
<span style="font-size:15px;font-weight:800;color:#0f766e">💰 CNY and USD salary</span><br>
<span style="font-size:12px;color:#475569">Monthly min, max and median pay, 13th-month aware, with an optional US dollar view.</span>
</td>
<td style="padding:14px 12px;width:25%;background:#FFFFFF;border:1px solid #99f6e4;border-left:none;vertical-align:top;border-top:none;color:#1C1917">
<span style="font-size:15px;font-weight:800;color:#0f766e">📊 Salary percentiles</span><br>
<span style="font-size:12px;color:#475569">Count, median, 25th and 75th percentile per keyword and city, with the sample size shown.</span>
</td>
<td style="padding:14px 12px;width:25%;background:#FFFFFF;border:1px solid #99f6e4;border-left:none;vertical-align:top;border-top:none;color:#1C1917">
<span style="font-size:15px;font-weight:800;color:#0f766e">🔁 Catch hiring changes</span><br>
<span style="font-size:12px;color:#475569">Flag new, updated, expired jobs and salary moves, with an optional webhook.</span>
</td>
<td style="padding:14px 12px;width:25%;background:#FFFFFF;border:1px solid #99f6e4;border-left:none;vertical-align:top;border-top:none;border-radius:0 0 10px 0;color:#1C1917">
<span style="font-size:15px;font-weight:800;color:#0f766e">📨 Score recruiter replies</span><br>
<span style="font-size:12px;color:#475569">A 0-100 reply score and English benefit tags on every job.</span>
</td>
</tr>
</table>

51job (前程无忧) is one of China's largest public job boards. This Actor turns its search results into salary and hiring data for recruiting teams, researchers, and HR technology products. Missing values stay absent, so the dataset stays trustworthy.

### 🔎 What does this Actor do?

Search the public 51job surface with one keyword and city or with a larger keyword and city matrix. The Actor returns one row per unique public job and preserves the fields buyers need to evaluate a role.

<table width="100%" style="display:table;width:100%;border-collapse:separate;border-spacing:0">
<tr>
<td style="border:none;padding:12px 18px;background:#ecfdf5;border-left:4px solid #0f766e;border-top:1px solid #99f6e4;border-right:1px solid #99f6e4;border-bottom:1px solid #99f6e4;border-radius:10px;color:#1C1917">
<span style="font-size:15px;color:#1c1917">&#9889; <b>Start with one useful search.</b> Then repeat the same scope on a schedule when you need a hiring change feed.</span>
</td>
</tr>
</table>

### 👥 Who is this for?

- **I am a job-data product manager** looking to add current China listings without maintaining another source collector.
- **I am a labor-market researcher** looking to compare salary, education, experience, skills, benefits, and employer activity across cities.
- **I am an HR technology engineer** looking to feed a database or alert workflow with compact rows and stable job IDs, or to switch from another 51job scraper without changing my input.
- **I am a market-intelligence analyst** looking to use hiring activity as a signal for companies, sectors, and regions.

### 🚀 How it works

<table width="100%" style="display:table;width:100%;border-collapse:separate;border-spacing:0">
<tr>
<td style="padding:16px 14px;width:33%;background:#ecfdf5;border:1px solid #99f6e4;border-radius:10px 0 0 10px;vertical-align:top;color:#1C1917">
<span style="font-size:12px;font-weight:800;color:#0f766e;letter-spacing:1px">STEP 1</span><br>
<span style="font-size:14px;font-weight:700;color:#1c1917">Choose the hiring scope</span><br>
<span style="font-size:12px;color:#475569">Enter keywords, pick cities, and set source-supported hiring filters.</span>
</td>
<td style="padding:16px 14px;width:33%;background:#ecfdf5;border:1px solid #99f6e4;border-left:none;vertical-align:top;color:#1C1917">
<span style="font-size:12px;font-weight:800;color:#0f766e;letter-spacing:1px">STEP 2</span><br>
<span style="font-size:14px;font-weight:700;color:#1c1917">Collect clean rows</span><br>
<span style="font-size:12px;color:#475569">Get deduplicated public jobs with source dates, salary, location, and employer context.</span>
</td>
<td style="padding:16px 14px;width:33%;background:#ecfdf5;border:1px solid #99f6e4;border-left:none;border-radius:0 10px 10px 0;vertical-align:top;color:#1C1917">
<span style="font-size:12px;font-weight:800;color:#0f766e;letter-spacing:1px">STEP 3</span><br>
<span style="font-size:14px;font-weight:700;color:#1c1917">Use the result that fits</span><br>
<span style="font-size:12px;color:#475569">Analyze full records, send compact rows to a workflow, or schedule changes mode.</span>
</td>
</tr>
</table>

### 📊 China salary data and statistics

Every run with disclosed salaries saves a `SALARY_STATS` record next to the dataset. It shows, for each keyword and city, the number of jobs, the median monthly pay, the 25th and 75th percentile, and the lowest and highest posted pay, in CNY and optionally in US dollars.

- Figures use the midpoint of each posted range, with annual and daily pay converted to monthly.
- They describe only the jobs collected in that run, and the sample size is shown beside every figure.
- Groups with fewer jobs than your minimum sample are left out, never estimated.
- Turn on the US dollar option to add dollar figures. The exchange rate and its date are saved with the results.

Use the Salary & Benefits view in the dataset to browse the same pay figures job by job.

### ⚙️ Input

| Field                     | Type             | Required | Description                                                                                                                          |
| ------------------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `sourceMode`              | enum             | Yes      | Choose generated keyword and city searches or exact public 51job search URLs.                                                        |
| `queries`                 | array of strings | No       | Keywords in Chinese or English. Each keyword is combined with every selected city.                                                   |
| `cities`                  | array of strings | No       | Pick from about 65 major cities across China, or All China. City names are accepted in saved inputs. Each city search returns up to 1,000 jobs. |
| `startUrls`               | array of URLs    | No       | Public search-result URLs. Detail pages and unrelated hosts are rejected.                                                            |
| `experience`              | enum             | No       | Source work-experience filter, from any experience to senior experience bands.                                                       |
| `education`               | enum             | No       | Minimum education filter from the source search form.                                                                                |
| `employmentType`          | string           | No       | Optional employment-type code copied from a supported public search URL.                                                             |
| `companyType`             | enum             | No       | Company structure filter such as private, state-owned, joint venture, foreign-owned, or listed.                                      |
| `companySize`             | enum             | No       | Employer headcount range from the source search form.                                                                                |
| `industries`              | array of strings | No       | Optional industry codes from a supported public search URL.                                                                          |
| `jobFunctions`            | array of strings | No       | Optional job-function codes from a supported public search URL.                                                                      |
| `salaryBand`              | string           | No       | Optional salary-band code copied from a supported public search URL.                                                                 |
| `postedWithin`            | string           | No       | Optional issue-date code copied from a supported public search URL.                                                                  |
| `sortBy`                  | string           | No       | Optional source sort code.                                                                                                           |
| `excludeKeywords`         | array of strings | No       | Remove rows matching the supplied text in titles, employers, descriptions, industries, functions, or tags.                           |
| `includeRecruiterSignals` | boolean          | No       | Keep public recruiter name, role, labels, online status, status, and responsiveness fields when available.                           |
| `minRecruiterResponsiveness` | integer       | No       | Keep only jobs whose recruiter responsiveness score meets this minimum (0-100). Leave empty for no filtering.                        |
| `convertToUsd`            | boolean          | No       | Add monthly US dollar min, max and median pay with the rate and its date. Off by default.                                             |
| `onlyDisclosedSalary`     | boolean          | No       | Skip jobs listed as negotiable or without a salary.                                                                                  |
| `minMonthlySalaryCny`     | integer          | No       | Keep only jobs whose highest posted monthly pay reaches this amount.                                                                 |
| `dropCrossCityDuplicates` | boolean          | No       | Keep one copy when the same employer posts the same title in several selected cities. Otherwise duplicates are only flagged.         |
| `salaryStatistics`        | boolean          | No       | Save the `SALARY_STATS` record. On by default.                                                                                       |
| `salaryStatisticsMinSample` | integer        | No       | Smallest number of jobs with a salary needed to publish a statistic. Default 5.                                                      |
| `includeSourceFields`     | boolean          | No       | Add the complete public search-result object under `source`, using 51job's own field names. Off by default.                          |
| `url`, `max_page`, `max_results` | string, integer | No | Alternative names for one search URL, page limit and result limit, so a pipeline built for another 51job scraper can switch unchanged. |
| `maxItems`                | integer          | No       | Global result cap from 1 to 5,000. Runs on a free Apify plan return up to 25 jobs.                                                    |
| `maxPagesPerSearch`       | integer          | No       | Page cap from 1 to 100 for each keyword and city search.                                                                             |
| `outputMode`              | enum             | No       | Choose full research records or compact workflow rows.                                                                               |
| `monitorMode`             | enum             | No       | Return a current snapshot or compare this run with a durable monitoring scope.                                                       |
| `stateName`               | string           | No       | Stable watchlist name for recurring changes mode. Use a distinct name for each schedule.                                             |
| `includeUnchanged`        | boolean          | No       | Include unchanged rows in changes mode when a complete comparison is required.                                                       |
| `notifyOnNoChanges`       | boolean          | No       | Send a heartbeat when a complete monitoring run has no selected changes.                                                             |
| `proxyConfiguration`      | object           | Yes      | Proxy settings. The default works for most runs, and the Actor retries automatically when a request is blocked.                      |
| `notificationWebhookUrl`  | string           | No       | Optional private HTTPS destination for selected monitoring events.                                                                   |
| `notificationEvents`      | array of strings | No       | Select new, updated, reappeared, or expired events for notifications.                                                                |

### 📊 Data table

Every row has `schemaVersion: 2`, a stable source job ID, a public job URL, and collection time. Optional values are omitted when the public listing does not publish them.

| Field                     | Type             | Description                                                                  |
| ------------------------- | ---------------- | ---------------------------------------------------------------------------- |
| `schemaVersion`           | integer          | Output contract version.                                                     |
| `jobId`                   | string           | Stable 51job listing identifier.                                             |
| `title`                   | string           | Public role title.                                                           |
| `jobUrl`                  | URL              | Public source page for the role.                                             |
| `applicationUrl`          | URL              | Distinct public application destination when published by the source.        |
| `dedupeKey`               | string           | Stable key used to remove repeated listings.                                 |
| `sourcePlatform`          | string           | Source label, currently `51job`.                                             |
| `listingUrl`              | URL              | Search-result URL that produced the row.                                     |
| `scrapedAt`               | datetime         | Time the Actor collected the row.                                            |
| `sourcePostedAt`          | datetime         | Source issue date when published.                                            |
| `sourceUpdatedAt`         | datetime         | Source update date when published.                                           |
| `daysSincePosted`         | number           | Age calculated from the source issue date.                                   |
| `province`                | string           | Source province when published.                                              |
| `city`                    | string           | Source city when published.                                                  |
| `district`                | string           | Source district when published.                                              |
| `locationRaw`             | string           | Original source location text.                                               |
| `address`                 | string           | Public work address when published.                                          |
| `longitude`               | number           | Numeric longitude when published and valid.                                  |
| `latitude`                | number           | Numeric latitude when published and valid.                                   |
| `isRemote`                | boolean          | Public remote-work signal when published.                                    |
| `isIntern`                | boolean          | Public internship signal when published.                                     |
| `salaryRaw`               | string           | Original public salary text.                                                 |
| `salaryDisclosed`         | boolean          | Whether the source salary text produced a reliable range.                    |
| `salaryNegotiable`        | boolean          | Whether the source marks compensation as negotiable.                         |
| `currency`                | string           | Currency label when a salary value is published, currently CNY.              |
| `salaryMinCny`            | number           | Minimum value in the source salary period.                                   |
| `salaryMaxCny`            | number           | Maximum value in the source salary period.                                   |
| `salaryPeriod`            | string           | Source salary period, such as daily, monthly, or annual.                     |
| `salaryMonthsPerYear`     | number           | Bonus-month count when the source publishes it.                              |
| `minSalaryMonthlyCny`     | number           | Monthly equivalent derived from the source range.                            |
| `maxSalaryMonthlyCny`     | number           | Monthly equivalent derived from the source range.                            |
| `annualMinCny`            | number           | Annual equivalent derived from the source range.                             |
| `annualMaxCny`            | number           | Annual equivalent derived from the source range.                             |
| `salaryMedianMonthlyCny`  | number           | Midpoint of the monthly range.                                               |
| `annualMedianCny`         | number           | Midpoint annual pay, using the stated number of pay months when published.   |
| `salaryMinMonthlyUsd`, `salaryMaxMonthlyUsd`, `salaryMedianMonthlyUsd` | number | Monthly US dollar figures when the US dollar option is on. |
| `usdPerCny`, `usdRateDate` | number, date    | Exchange rate used and its date, saved on every converted row.               |
| `experienceRaw`           | string           | Original experience requirement.                                             |
| `experienceMinYears`      | number           | Parsed minimum experience when available.                                    |
| `experienceMaxYears`      | number           | Parsed maximum experience when available.                                    |
| `educationRaw`            | string           | Original education requirement.                                              |
| `educationEnglish`        | string           | English education label when the source text is recognized.                  |
| `employmentType`          | string           | Public employment type when published.                                       |
| `jobCategory`             | string           | Public role category when published.                                         |
| `jobFunction`             | string           | Public role function or function code.                                       |
| `industry`                | string           | Public role industry or industry code.                                       |
| `tags`                    | array of strings | Public source tags.                                                          |
| `skillTags`               | array of strings | Tags that describe skills and duties, without the repeated experience and education tags. |
| `benefitTags`             | array of strings | Tags that describe benefits, such as social insurance or paid leave.         |
| `benefitTagsEnglish`      | array of strings | English names for recognized benefit tags. Unrecognized tags get no name.    |
| `openings`                | integer          | Number of openings when published as a valid count.                          |
| `companyId`               | string           | Public employer identifier.                                                  |
| `companyName`             | string           | Public employer name.                                                        |
| `companyFullName`         | string           | Full employer name when published.                                           |
| `companyLogo`             | URL              | Public employer logo URL when published.                                     |
| `companyEmployees`        | string           | Public employee-size text.                                                   |
| `companyType`             | string           | Original public company type text.                                           |
| `companyTypeEnglish`      | string           | English company type label when recognized.                                  |
| `companyIndustry`         | string           | Public employer industry.                                                    |
| `companyUrl`              | URL              | Public employer page when published.                                         |
| `description`             | string           | Cleaned public job description.                                              |
| `descriptionShort`        | string           | Short public description for compact previews.                               |
| `recruiterName`           | string           | Public recruiter name when published.                                        |
| `recruiterTitle`          | string           | Public recruiter role when published.                                        |
| `recruiterLabels`         | array of strings | Public recruiter labels.                                                     |
| `recruiterOnline`         | boolean          | Public recruiter online signal.                                              |
| `recruiterStatus`         | string           | Public recruiter status.                                                     |
| `recruiterLastReplyMinutesAgo` | number      | Minutes since the recruiter's last reply, when published.                    |
| `recruiterRepliesToday`   | number           | Replies sent today, when published (source caps the displayed count at 10+). |
| `recruiterRepliesToday10Plus` | boolean     | Whether today's reply count hit the source's display ceiling.                |
| `recruiterLastResumeViewMinutesAgo` | number | Minutes since the recruiter last viewed a resume, when published.           |
| `recruiterLastResumeProcessedMinutesAgo` | number | Minutes since the recruiter last processed a resume, when published.   |
| `recruiterResumeViewsThisWeek` | number      | Resumes viewed this week, when published.                                    |
| `recruiterResumesProcessedThisWeek` | number | Resumes processed this week, when published.                                |
| `recruiterResponsivenessScore` | number      | 0-100 score derived from the fields above; higher means a more recently or frequently responsive recruiter. |
| `applicationDeadlineText` | string           | Public text about the latest application activity.                           |
| `lastApplicantMinutesAgo` | number           | Minutes since the latest applicant, when the listing states it.              |
| `daysSinceUpdated`        | number           | Days since the listing was last refreshed.                                   |
| `isRefreshedListing`      | boolean          | Whether the listing was refreshed at least a day after it was posted.        |
| `crossCityDuplicate`      | boolean          | True when the same employer and title appear in several of your cities.      |
| `crossCityDuplicateCities`, `crossCityDuplicateOf` | array, string | The cities involved and the first copy's job ID. |
| `source`                  | object           | Original public search-result object, only when `includeSourceFields` is on. |
| `searchQuery`             | string           | Keyword used for the row's search scope.                                     |
| `searchCity`              | string           | City code used for the row's search scope.                                   |
| `changeType`              | string           | Monitoring lifecycle event: new, updated, reappeared, unchanged, or expired. |
| `changedFields`           | array of strings | Fields that changed since the previous stored observation.                   |
| `firstSeenAt`             | datetime         | First successful observation in this monitoring scope.                       |
| `lastSeenAt`              | datetime         | Most recent successful observation in this monitoring scope.                 |
| `expiredAt`               | datetime         | Time an expiry event was emitted after repeated complete absence.            |
| `salaryChange`            | object           | Before and after monthly pay and the percentage move, on updated jobs whose pay changed. |
| `listingLifetimeDays`     | number           | Days between first sighting and expiry, on expired jobs.                     |

### 🔁 Monitoring

Set `monitorMode` to `changes` and keep the same search scope and `stateName` in a scheduled task. The first complete run emits `NEW` rows. Later complete runs emit new, updated, and reappeared rows by default.

Updated jobs whose pay moved carry a `salaryChange` summary, and expired jobs carry `listingLifetimeDays`, a useful time-to-fill signal. A missing listing becomes `EXPIRED` after two consecutive complete scans. It becomes `REAPPEARED` if it returns later. Blocked, malformed, empty, failed, or truncated scans never advance missing-job expiry state.

Full records suit research exports. Compact records keep stable identity, source dates, salary context, employer identity, and lifecycle fields for feeds and alerts. Webhook delivery is bounded and best effort. A delivery failure does not discard valid rows or monitoring state.

### ✨ Example recipes

- [Shanghai AI engineer jobs](https://apify.com/getascraper/51job-scraper/examples/51job-shanghai-ai-engineer-jobs): Use the overview view for a native-language AI hiring search.
- [Shenzhen foreign-owned company jobs](https://apify.com/getascraper/51job-scraper/examples/51job-shenzhen-foreign-owned-company-jobs): Use the prospector view for employer research.
- [Beijing senior software engineer salaries](https://apify.com/getascraper/51job-scraper/examples/51job-beijing-senior-software-engineer-salaries): Use the salary view for compensation analysis.

### 💰 Pricing

Pricing is pay per saved result, with volume discounts on higher Apify plans. Empty runs and runs that save no rows cost nothing. Salary statistics, US dollar conversion and the original 51job fields add no extra charge. There are no subscriptions or maintenance fees.

### ⭐ Enjoying 51job Scraper?

<table width="100%" style="display:table;width:100%;border-collapse:separate;border-spacing:0">
<tr>
<td style="padding:20px 24px 14px;background:#ecfdf5;border:1px solid #99f6e4;border-left:5px solid #0f766e;border-radius:10px 10px 0 0;color:#1C1917">
<span style="font-size:20px;letter-spacing:4px;color:#0f766e">⭐ ⭐ ⭐ ⭐ ⭐</span><br>
<span style="font-size:17px;font-weight:800;color:#1c1917">Turn repeated job research into a dependable hiring feed.</span><br>
<span style="font-size:14px;color:#475569">A five-star rating takes a few seconds and helps recruiting teams find this source. Your feedback also guides the next improvements.</span>
</td>
</tr>
<tr>
<td style="padding:0;background:#0f766e;border:1px solid #0f766e;border-left:5px solid #0f766e;border-top:none;border-radius:0 0 10px 10px;text-align:center;color:#FFFFFF">
<a href="https://apify.com/getascraper/51job-scraper/reviews" style="display:block;padding:13px 16px;color:#ffffff;text-decoration:none;font-weight:800;font-size:15px;letter-spacing:0.3px">&#9733;&nbsp;&nbsp;Rate this Actor on Apify</a>
</td>
</tr>
</table>

### ❓ FAQ

##### 前程无忧会被拦截吗？

51job can show access challenges to automated visitors. The Actor uses bounded timeouts, rotates sessions and retries automatically with a stronger connection when a request is blocked. If every attempt is blocked, the run still ends cleanly with a clear message and charges nothing. The run summary reports what, if anything, was affected.

##### 前程无忧薪资数据怎么导出？

Run the Actor with your keywords and cities, then open the Salary & Benefits view or download the dataset as CSV, Excel or JSON. The `SALARY_STATS` record in the run's storage holds the median and percentile figures per keyword and city. Turn on the US dollar option to add dollar columns.

##### Does it collect private phone numbers?

No. The Actor reads public job listing fields only. It does not collect candidate profiles, private phone numbers, resumes, or login-only information.

##### How reliable are the salary statistics?

They are computed only from the jobs collected in your run, so a larger and more focused run gives a better picture. Each figure shows its sample size, and groups below your minimum sample are left out instead of estimated.

### 🔄 Migrating from v1

Saved inputs using `scrapeMode`, `searchQuery`, `jobArea`, `workYear`, `degree`, `outputFormat`, or `incrementalMode` are accepted for one migration window. New tasks should use `sourceMode`, `queries`, `cities`, `experience`, `education`, `outputMode`, and `monitorMode`.

Pipelines built for another 51job scraper can send `url`, `max_page` and `max_results` as they are, and can turn on `includeSourceFields` to receive the original 51job field names under `source`.

The v2 output replaces ambiguous aliases such as `datePosted`, `applyUrl`, `lon`, `lat`, `experienceString`, and `degreeString` with explicit source and normalized fields. Missing source values remain absent.

### 🔗 Other actors

- [China Jobs Feed 中国招聘: Zhaopin, Liepin, 51job, Lagou, Shixiseng](https://apify.com/getascraper/china-jobs-feed) ↗ - reads several Chinese job boards in one run, in one format, with a monitor mode for new jobs.
- [💼 Liepin Scraper 猎聘: China Jobs, Salary Ranges & Change Tracking](https://apify.com/getascraper/liepin-scraper) ↗: Track white-collar roles and salary ranges from Liepin.
- [💼 Zhaopin Scraper 智联招聘: China Jobs & Salary API](https://apify.com/getascraper/zhaopin-jobs-scraper) ↗: Collect broad China jobs and salary data from Zhaopin.
- [💼 iGuopin Jobs Scraper 国聘: China SOE & Campus Jobs](https://apify.com/getascraper/iguopin-jobs-scraper) ↗: Track public Chinese state-owned enterprise, campus, and graduate roles.
- [💼 ChinaHR Jobs Scraper 中华英才网: China Hiring Data](https://apify.com/getascraper/chinahr-jobs-scraper) ↗: Collect broad China hiring records from ChinaHR.
- [🇨🇳 eChinaCities Jobs Scraper: English Jobs in China](https://apify.com/getascraper/echinacities-jobs-scraper) ↗: Find English-language jobs in China with relocation context.
- [🎓 NCSS Jobs Scraper 国家大学生就业: Graduate & Internship Data](https://apify.com/getascraper/ncss-jobs-scraper) ↗: Collect graduate and internship opportunities from the national platform.

# Changelog

This Actor's version history is a separate document: https://apify.com/getascraper/51job-scraper/changelog.md

# Actor input Schema

## `sourceMode` (type: `string`):

Build searches from the filters below, or run exact public 51job search-result URLs.

## `queries` (type: `array`):

Job titles or keywords such as python, data engineer, 软件工程师, 会计, or 销售经理. Every keyword is combined with every selected city, and salary statistics are grouped per keyword and city.

## `cities` (type: `array`):

Select one or more cities. The Actor removes duplicate jobs across the resulting search combinations. Common cities are listed; each city search returns up to 1,000 jobs at most.

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

Optional HTTPS links beginning with https://we.51job.com/pc/search. Detail pages and unrelated hosts are rejected.

## `experience` (type: `string`):

Filter by source-published work experience requirement.

## `education` (type: `string`):

Minimum source-published education requirement.

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

Optional 51job public-search employment-type code. Leave blank unless copied from a supported public search URL.

## `companyType` (type: `string`):

Filter by corporate structure.

## `companySize` (type: `string`):

Filter by source-published employee range.

## `industries` (type: `array`):

Optional source industry codes from a public 51job search URL.

## `jobFunctions` (type: `array`):

Optional source job-function codes from a public 51job search URL.

## `salaryBand` (type: `string`):

Optional salary-band code copied from a supported public 51job search URL. Leave blank for all salaries.

## `postedWithin` (type: `string`):

Optional 51job issue-date code copied from a supported public search URL.

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

Optional 51job public-search sort code.

## `excludeKeywords` (type: `array`):

Exclude rows whose title, company, description, industry, function, or source tags contain any supplied keyword.

## `includeRecruiterSignals` (type: `boolean`):

Include only source-published recruiter name, role, labels, online status, and status fields when available.

## `convertToUsd` (type: `boolean`):

Add monthly US dollar minimum, maximum and median salary using a free public exchange rate. The rate and its date are saved on every row. Leave off for CNY only.

## `onlyDisclosedSalary` (type: `boolean`):

Skip jobs listed as negotiable (面议) or without a salary. Useful for pay research.

## `minMonthlySalaryCny` (type: `integer`):

Keep only jobs whose highest listed monthly pay reaches this amount in CNY. Annual and daily pay is converted to monthly first. Jobs without a disclosed salary are excluded once this is set. Leave empty for no filter.

## `dropCrossCityDuplicates` (type: `boolean`):

When the same employer posts the same title in several of your cities, keep only the first. Off by default: duplicates are only flagged on the rows.

## `includeSourceFields` (type: `boolean`):

Add the complete public search-result object under a source key on every row, with 51job's own field names. Useful when moving a pipeline from another 51job scraper. Off by default.

## `salaryStatistics` (type: `boolean`):

Save a SALARY_STATS record with count, median, 25th and 75th percentile, minimum and maximum monthly pay per keyword and city, computed only from the jobs collected in this run.

## `salaryStatisticsMinSample` (type: `integer`):

A group is left out of the salary statistics when fewer jobs than this have a disclosed salary.

## `minRecruiterResponsiveness` (type: `integer`):

Keep only jobs whose recruiter responsiveness score (0-100, derived from the source's own recent-reply, today's-reply-count, and resume-activity labels) meets this minimum. Jobs with no responsiveness signal at all are excluded once this is set. Leave empty for no filtering.

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

Global maximum across all queries and cities. The run reports when this cap prevents complete source coverage.

## `maxPagesPerSearch` (type: `integer`):

Safety cap on public result pages fetched for each keyword-city search.

## `outputMode` (type: `string`):

Full records are best for research; compact records are better for databases, alerts, and feeds.

## `monitorMode` (type: `string`):

Snapshot returns every matching job. Changes compares complete runs and emits only new, updated, reappeared, or safely expired jobs.

## `stateName` (type: `string`):

Stable name that separates one watchlist or schedule from another.

## `includeUnchanged` (type: `boolean`):

Include unchanged rows during changes mode. Disabled by default to avoid duplicate result charges.

## `notifyOnNoChanges` (type: `boolean`):

Send a webhook heartbeat when a complete monitoring run finds no selected changes.

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

Datacenter routing is the tested default and keeps proxy cost low. Switch to Residential with Singapore, Hong Kong, or Japan only if a larger run is blocked or rate-limited.

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

Alias for a single 51job search URL, for pipelines built against another 51job scraper. When filled, it replaces the keyword and city search. Prefer Direct 51job search URLs above.

## `max_page` (type: `integer`):

Alias for Maximum pages per search. 0 means no limit.

## `max_results` (type: `integer`):

Alias for Maximum output rows. 0 means the largest allowed run (5,000).

## `notificationWebhookUrl` (type: `string`):

Optional secret HTTPS endpoint for a bounded JSON change summary. It is used only in changes mode.

## `notificationEvents` (type: `array`):

Change types that can trigger the webhook.

## Actor input object example

```json
{
  "sourceMode": "search",
  "queries": [
    "python"
  ],
  "cities": [
    "020000"
  ],
  "startUrls": [],
  "experience": "all",
  "education": "all",
  "employmentType": "",
  "companyType": "all",
  "companySize": "all",
  "industries": [],
  "jobFunctions": [],
  "salaryBand": "",
  "postedWithin": "",
  "sortBy": "0",
  "excludeKeywords": [],
  "includeRecruiterSignals": true,
  "convertToUsd": false,
  "onlyDisclosedSalary": false,
  "dropCrossCityDuplicates": false,
  "includeSourceFields": false,
  "salaryStatistics": true,
  "salaryStatisticsMinSample": 5,
  "maxItems": 20,
  "maxPagesPerSearch": 5,
  "outputMode": "full",
  "monitorMode": "snapshot",
  "stateName": "default",
  "includeUnchanged": false,
  "notifyOnNoChanges": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "notificationEvents": [
    "NEW",
    "UPDATED",
    "REAPPEARED",
    "EXPIRED"
  ]
}
```

# Actor output Schema

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

No description

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

No description

## `salaryStatistics` (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 = {
    "sourceMode": "search",
    "queries": [
        "python"
    ],
    "cities": [
        "020000"
    ],
    "startUrls": [],
    "experience": "all",
    "education": "all",
    "employmentType": "",
    "companyType": "all",
    "companySize": "all",
    "industries": [],
    "jobFunctions": [],
    "salaryBand": "",
    "postedWithin": "",
    "sortBy": "0",
    "excludeKeywords": [],
    "includeRecruiterSignals": true,
    "convertToUsd": false,
    "onlyDisclosedSalary": false,
    "dropCrossCityDuplicates": false,
    "includeSourceFields": false,
    "salaryStatistics": true,
    "salaryStatisticsMinSample": 5,
    "maxItems": 20,
    "maxPagesPerSearch": 5,
    "outputMode": "full",
    "monitorMode": "snapshot",
    "stateName": "default",
    "includeUnchanged": false,
    "notifyOnNoChanges": false,
    "proxyConfiguration": {
        "useApifyProxy": true
    },
    "notificationEvents": [
        "NEW",
        "UPDATED",
        "REAPPEARED",
        "EXPIRED"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("getascraper/51job-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 = {
    "sourceMode": "search",
    "queries": ["python"],
    "cities": ["020000"],
    "startUrls": [],
    "experience": "all",
    "education": "all",
    "employmentType": "",
    "companyType": "all",
    "companySize": "all",
    "industries": [],
    "jobFunctions": [],
    "salaryBand": "",
    "postedWithin": "",
    "sortBy": "0",
    "excludeKeywords": [],
    "includeRecruiterSignals": True,
    "convertToUsd": False,
    "onlyDisclosedSalary": False,
    "dropCrossCityDuplicates": False,
    "includeSourceFields": False,
    "salaryStatistics": True,
    "salaryStatisticsMinSample": 5,
    "maxItems": 20,
    "maxPagesPerSearch": 5,
    "outputMode": "full",
    "monitorMode": "snapshot",
    "stateName": "default",
    "includeUnchanged": False,
    "notifyOnNoChanges": False,
    "proxyConfiguration": { "useApifyProxy": True },
    "notificationEvents": [
        "NEW",
        "UPDATED",
        "REAPPEARED",
        "EXPIRED",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("getascraper/51job-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 '{
  "sourceMode": "search",
  "queries": [
    "python"
  ],
  "cities": [
    "020000"
  ],
  "startUrls": [],
  "experience": "all",
  "education": "all",
  "employmentType": "",
  "companyType": "all",
  "companySize": "all",
  "industries": [],
  "jobFunctions": [],
  "salaryBand": "",
  "postedWithin": "",
  "sortBy": "0",
  "excludeKeywords": [],
  "includeRecruiterSignals": true,
  "convertToUsd": false,
  "onlyDisclosedSalary": false,
  "dropCrossCityDuplicates": false,
  "includeSourceFields": false,
  "salaryStatistics": true,
  "salaryStatisticsMinSample": 5,
  "maxItems": 20,
  "maxPagesPerSearch": 5,
  "outputMode": "full",
  "monitorMode": "snapshot",
  "stateName": "default",
  "includeUnchanged": false,
  "notifyOnNoChanges": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "notificationEvents": [
    "NEW",
    "UPDATED",
    "REAPPEARED",
    "EXPIRED"
  ]
}' |
apify call getascraper/51job-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,getascraper/51job-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/e2mxWFMIH10tIVN0f/builds/kDyge1bArp32hnfeG/openapi.json
