# Naukri.com Scraper - Jobs, Salaries & Companies (`abotapi/naukri-com-scraper`) Actor

Scrape Naukri.com jobs by keyword or URL. Extract job title, company, skills, experience, salary, location and posting date, with optional full descriptions, education requirements and company details. Supports incremental monitoring and resume runs.

- **URL**: https://apify.com/abotapi/naukri-com-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 2 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 job records

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

## Naukri Scraper: India Job Listings, Salaries and Full Descriptions

Naukri Scraper turns Naukri.com, India's largest job board, into clean structured data. Search by keyword and city, or paste job and search links straight from the site, and every hit carries the title, company, skills, experience band, salary and posting date from the search results. Turn on full job details and the same record also carries the complete description, key skills, education, employment type, industry, company profile and view/apply counts from the job's own page. Export to JSON, CSV or Excel, or pull results straight into your app through the API.

### Why This Scraper?

- **Two ways to find jobs.** Search by keyword and an optional city, or paste your own job or search links, filters and all.
- **Every filter you built on the site comes along.** A pasted search link keeps the filters you applied on naukri.com, because the page path carries them; the actor walks it exactly as configured.
- **Card fields plus full detail on demand.** Every hit carries the search-card fields; switch on Fetch full job details for the complete description, key skills, education, employment type, industry, company profile and view/apply counts, and pay the detail surcharge only for records whose job page read actually succeeded.
- **Built for schedules.** Incremental mode returns only new, updated and reappeared postings on recurring runs, and a posting is only marked expired once a run has genuinely scanned the whole tracked search end to end.
- **Resume without repaying.** Continue one interrupted run from its previous run or dataset id and skip everything already collected there.
- **A refused run fails loudly instead of returning an empty dataset**, so "no jobs matched" is never confused with "the site could not be read".
- **Send results straight into your apps.** Pipe finished runs into Notion, Linear, Airtable or your own MCP-connected tool.

### Use Cases

- **Recruiters and staffing agencies:** track fresh postings for specific roles and cities without checking the site by hand.
- **Job seekers and career sites:** build a personal or aggregated feed of matching openings with salary, experience band and posting date.
- **Compensation and market research teams:** collect salary bands, experience ranges and skill tags across postings to benchmark a role or region.
- **Competitive intelligence teams:** monitor a set of roles or companies and get notified the moment a new or changed posting appears, using incremental mode.
- **Data teams and ML pipelines:** build historical datasets of postings, skill demand and hiring activity for analysis.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `recordId` / `jobId` / `kind` | `"999000111222"` / `"999000111222"` / `"job"` |
| `title` | `"Senior Backend Engineer"` |
| `url` | Naukri link back to the job's own page |
| `companyName` | `"Example Tech Solutions Pvt Ltd"` |
| `companyId` | `500123` |
| `companyLogo` | Naukri-hosted logo image link |
| `skills` | `["Python", "Django", "SQL"]` |
| `experience` / `minimumExperience` / `maximumExperience` | `"3-6 Yrs"` / `3` / `6` |
| `salary` / `currency` | `"8-14 Lacs PA"` (the site's own salary label; the numbers are in `salaryDetail`) / `"INR"` |
| `salaryDetail` | `{ "minimumSalary": 800000, "maximumSalary": 1400000, "currency": "INR", "hideSalary": false, "variablePercentage": 10 }` |
| `location` | `"Bengaluru"` |
| `descriptionSnippet` | short excerpt shown on the search card |
| `vacancy` | `2` |
| `postedAt` | `"2026-06-15T00:00:00+00:00"` (the exact posting timestamp) |
| `postedDaysAgoLabel` | `"3 Days Ago"` (a volatile display label, not the reliable field) |
| `walkinJob` | `false` |
| `searchScope` | the keyword and city, or pasted link, that produced this row |
| `companyRating` | `{ "aggregateRating": 4.2, "reviewsCount": 350, "reviewsUrl": "..." }` |
| `groupId` / `isTopGroup` / `companyJobsUrl` | the employer's company group id (the id `topCompanies` accepts), whether it is a featured top employer, and its careers page on the site |
| `externalApply` / `applyUrl` / `listingSource` | `true` and the employer's own application link when applying happens off-site; `listingSource` is how the listing reached the site (`jp` posted on the site, `crawled` copied from the employer's careers site) |
| `diversityTags` | `["women", "women returning to work"]` when the employer flags a hiring preference |
| `workFromHomeType` / `keySkills` | `"Hybrid"`, `"Remote"` or `"Work from office"` / `{ "preferred": [...], "other": [...] }` (full details) |
| `walkInDetails` | walk-in jobs only: `{ "startDate": "2026-10-05", "endDate": "2026-10-06", "dailyTiming": "10.00 AM - 4.00 PM", "venueAddress": "...", "saturdayWorking": true, "sundayWorking": true }` plus `mapUrl` / `contactName` / `contactPhone` when the employer publishes them |
| `internshipDuration` | internships only: `"3 months duration"` (the stipend is in `salary`, e.g. `"15,000/month"`) |
| `roleCategoryId` / `hasApplyQuestionnaire` / `scrapedAt` | the site's numeric role category id / `true` when applying asks the employer's screening questions / when this row was collected |

