Job cards connect through a data interface to a checked storage tray.
Check the returned rows before storing them.

An API is a request from your program to another service. Apify is the host that starts the work and stores the result. An Actor is a hosted data collection program. The Naukri Jobs Actor is the Actor in this method. It reads public Naukri pages. It does not use a Naukri login. A Task is a saved input preset. The current public Task is only a starting input. It does not analyze jobs for you. The Actor saves source rows. You check the receipt and choose rows for your project.

This path uses Apify's API. It is not an official Naukri API. It is not a private company database. It is not a full index of Indian jobs. It shows this one public Actor.

1. Set one search and a row cap

Write down the rows you want before you start. Choose one role. Choose one Indian city, or leave the city empty. Choose how recent the source post should be. Choose a small maximum number of saved rows.

The current public Actor accepts this input:

  • `searchTerm` takes one keyword on each line.
  • `location` is optional.
  • `maxItems` is the row cap. It limits saved rows for the whole run. The cap is not separate for each keyword line. If you leave it out, the numeric cap is gone. Always send a number for this method.
  • `postedWithinDays` is text. Use "0", "1", "3", "7", "15", or "30". The text "0" means any post date.
  • `experienceYears` is optional. It is a whole number from 0 to 30. Fresher means little or no earlier job experience in that search setting. The value 0 asks for the fresher search setting. This method does not judge fit for the job.
  • `sortBy` is `relevance` or `date`.
  • `excludeCompanies` is optional. The example below does not use it.
  • `searchDepth` is `standard`, `extended`, or `deep`. `standard` is the narrow first check. It reads fewer source pages than the other two. This method does not need a deeper search.

The current public Task stores `searchTerm` "software engineer", `postedWithinDays` "7", `sortBy` "date", and `searchDepth` "standard". Put your role in `searchTerm`. Put your city in `location` if you want one city. Set your own `maxItems`.

A checked control used different values. It used one keyword, "software engineer". It sent no location. It used `postedWithinDays` "0", `maxItems` 2, `sortBy` "relevance", and `searchDepth` "standard". The run saved two unique Naukri job IDs. Each row had a title, a company name, and a location. Each row had a direct Naukri `jobUrl`. Each `sourceUrl` matched the requested query. `source` was naukri.com. Each row had `observedAt`. Both rows included `descriptionText`. Both rows included `experienceRaw`. The two numeric ranges were 0-5 and 0-2. Those zeros are observed minimum years. They are not empty fields. The control did not send `experienceYears`. It does not prove that a fresher filter was applied.

The checked run finished with success. Its OUTPUT record showed these known values. `rowsSaved` was 2. `requestedMaxItems` was 2. `pagesRequested` was 1. The receipt reported 20 candidates seen. `failedRequests` was 0. `blockedRequests` was 0. `stopReason` was `max_items_reached`. The run hit the cap. Two saved rows are not every matching job. The control did not state `searchesQueued`, `searchesFinished`, or `sourceStates`. Those three values are unknown. Unknown is not the same as 0.

A saved row can include the fields below.

  • `schemaVersion`
  • `jobId`
  • `title`
  • `companyName`
  • `location`
  • `experienceRaw`
  • `experienceMinYears`
  • `experienceMaxYears`
  • `skills`
  • `descriptionText`
  • `postedAt`
  • `postedAtRaw`
  • `jobUrl`
  • `searchTerm`
  • `searchLocation`
  • `sourceUrl`
  • `pageNumber`
  • `rankOnPage`
  • `status`
  • `statusReason`
  • `source`
  • `observedAt`

An optional value can be absent. Absence is unknown. It is not a zero. `descriptionText` can be present and still miss part of the employer text. It can also miss a requirement. `postedAt` and the experience fields are what the source showed. They are not a promise that the employer will hire. `observedAt` is when the Actor saw the row. It is not the time Naukri first published the post. Keep those two times separate.

`searchTerm`, `searchLocation`, and `observedAt` belong to the raw source row. If you add your own query column later, you created that column. It is not a source field named query.

2. Start the run and keep the run ID

Use your own Apify account. Keep the token in the safe place your project already uses for secrets. A token is a secret that proves the call belongs to your account. Keep a real token out of articles. Keep it out of browser code. Keep it out of logs. Keep it out of a picture of a URL. This public read does not need a Naukri login.

The start call returns information about the run. It does not return job rows. A run is one start of the Actor. Send the input as JSON. JSON is a text format for structured data. Include the header `Content-Type: application/json`. Send the token in the `Authorization` header. Write `Bearer`, one space, then the token. The body below is original teaching input. It is not a live result.

http
POST https://api.apify.com/v2/acts/chronometrica~naukri-jobs-scraper/runs
Authorization: Bearer <APIFY_TOKEN>
Content-Type: application/json

{
  "searchTerm": "data analyst",
  "location": "Bengaluru",
  "maxItems": 2,
  "postedWithinDays": "7",
  "sortBy": "date",
  "searchDepth": "standard"
}
2. Start the run and keep the run ID