With **Fetch full job details** on, each record also carries `description`, `locations`, `locationDetails` (each location with its localities and location page link), `locationIds` (the site's numeric city ids, usable in the `cities` filter), `education`, `educationDetails` (`ug` / `pg` / `ppg` lists), `employmentType`, `jobType`, `industry`, `functionalArea`, `jobRole`, `roleCategory`, `postedByConsultant`, `viewCount`, `applyCount`, `companyDetails` (name, description, address, website, hiring-for and overview link, each only when the job page serves it), `companyBenefitsUrl` and `companyBenefitsReportedBy` from the job's own page, plus a structured `salaryDetail` breakdown whenever the card or job page carries one. The description comes in three formats: `description` (plain text), `descriptionHtml` (the job page's own markup) and `descriptionMarkdown` (ready for LLM and Markdown pipelines). In Incremental mode, each record also carries `changeType`, `changedFields`, `firstSeenAt`, `lastSeenAt` and `isRepost` (plus `repostOfJobId` when a new job id re-posts a job the campaign already tracked).

#### Output views

The **Output** tab offers six table views of the same dataset: **Jobs** (every field), **Job details** (the job-page fields), **Company insights (AmbitionBox)** (rating, salary insight, benefits, company profile), **Company reviews** (one row per review snippet, with its job), **Walk-in interviews** (walk-in dates, timing and venue) and **Changes (incremental mode)** (change type, changed fields, first/last seen, reposts). Reviews always ride on their job row in the dataset itself.

The **Run summary** link on the same tab opens a small `OUTPUT` record in the run's key-value store: `mode`, `recordsEmitted`, per-search `scopes`, `scopesFailed`, `pagesRejected`, `maxItemsReached` (true only when the item limit was actually reached), `pageLimitReached` (true when the page limit stopped a search), `coverageShortfall` (a search the source said had more jobs than were served), `unresolvedFilterValues` (filter names or ids that could not be matched, first 20) and `finishedAt`.

### How to Use

1. Pick a **mode**: `search` (keywords, optionally with one city) or `url` (paste job or search page links).
2. Fill in the keywords and city, or the links, and turn on **Fetch full job details** if you want the full description and company profile.
3. Set **Max items** (and optionally **Max pages per search**) to control run size and cost, then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Search two keywords in one city:**

```json
{
  "mode": "search",
  "keywords": ["python developer", "data analyst"],
  "location": "bangalore",
  "fetchDetails": true,
  "maxItems": 50
}
```

**Paste a filtered search link:**

```json
{
  "mode": "url",
  "urls": ["https://www.naukri.com/python-developer-jobs-in-bangalore"],
  "fetchDetails": true
}
```

**Card-only run, capped by pages instead of items:**

```json
{
  "mode": "search",
  "keywords": ["sales executive"],
  "fetchDetails": false,
  "maxPages": 5,
  "maxItems": 0
}
```

**Paste a single job link:**

```json
{
  "mode": "url",
  "urls": ["https://www.naukri.com/job-listings-senior-backend-engineer-example-tech-solutions-bengaluru-999000111222"]
}
```

#### URL mode: every supported link shape

In URL mode the **Job or search page links** field ships with a default list of eight distinct examples (one per genuinely different shape: keyword search, keyword plus city, city only, category, company careers, city-scoped company careers, company overview, single job page), so an untouched URL-mode run scrapes one page set per shape. The prefill adds a few variants of those shapes (page 2, a filter query, tracking parameters, the mobile host, the bare host) that resolve to the same targets. A run with the list emptied and no job ids ends as failed with a "No input" message. Search filters apply to Search mode only; a pasted search link keeps the filters in its own address.

Paste any mix of these into **Job or search page links**; search and company links are walked forward page by page the same way a Search-mode run is. `m.naukri.com` and the bare `naukri.com` host (no `www`) both work for every shape below, since the site itself redirects them.

| Shape | Example | What it does |
| --- | --- | --- |
| Keyword search | `naukri.com/python-developer-jobs` | Walks the keyword search |
| Keyword + city | `naukri.com/python-developer-jobs-in-bangalore` | Walks the keyword search in that city |
| City only, no keyword | `naukri.com/jobs-in-bangalore` | Walks every job in that city |
| A specific page | `naukri.com/python-developer-jobs-2` | Starts the walk from page 2 (treated as a partial scope for Incremental mode's EXPIRED detection) |
| A filtered link you built on the site | `naukri.com/python-developer-jobs?experience=5` | Keeps every filter from the URL's query string |
| Category pages | `/fresher-jobs`, `/walkin-jobs`, `/internship-jobs`, `/work-from-home-jobs`, `/it-jobs` | Each walks that fixed category |
| Company careers page | `naukri.com/tata-technologies-jobs-careers-1218` | Walks that one employer's open jobs only |
| Company careers page in one city | `naukri.com/capgemini-jobs-careers-in-bengaluru-gid-1288` | Walks that employer's open jobs in that city |
| Company overview page | `naukri.com/capgemini-overview-1288` (every record's `companyDetails.overviewUrl`) | Walks every open job of that company group |
| A single job page | `naukri.com/job-listings-...-<jobId>` | Reads that one job directly (no search) |
| A job page with tracking params | `naukri.com/job-listings-...-<jobId>?src=...&sid=...` | Tracking params are ignored; the job still resolves by its numeric id |

Naukri resolves a job page purely by the numeric id at the end of the URL; the surrounding slug text is cosmetic, so even a mismatched or garbled slug still reads the right job as long as the id is correct.

Any other naukri.com link, such as a recruiter page (`/recruiters/...`) or a jobseeker-login-only page (`/mnjuser/...`), has no public job data to scrape and is skipped with a warning in the run log rather than silently dropped.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/naukri-com-scraper").call(run_input={
    "keywords": ["python developer"], "location": "bangalore"
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], job["companyName"], job["salary"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/naukri-com-scraper').call({
  keywords: ['python developer'], location: 'bangalore',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Caps and page size

The source serves 20 jobs per page; the actor respects that page size and treats a short page as the natural end of a search. **Max items** is the one cap that always applies (a total across every keyword or link, `0` for unlimited); **Max pages per search** additionally bounds how deep any single keyword or search link is walked. When the source reports more matching jobs than the walk actually served, the affected search is treated as incomplete rather than assumed finished.

If job page reads keep failing, the actor stops reading job pages and returns search-card data for the rest of the run, without the detail charge for those rows. Currently a single job page that fails all three of its read attempts is enough to switch this on, so an otherwise healthy run can return its later rows without full details; re-run the search if you need them.

#### Resume & recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset id and the actor skips every record id already collected there, so you don't pay twice. It is unrelated to the page-by-page walk itself, which always restarts from page one and stops on its own natural end for the current run.
- **Incremental mode** (`incrementalMode`) is for repeating the same search on a schedule. Each record is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED` or `EXPIRED` (only after a run that scanned the whole tracked search, and only with `emitExpired`). `stateKey` names or shares the stored state; leaving it empty derives one automatically from the search and filter settings (never from Max items or Max pages, so raising a cap keeps the same baseline). With incremental mode off, output is exactly as before. A day-count age label re-rendering on its own (for example "13 Days Ago" becoming "14 Days Ago") never counts as an update; the exact posting timestamp does. The view/apply counts, the AmbitionBox rating block, the AmbitionBox review snippets, the AmbitionBox salary insight's figures, the company logo URL and the search label are ignored the same way by default, since each drifts on its own without the posting itself changing. `companyBenefits` is NOT ignored by default: it changes about as rarely as the posting itself, so a real change there is treated as a real update.
- **Skip reposted jobs (incremental mode only)** (`skipReposts`): every incremental row carries `isRepost`; it is `true` when a NEW job id re-posts a job this campaign already tracked under another id (same title, employer and location, `repostOfJobId` names the earlier id). Turn this on to leave those reposts out (not returned, not billed, still remembered).
- **Ignore fields when detecting changes (incremental mode only)** (`ignoreFieldsForChanges`) extends that default ignore list with your own field names, for example `salary` if a currency re-tagging alone shouldn't count as an update. Dotted paths like `companyRating.aggregateRating` reach into a nested field without ignoring its siblings. Ignored fields are still returned in the output; they just don't drive the NEW/UPDATED/UNCHANGED decision.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify > Settings > Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"MCP connectors"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` (walk keyword results) or `url` (read pasted links). |
| `keywords` | array | `["python developer"]` | Job keywords for search mode, exactly as typed into the site's search box. |
| `location` | string | (none) | Optional city applied to every keyword above. Leave empty to search all India. |
| `sortBy` | string | `relevance` | Search mode: `relevance` (site default) or `date` (newest first). |
| `experience` | integer | (none) | Search mode: only jobs open to this many years of experience (0-30, 0 = fresher). |
| `freshness` | string | `any` | Search mode: only jobs posted within `1`, `3`, `7`, `15` or `30` days. |
| `workMode` | array | (none) | Search mode: any of `office`, `remote`, `hybrid`, `temporary_wfh`. |
| `salaryRange` | array | (none) | Search mode: any of the site's salary bands in lakhs per year, `0to3` up to `100to500` (1-5 crore). |
| `cities` | array | (none) | Search mode: city names (e.g. Hyderabad, Bangalore) or numeric city ids; several at once. |
| `department` | array | (none) | Search mode: department names as the site shows them (e.g. Data Science & Analytics) or ids. |
| `roleCategory` | array | (none) | Search mode: role category names (e.g. Software Development) or ids. |
| `industry` | array | (none) | Search mode: industry names (e.g. IT Services & Consulting) or ids. |
| `companyType` | array | (none) | Search mode: any of `foreign_mnc`, `corporate`, `indian_mnc`, `startup`, `govt_psu`, `mnc`, `others`. |
| `postedBy` | string | `any` | Search mode: `company` (direct employer) or `consultant` (recruitment agency) only. |
| `topCompanies` | array | (none) | Search mode: company names (e.g. Infosys) or numeric company group ids. |
| `walkInOnly` | boolean | `false` | Search mode: only walk-in interview jobs. |
| `stipend` | array | (none) | Search mode, internship searches: any of `unpaid`, `0to10k`, `10to20k`, `20to30k`, `30to40k`, `40to50k`, `50k_plus` per month. |
| `internshipDuration` | array | (none) | Search mode, internship searches: any of `1` to `6` months or `flexible`. |
| `ugCourse` | array | (none) | Search mode: accepted undergraduate qualifications by name (e.g. B.Tech / B.E., B.Sc) or id. |
| `pgCourse` | array | (none) | Search mode: accepted postgraduate qualifications by name (e.g. MCA, M.Tech) or id. |
| `urlParams` | array | (none) | Advanced: extra query parameters added as-is to every search page address, for a site filter not listed above. |
| `urls` | array | sample | Job or search page links for URL mode. |
| `jobIds` | array | (none) | Job ids (the 12-digit number ending a job link) or job links, read directly, in either mode. |
| `fetchDetails` | boolean | `true` | Read each job's own page for the full detail set. Adds the per-job detail surcharge. |
| `maxItems` | integer | `20` | Total record cap across all keywords or links. `0` means unlimited. |
| `maxPages` | integer | `0` | Result pages walked per keyword or search link. `0` means no page limit. |
| `maxReviewsPerJob` | integer | `20` | Caps how many AmbitionBox employee-review snippets are kept per job, when the job's own page serves them. Only applies when Fetch full job details is on. |
| `proxyConfiguration` | object | Apify Proxy | Connection settings; the standard shared pool by default. |
| `resumeFromRunId` | string | (none) | Continue one specific interrupted run or dataset without re-collecting what it already gathered. |
| `incrementalMode` | boolean | `false` | Turn on for recurring runs over the same search: return only new, updated and reappeared records. |
| `stateKey` | string | (none) | Name or share an incremental-mode monitoring campaign. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) records that have not changed since the last run. |
| `emitExpired` | boolean | `false` | Also return (and bill) records no longer found, once a run has fully scanned the tracked search. |
| `skipReposts` | boolean | `false` | Incremental mode only: leave out new job ids that re-post a job the campaign already tracked (same title, employer and location). |
| `ignoreFieldsForChanges` | array | (none) | Incremental mode only: extra field names (or dotted paths) that should not make a record count as UPDATED. The age label, view/apply counts, the AmbitionBox rating block, the company logo URL and the search label are already ignored by default. |
| `mcpConnectors` | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which records are created. |
| `maxNotifyListings` | integer | `50` | Cap on records exported to each connector per run. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "recordId": "999000111222",
  "jobId": "999000111222",
  "kind": "job",
  "title": "Senior Backend Engineer",
  "url": "https://www.naukri.com/job-listings-senior-backend-engineer-example-tech-solutions-bengaluru-999000111222",
  "companyName": "Example Tech Solutions Pvt Ltd",
  "companyId": 500123,
  "companyLogo": "https://img.example.com/logo_images/groups/v1/500123.gif",
  "skills": ["Python", "Django", "SQL"],
  "experience": "3-6 Yrs",
  "minimumExperience": 3,
  "maximumExperience": 6,
  "salary": "8-14 Lacs PA",
  "salaryDetail": { "minimumSalary": 800000, "maximumSalary": 1400000, "currency": "INR", "hideSalary": false },
  "location": "Bengaluru",
  "descriptionSnippet": "Own backend services for a growing product team...",
  "description": "Full job description text goes here...",
  "vacancy": 2,
  "postedAt": "2026-06-15T00:00:00+00:00",
  "postedDaysAgoLabel": "3 Days Ago",
  "currency": "INR",
  "walkinJob": false,
  "searchScope": "python developer in bangalore",
  "education": "Any Graduate",
  "employmentType": "Full Time, Permanent",
  "jobType": "fulltime",
  "industry": "IT Services",
  "functionalArea": "Engineering - Software",
  "companyRating": { "aggregateRating": 4.2, "reviewsCount": 350 },
  "companyReviews": [
    {
      "reviewerName": "Anonymous",
      "roleAndLocation": "Backend Engineer in Bengaluru",
      "rating": "4",
      "likes": "Good work-life balance and a supportive team.",
      "dislikes": "Promotions could be faster.",
      "publishedOn": "01 Feb 2026",
      "reviewUrl": "https://www.ambitionbox.com/reviews/example-tech-solutions-reviews?rid=1234567"
    }
  ],
  "companySalaryInsight": {
    "jobProfileName": "Senior Backend Engineer",
    "averageCtc": "14.2",
    "minCtc": "11.0",
    "maxCtc": "18.5",
    "salariesUrl": "https://www.ambitionbox.com/salaries/example-tech-solutions-salaries/senior-backend-engineer"
  },
  "companyBenefits": ["Health Insurance", "Free Meal", "Transport / Travel Support"],
  "descriptionMarkdown": "**About the role**\n\n- Own backend services\n- Mentor two engineers",
  "roleCategoryId": "1028",
  "hasApplyQuestionnaire": false,
  "scrapedAt": "2026-06-18T09:30:00+00:00"
}
```

`companyReviews`, `companySalaryInsight` and `companyBenefits` come from the AmbitionBox widget naukri.com's own job page embeds (not a separate ambitionbox.com request); they're only present when Fetch full job details is on and the source actually served that widget for the company. `maxReviewsPerJob` caps the review list; the source itself may serve fewer than the cap.

### Plan Requirement

The actor runs on Apify's pay-per-event pricing: one event when the run starts, one event per job record returned, and one job detail enrichment event per record when Fetch full job details is on and the job page read actually succeeded. Company review snippets ride on their job row and have no event of their own. The **Pricing** tab shows the live rates.

Proxy: the default Apify Proxy setting reads the site on a measured basis (the standard shared pool served full 20-job result pages in testing), so most runs need no action. Free Apify plans do not include residential proxies. If the default connection is refused, the actor tries the next connection on its own and logs which one served the data. If your runs are still refused, pick Apify residential (India) under Connection & proxy (billed per GB on your account), or supply your own proxy URLs. When nothing could be loaded the run ends as failed with one of these messages: "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required", "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Naukri.com pages", or "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search."

### FAQ

#### How much does it cost?

You pay per job record returned, plus one detail enrichment charge per record when Fetch full job details succeeds for it. The **Pricing** tab shows the current rates. Use **Max items** to cap the cost of any run.

#### Is it legal to scrape Naukri.com?

This actor collects only publicly available job listing data. You are responsible for how you use it: follow Naukri's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution.

#### Can I get only new job postings on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental changes for scheduled runs**. Later runs then return only new, updated and reappeared postings, and unchanged ones are suppressed and not billed unless you turn on **Emit unchanged**.

#### Why didn't I get any EXPIRED rows even with Emit expired on?

Expired postings are only produced once a run has scanned its whole tracked search end to end: not capped by Max items or Max pages, not resumed, every scope reached its natural end (including a scope whose very first page comes back empty while the source still reports matching jobs for it, which also counts as incomplete), and the source's own reported job count matched what the walk actually served. A pasted search link that already starts past page one, or carries its own filter query string, counts the same way: the run never saw that scope's full window, so it is not trusted to decide what is EXPIRED from it. This check applies to the whole run, not one keyword at a time, so for example a two-keyword incremental run where the first keyword finishes cleanly but the second hits Max items mid-page skips expiry detection for both keywords that run. Raise Max items (or set it to `0`), or give each keyword its own scheduled run and state key, so a cap on one search never withholds expired detection on another.

#### What's the difference between Resume and Incremental mode?

**Resume** (`resumeFromRunId`) continues ONE specific interrupted run: paste its run or dataset id and the actor skips everything it already collected there. **Incremental mode** is for running the SAME search again and again, for example daily, and getting only what changed; it keeps its own state per search and filter combination, or per **State key**.

#### Why did my run fail instead of returning an empty dataset?

The log may show a boxed warning "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." and, once a backup connection serves data, "Served over the backup connection (the default connection was unavailable)." Both are informational; the run keeps going.

If every page read is refused, the run stops with a clear message so "no jobs matched" is never confused with "the site could not be read". Run it again in a few minutes. If only some keywords or links could be read, you still get what was found from the rest. One exception in Incremental mode: the check counts only returned records, so a scheduled run where every posting read was unchanged (and suppressed) and one page was refused also ends as failed, even though its state was still updated. Re-run it, or turn on **Emit unchanged** (which returns and bills those rows) if you need such runs to succeed.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable as the run finishes.

### 🔗 More scrapers you might like

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>💼 <a href="https://apify.com/abotapi/hh-ru-jobs-scraper"><b>HH.ru Jobs Scraper</b></a><br>From $1/1K. Scrape HH.ru job listings with 50+ structured fields. Search by filters or...</td><td>💼 <a href="https://apify.com/abotapi/edjoin-scraper"><b>EdJoin.org Scraper</b></a><br>Scrape EdJoin.org K-12 and higher-ed jobs by keyword, location, filters or URL. Extract...</td></tr>
<tr><td>💼 <a href="https://apify.com/abotapi/hiring-cafe-scraper"><b>Hiring.Cafe Jobs</b></a><br>From $1/1K. Unlock powerful Hiring.Cafe job data extraction. Use advanced filters or URLs...</td><td>💼 <a href="https://apify.com/abotapi/bayt-com-jobs-scraper"><b>Bayt.com Scraper</b></a><br>Scrape Bayt.com job listings by keyword, country, or URL. Extract structured data...</td></tr>
<tr><td>💼 <a href="https://apify.com/abotapi/dice-com-scraper"><b>Dice.com Scraper</b></a><br>Scrape tech job listings from Dice.com by keyword, filters, or URL. Extract job titles...</td><td>💼 <a href="https://apify.com/abotapi/stepstone-de"><b>StepStone.de</b></a><br>Scrape StepStone.de jobs by keyword, location, filters, or URL. Extract 35+ fields...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/naukri-com-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

Search mode walks the job results for your keywords. URL mode reads the pasted links instead: search pages are walked like a search, single job pages are read directly.

## `keywords` (type: `array`):

Job search keywords, exactly as you would type them into the site's search box.

## `location` (type: `string`):

Optional city name applied to every keyword above, e.g. bangalore, mumbai, delhi ncr. Leave empty to search all India.

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

Links to read. In URL mode this list is used; in Search mode it is ignored.

## `jobIds` (type: `array`):

Optional. Job ids (the 12-digit number at the end of a job page link) or job page links, read directly one job page each. Works in both modes, in addition to the keywords or links above.

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

Order of the results. Relevance is the site's default order; Date posted returns the newest jobs first.

## `experience` (type: `integer`):

Only jobs open to a candidate with this many years of experience (0 = fresher). Leave empty for any experience.

## `freshness` (type: `string`):

Only jobs posted within this many days. A time window is a moving view, so incremental mode does not mark jobs that age out of it as EXPIRED.

## `workMode` (type: `array`):

Only jobs with any of these work arrangements. Leave empty for all.

## `salaryRange` (type: `array`):

Only jobs whose advertised salary falls in any of these bands. Jobs that hide their salary are not matched by this filter.

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

Only jobs in any of these cities, by name (e.g. Hyderabad, Bengaluru, Pune; Bangalore, Gurgaon and other older spellings work too) or numeric city id. Unlike Location above, this accepts several cities at once.

## `department` (type: `array`):

Only jobs in any of these departments, by name as the site shows it (e.g. Engineering - Software & QA, Data Science & Analytics) or numeric id.

## `roleCategory` (type: `array`):

Only jobs in any of these role categories, by name (e.g. Software Development, Quality Assurance and Testing) or numeric id.

## `industry` (type: `array`):

Only jobs at employers in any of these industries, by name (e.g. IT Services & Consulting, Software Product) or numeric id.

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

Only jobs at any of these kinds of employer. Leave empty for all.

## `postedBy` (type: `string`):

Only jobs posted directly by the employer, or only jobs posted by a recruitment consultant.

## `topCompanies` (type: `array`):

Only jobs at any of these companies, by name (e.g. Infosys, Accenture, Tata Consultancy Services) or numeric company group id. Names are matched against the companies the site lists for the search.

## `walkInOnly` (type: `boolean`):

Only jobs with a walk-in interview.

## `stipend` (type: `array`):

Internship searches only (e.g. keyword internship): only internships paying any of these monthly stipends (INR). Leave empty for all.

## `internshipDuration` (type: `array`):

Internship searches only: only internships of any of these durations. Leave empty for all.

## `ugCourse` (type: `array`):

Only jobs accepting any of these undergraduate qualifications, by name (e.g. B.Tech / B.E., B.Sc, Any Graduate) or numeric id.

## `pgCourse` (type: `array`):

Only jobs accepting any of these postgraduate qualifications, by name (e.g. MCA, M.Tech, MBA/PGDM) or numeric id.

## `urlParams` (type: `array`):

Optional. Extra query parameters added as-is to every search page address, for a site filter not listed above (copy the key and value from the address of a filtered page on the site). Incremental mode does not mark jobs EXPIRED for a search that uses them.

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

Read each job's own page for the full description, key skills, education, employment type, workplace label, industry, company details and view/apply counts. Off keeps the lighter search-card fields. On adds the per-job detail surcharge, charged once per record whose job page read succeeded.

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

Maximum job records returned in total, across all keywords or links. 0 means unlimited (the run only stops when the results end).

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

Maximum result pages walked per keyword or search link. 0 (default) means no page limit, so Max items is the only cap that stops a run early.

## `maxReviewsPerJob` (type: `integer`):

Each job's own page carries up to a handful of AmbitionBox employee-review snippets for that job's company (reviewer, rating, title, likes/dislikes, date, link), plus a salary insight and a benefits list, when the site serves them. Caps how many review snippets are kept per job (the site itself may serve fewer); 0 keeps none. Only applies when Fetch full job details is on. Reviews ride on their job row (see the Company reviews output view).

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

The job board is read through a browser session. The standard shared pool serves it; pick Apify residential (India) only if your runs are refused, it is billed per GB.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large pull without returning items already collected there.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns every matching record as NEW. Later runs normally return only NEW, UPDATED and REAPPEARED records. Turn on Emit unchanged or Emit expired only when you also want those rows returned (and billed). State is kept separately for each search, filter and URL setup; use State key to name or deliberately share a monitoring campaign. The days-ago age label shown on each card is excluded from change detection: the site re-renders it as time passes even when the posting date is unchanged, so a display flip alone never marks a job UPDATED.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or deliberately share state across differently configured runs. Leave empty to let the actor derive a key automatically from the search and filter settings.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return records that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return records that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search; the source sometimes serves fewer results than it reports, and a run that could not see every matching job skips EXPIRED detection rather than guessing.

## `skipReposts` (type: `boolean`):

Off by default. In incremental mode every row carries isRepost: true when a NEW job id re-posts a job this monitoring campaign already tracked under another id (same title, employer and location; repostOfJobId names the earlier id). Turn this on to leave those reposts out of the results (they are not returned and not billed, but are still remembered).

## `ignoreFieldsForChanges` (type: `array`):

Output field names (or dotted paths like companyRating.aggregateRating) that should NOT make a record count as UPDATED. By default the posted-days-ago label, viewCount, applyCount, companyLogo, searchScope and the AmbitionBox rating/review count are already ignored, because they drift on their own without the posting itself changing. Add more field names here if changes to them are not relevant to you. Ignored fields are still returned in the output.

## `mcpConnectors` (type: `array`):

Connector IDs to export pushed records to when the run finishes.

## `notionParentPageUrl` (type: `string`):

When a Notion connector is used, the parent page the records are written under.

## `maxNotifyListings` (type: `integer`):

Cap on records sent through the connectors above per run.

## Actor input object example

```json
{
  "mode": "search",
  "keywords": [
    "python developer"
  ],
  "urls": [
    "https://www.naukri.com/python-developer-jobs",
    "https://www.naukri.com/python-developer-jobs-in-bangalore",
    "https://www.naukri.com/jobs-in-bangalore",
    "https://www.naukri.com/python-developer-jobs-2",
    "https://www.naukri.com/python-developer-jobs?experience=5",
    "https://www.naukri.com/it-jobs",
    "https://www.naukri.com/tata-technologies-jobs-careers-1218",
    "https://www.naukri.com/capgemini-jobs-careers-in-bengaluru-gid-1288",
    "https://www.naukri.com/capgemini-overview-1288",
    "https://www.naukri.com/job-listings-python-software-developer-capgemini-hyderabad-pune-bengaluru-6-to-11-years-240826007480",
    "https://www.naukri.com/job-listings-python-software-developer-capgemini-hyderabad-pune-bengaluru-6-to-11-years-240826007480?src=jobsearchDesk&sid=16960000000001",
    "https://m.naukri.com/python-developer-jobs",
    "https://naukri.com/data-analyst-jobs"
  ],
  "sortBy": "relevance",
  "freshness": "any",
  "postedBy": "any",
  "walkInOnly": false,
  "fetchDetails": true,
  "maxItems": 20,
  "maxPages": 0,
  "maxReviewsPerJob": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "skipReposts": false,
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `details` (type: `string`):

No description

## `companyInsights` (type: `string`):

No description

## `reviews` (type: `string`):

No description

## `walkIns` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `runSummary` (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 = {
    "mode": "search",
    "keywords": [
        "python developer"
    ],
    "urls": [
        "https://www.naukri.com/python-developer-jobs",
        "https://www.naukri.com/python-developer-jobs-in-bangalore",
        "https://www.naukri.com/jobs-in-bangalore",
        "https://www.naukri.com/python-developer-jobs-2",
        "https://www.naukri.com/python-developer-jobs?experience=5",
        "https://www.naukri.com/it-jobs",
        "https://www.naukri.com/tata-technologies-jobs-careers-1218",
        "https://www.naukri.com/capgemini-jobs-careers-in-bengaluru-gid-1288",
        "https://www.naukri.com/capgemini-overview-1288",
        "https://www.naukri.com/job-listings-python-software-developer-capgemini-hyderabad-pune-bengaluru-6-to-11-years-240826007480",
        "https://www.naukri.com/job-listings-python-software-developer-capgemini-hyderabad-pune-bengaluru-6-to-11-years-240826007480?src=jobsearchDesk&sid=16960000000001",
        "https://m.naukri.com/python-developer-jobs",
        "https://naukri.com/data-analyst-jobs"
    ],
    "fetchDetails": true,
    "maxItems": 20,
    "maxPages": 0,
    "maxReviewsPerJob": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "skipReposts": false,
    "maxNotifyListings": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/naukri-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",
    "keywords": ["python developer"],
    "urls": [
        "https://www.naukri.com/python-developer-jobs",
        "https://www.naukri.com/python-developer-jobs-in-bangalore",
        "https://www.naukri.com/jobs-in-bangalore",
        "https://www.naukri.com/python-developer-jobs-2",
        "https://www.naukri.com/python-developer-jobs?experience=5",
        "https://www.naukri.com/it-jobs",
        "https://www.naukri.com/tata-technologies-jobs-careers-1218",
        "https://www.naukri.com/capgemini-jobs-careers-in-bengaluru-gid-1288",
        "https://www.naukri.com/capgemini-overview-1288",
        "https://www.naukri.com/job-listings-python-software-developer-capgemini-hyderabad-pune-bengaluru-6-to-11-years-240826007480",
        "https://www.naukri.com/job-listings-python-software-developer-capgemini-hyderabad-pune-bengaluru-6-to-11-years-240826007480?src=jobsearchDesk&sid=16960000000001",
        "https://m.naukri.com/python-developer-jobs",
        "https://naukri.com/data-analyst-jobs",
    ],
    "fetchDetails": True,
    "maxItems": 20,
    "maxPages": 0,
    "maxReviewsPerJob": 20,
    "proxyConfiguration": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "skipReposts": False,
    "maxNotifyListings": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/naukri-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",
  "keywords": [
    "python developer"
  ],
  "urls": [
    "https://www.naukri.com/python-developer-jobs",
    "https://www.naukri.com/python-developer-jobs-in-bangalore",
    "https://www.naukri.com/jobs-in-bangalore",
    "https://www.naukri.com/python-developer-jobs-2",
    "https://www.naukri.com/python-developer-jobs?experience=5",
    "https://www.naukri.com/it-jobs",
    "https://www.naukri.com/tata-technologies-jobs-careers-1218",
    "https://www.naukri.com/capgemini-jobs-careers-in-bengaluru-gid-1288",
    "https://www.naukri.com/capgemini-overview-1288",
    "https://www.naukri.com/job-listings-python-software-developer-capgemini-hyderabad-pune-bengaluru-6-to-11-years-240826007480",
    "https://www.naukri.com/job-listings-python-software-developer-capgemini-hyderabad-pune-bengaluru-6-to-11-years-240826007480?src=jobsearchDesk&sid=16960000000001",
    "https://m.naukri.com/python-developer-jobs",
    "https://naukri.com/data-analyst-jobs"
  ],
  "fetchDetails": true,
  "maxItems": 20,
  "maxPages": 0,
  "maxReviewsPerJob": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "skipReposts": false,
  "maxNotifyListings": 50
}' |
apify call abotapi/naukri-com-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/naukri-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/bP1EqZknrmww4fJh0/builds/ajJ2ZcAtDX2TYgBJF/openapi.json