This body requests the role data analyst. It limits the place to Bengaluru. It saves at most 2 rows. It asks for posts from the last 7 days. It sorts by date. It uses standard depth. The start-run documentation describes this endpoint. The same API lets you set a timeout and a charge cap. A charge cap stops the run at a cost limit you choose. Set both limits for a first run. This article gives no price.

From the response, save `data.id`. That is the run ID. Also save the input and options the service stored. The run ID, the dataset ID, and the store ID are three different values. Keep them distinct.

3. Wait for a finished state

Read the run with the same private header. Wait a short time between reads. Stop when the status is terminal. A terminal status is a finished status. The finished values are `SUCCEEDED`, `FAILED`, `TIMED-OUT`, and `ABORTED`.

http
GET https://api.apify.com/v2/actor-runs/<RUN_ID>
Authorization: Bearer <APIFY_TOKEN>
3. Wait for a finished state

Save `status`, `buildId`, `startedAt`, `finishedAt`, `defaultDatasetId`, and `defaultKeyValueStoreId`. `SUCCEEDED` means the Actor finished. It does not mean the source was fully covered. `FAILED`, `TIMED-OUT`, and `ABORTED` need a look before you trust rows. The run-status documentation describes this read.

4. Read the OUTPUT receipt

OUTPUT is the Actor's summary record. Read it from the key-value store. A key-value store holds named records for the run. Use `defaultKeyValueStoreId` as the store ID.

http
GET https://api.apify.com/v2/key-value-stores/<STORE_ID>/records/OUTPUT
Authorization: Bearer <APIFY_TOKEN>
4. Read the OUTPUT receipt

Read these fields:

  • `rowsSaved` is the number of rows stored.
  • `requestedMaxItems` is the cap you asked for.
  • `searchesQueued` is the search work lined up.
  • `searchesFinished` is the search work that finished.
  • `pagesRequested` is the number of source pages requested. A source page is one Naukri results page. It is not one read of the saved dataset.
  • `blockedRequests` is the number of blocked source requests.
  • `failedRequests` is the number of failed source requests.
  • `sourceStates` says how the source ended.
  • `stopReason` says why the Actor stopped.

If `rowsSaved` equals `requestedMaxItems` and `stopReason` is `max_items_reached`, the cap stopped the run. The rows can still help. They are not the full search. A counted 0 means the Actor reported none. A missing field is unknown. Do not change unknown into 0.

Use `sourceStates` before you explain an empty file. An empty result can mean the source returned no rows. It can also mean the source was not reached. Those two outcomes are different. If `failedRequests` or `blockedRequests` is above 0, do not treat the city as empty. If `sourceStates` is missing, the source outcome is unknown. Keep the receipt either way.

5. Read the dataset after the run ends

A dataset is the saved set of row items. Read it after the run is terminal. A dataset that is still growing is not stable. Finish the run first. If you need rows before the end, design that case on purpose. Do not treat a middle page as the final set.

http
GET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=json&offset=0&limit=2
Authorization: Bearer <APIFY_TOKEN>
5. Read the dataset after the run ends

The response is a JSON array. `offset` is the number of saved rows to skip. `limit` is the number of rows to return in this read. This read is a dataset page. It is not a Naukri source page. The dataset-items documentation describes this call. Count the rows in the array. Add that count to `offset`. Read again. Keep a small receipt for every read. The receipt needs the offset, the limit, the returned count, and any read error.

For a finished dataset of two rows, the first read can return two rows. The next read uses offset 2 and limit 2. It can return an empty array. The empty array has a returned count of 0. That 0 means this page had no more rows. It does not mean the earlier rows are gone. Rows read are 2. If `rowsSaved` is 2, those counts match.

If a dataset read fails, keep that failure in the receipt. Do not replace a failed read with an empty array. The unread rows are still unknown.

6. Save identity before you filter

Store the raw row. Store `jobId` as text. Keep leading zeroes. A program that stores the ID as a number can drop those zeroes. Add the source prefix `naukri:` to the job ID. That prefix is the namespace. A namespace shows which site created the ID. Another job site can use the same digits for a different job. Its namespace stays separate.

Also store `jobUrl`, `sourceUrl`, raw `searchTerm`, raw `searchLocation`, `observedAt`, and the run ID. Check that the identity is present. Check the types you expect. If `jobId` is missing, the row stays unresolved. Unresolved means you cannot tie the row to a source job yet. Do not create a source key for it.

An observation is one saved sighting of a source row. The same listing on a later run is a new observation of a known source job. It is not automatically a new open job. Keep duplicate rows visible. Keep a conflict between two rows for one identity visible. Then apply your project rules. Keep failed rows in the receipt when you select the useful rows.

Salary meaning is outside this method. Rules about fit for the job are outside this method. This method does not submit applications. A later schedule is a new choice. This article does not start a repeating collection.

7. Line up a finished receipt

The story in this section is original fiction. It is not live test output. It is not a product result. The IDs, URLs, and times are made up for teaching. Do not request these story URLs. Do not treat the rows as real Naukri rows.

Fiction run R1 uses the teaching body from the start section. The story request is a POST to `https://api.apify.com/v2/acts/chronometrica~naukri-jobs-scraper/runs` with `Content-Type: application/json`. The story start response includes `data.id` "R1". It includes no job rows.

R1 later finishes with status `SUCCEEDED`. The story dataset ID is D1. The story store ID is K1. The saved run record in the story looks like this:

json
{
  "data": {
    "id": "R1",
    "status": "SUCCEEDED",
    "buildId": "fiction-build",
    "startedAt": "2026-04-01T09:00:00Z",
    "finishedAt": "2026-04-01T09:05:00Z",
    "defaultDatasetId": "D1",
    "defaultKeyValueStoreId": "K1"
  }
}
7. Line up a finished receipt

The story OUTPUT record is:

json
{
  "rowsSaved": 2,
  "requestedMaxItems": 2,
  "failedRequests": 0,
  "stopReason": "max_items_reached"
}
7. Line up a finished receipt

In this story, `blockedRequests`, `searchesQueued`, `searchesFinished`, `pagesRequested`, and `sourceStates` are not given on that OUTPUT record. Leave them unknown. Do not write 0 for them. `failedRequests` is 0 because the story says there were no failed requests. That 0 is known.

The first dataset read uses offset 0 and limit 2. It returns this array:

json
[
  {
    "jobId": "000101",
    "jobUrl": "https://www.naukri.com/example-fiction/000101",
    "sourceUrl": "https://www.naukri.com/example-fiction/search",
    "searchTerm": "data analyst",
    "searchLocation": "Bengaluru",
    "source": "naukri.com",
    "observedAt": "2026-04-01T09:04:00Z"
  },
  {
    "jobId": "000102",
    "jobUrl": "https://www.naukri.com/example-fiction/000102",
    "sourceUrl": "https://www.naukri.com/example-fiction/search",
    "searchTerm": "data analyst",
    "searchLocation": "Bengaluru",
    "source": "naukri.com",
    "observedAt": "2026-04-01T09:04:00Z"
  }
]
7. Line up a finished receipt

The returned count is 2. These story rows do not include `postedAt`. The publish time is unknown. It is not 0. The next read uses offset 2 and limit 2. It returns:

json
[]
7. Line up a finished receipt

The returned count is 0. Rows read are 2. `rowsSaved` is 2. The counts match: 2 equals 2. Known source IDs are `naukri:000101` and `naukri:000102`. That is two known IDs. The leading zeroes stay in the text. Both rows keep the story source URL, the raw query fields, and `observedAt`. The cap means two observed listings. It does not mean Bengaluru has only two open jobs.

A later part of the same fiction is run R2. The story says R2 returns `000102` again and `000103`. It does not give a separate OUTPUT record. Do not invent `stopReason` or request counts for R2. Its two story rows are:

json
[
  {
    "jobId": "000102",
    "jobUrl": "https://www.naukri.com/example-fiction/000102",
    "sourceUrl": "https://www.naukri.com/example-fiction/search",
    "searchTerm": "data analyst",
    "searchLocation": "Bengaluru",
    "source": "naukri.com",
    "observedAt": "2026-04-08T09:04:00Z"
  },
  {
    "jobId": "000103",
    "jobUrl": "https://www.naukri.com/example-fiction/000103",
    "sourceUrl": "https://www.naukri.com/example-fiction/search",
    "searchTerm": "data analyst",
    "searchLocation": "Bengaluru",
    "source": "naukri.com",
    "observedAt": "2026-04-08T09:04:00Z"
  }
]
7. Line up a finished receipt

Store both R2 rows as new observations. Only `naukri:000103` is a new known source ID. `naukri:000102` is already known.

The arithmetic uses plain counts:

  • R1 observations = 2.
  • R2 observations = 2.
  • All observations = 2 + 2 = 4.
  • Known IDs after R1 = 2.
  • New known ID on R2 = 1.
  • Repeated known ID on R2 = 1.
  • Known IDs after both runs = 2 + 1 = 3.
  • Unresolved IDs in these four rows = 0, because each story row has a `jobId`.

Four observations are not four jobs. Three known source IDs are not three employer jobs. The story has no separate match between employers. It does not prove three openings.

7. Line up a finished receipt
CheckCount
R1 observations2
R2 observations2
All observations4
Known source IDs3
New known ID in R21
Repeated known ID in R21
Unresolved IDs in the four rows0
Employer jobs provednot shown

One extra fiction check is not part of those four rows. If a download repeats `000102` on the same page, record the duplicate. Do not create a second identity. The known ID stays `naukri:000102`. If another story row has a title but no `jobId`, add 1 to unresolved. Do not create a key such as `naukri:unknown`.

Give your project the raw rows, the run IDs, and this receipt. Add a short note that the rows came from public Naukri pages through this Actor. Add the time in `observedAt`. Then the project can use the sample. The cap is not a full market.

Sources & further reading

By Chronometrica · Updated

Back to top ↑