Shine.com Jobs Scraper | India Jobs Feed, New and Closed avatar

Shine.com Jobs Scraper | India Jobs Feed, New and Closed

Pricing

from $1.40 / 1,000 scraped jobs

Go to Apify Store
Shine.com Jobs Scraper | India Jobs Feed, New and Closed

Shine.com Jobs Scraper | India Jobs Feed, New and Closed

Shine jobs scraper for India job listings: search any role and city, get one clean row per job with salary band, experience and description. Schedule it for a feed of what moved: mostly new roles, plus the rarer closures, each confirmed against the posting's own page.

Pricing

from $1.40 / 1,000 scraped jobs

Rating

0.0

(0)

Developer

Gerald Dobin

Gerald Dobin

Maintained by Community

Actor stats

0

Bookmarked

53

Total users

25

Monthly active users

23 days ago

Last modified

Share

Shine.com Jobs Scraper: India Job Listings

Give this Actor a list of job titles or skills, and optionally a list of Indian cities, and it returns every matching job advertised on Shine.com as clean rows in one schema. Shine is one of India's largest job portals, and each row carries the job title, the hiring company, the public job page link, every city the role is advertised in, the salary band parsed into numbers, the experience band parsed into years, the employment type, the industry, the employer's own skill tags, the posting and expiry dates, and the full job description as both cleaned HTML and plain text. Jobs are deduplicated by Shine's own job id, so a role that appears under two of your keywords arrives once and is billed once.

Who it is for

Recruiters and staffing agencies sourcing in India. Watch "python developer in Bangalore" and "data analyst in Pune" on a daily schedule and see every new opening with the pay band and the experience band already in the row, instead of clicking through a results page. The company id groups every listing from one poster, so you can tell a direct employer from an agency at a glance.

Job boards and aggregators that need an India feed. Point the Actor at fifty role and city combinations, run it nightly, and load the rows straight into your index. Every row has a stable jobId to upsert on and an expiresAt date, so yesterday's index and today's diff cleanly. Read "How long a run takes" below before you schedule a list that long: fifty searches at the default hundred jobs each needs about two hours, which is more than the default one hour run timeout.

Market and salary research across roles and cities. salaryMinLakh and salaryMaxLakh are real numbers in lakh per year, parsed out of the text Shine prints, so you can pivot pay against experience, city and industry without writing a parser. The same search run against several cities gives you a like for like comparison.

Sample output

One row per job. Here is a real row from a test run, with the two long description fields shortened for readability:

{
"jobId": "19443568",
"title": "Python Developer",
"company": "SP Staffing Services Private Limited Hiring For Leading MNC Company",
"companyId": 734032,
"url": "https://www.shine.com/jobs/python-developer/sp-staffing-services-private-limited/19443568",
"locations": ["Hyderabad"],
"location": "Hyderabad",
"salaryText": "Rs 26 - 38 Lakh/Yr",
"salaryMinLakh": 26,
"salaryMaxLakh": 38,
"salaryPeriod": "year",
"experienceText": "5 to 10 Yrs",
"experienceMinYears": 5,
"experienceMaxYears": 10,
"employmentType": "Regular, Full time",
"jobType": "Regular",
"industry": "IT Services & Consulting",
"keywords": ["rest api", "python", "flask", "django", "gcp", "fast api"],
"postedAt": "2026-08-11T07:12:45.000Z",
"postedText": "2026-08-11T12:42:45",
"expiresAt": "2026-10-09T18:30:00.000Z",
"descriptionHtml": "<h2><strong>Warm Greetings!</strong></h2><p><strong>Role:</strong> Python Developer ...</p>",
"descriptionText": "Warm Greetings!\nRole: Python Developer\nExperience: 5 to 10 yrs ...",
"searchKeyword": "python developer",
"searchLocation": null,
"sourcePage": "https://www.shine.com/job-search/python-developer-jobs",
"scrapedAt": "2026-09-12T15:44:10.311Z",
"error": null
}

Every field is present on every row. A field is null when Shine does not publish it, never missing and never an empty string. Dates are ISO 8601 in UTC, converted from the India Standard Time the site publishes them in, and postedText keeps the original for reference. A date that does not exist, such as a 31st of February, comes back empty rather than quietly sliding into March.

Salary numbers are always annual and always in lakh. A monthly band is multiplied by twelve, a crore figure is multiplied by a hundred, and a figure Shine writes in rupees, such as "Rs 50,000 - 3.0 Lakh/Yr", is converted, so every row is comparable with every other row. salaryPeriod records what the employer actually quoted, year or month. When the employer hides the pay, or writes a number with no unit at all, salaryText keeps whatever was published and the three salary fields are null: a wrong number in a salary dataset is worse than a missing one.

Contact details are removed. Employers routinely paste a recruiter's mobile number or email address into the description, the job title, or the company line. Every string in every row is checked and any email address or Indian phone number is replaced with [contact removed], and mailto: and tel: links are stripped out of the description entirely. The rest of the sentence is left alone, so the row is still readable. There is no setting to turn this off.

Every search you ask for leaves exactly one accounted outcome. Either it delivered job rows that carry its searchKeyword and searchLocation, or there is one row saying why it did not. That row carries searchKeyword, searchLocation, sourcePage, error and scrapedAt, with every other field empty, and the error column holds one of a short fixed set of codes so you can filter on it:

CodeWhat happened
no jobs for this searchShine has no jobs for that keyword and city. Not an error, just an answer.
filtered outShine had jobs and your own filters, or a duplicate from an earlier keyword, removed every one of them.
unrecognised cityShine did not know the city and answered with all India results, so the search was skipped rather than billed.
run out of timeThe run reached its wall clock limit before this search was finished, or before it was started at all.
run stopped earlyThe run stopped for another reason: its job ceiling, its cost ceiling, or Shine refusing three requests in a row.
change memory unreadableOnly with onlyChanges on. Part of that search's memory of the last run could not be read, so this run cannot say what moved. The search is skipped rather than delivered in full, and its memory is left untouched for the next run.
redirected to page 1Shine answered the first page with a redirect, which is how it says a search has nothing.
blocked, network error, unreadable page, page not found, address refused, response too large, unexpected errorSomething went wrong reading a page.

None of these rows is ever charged for. You pay only for job rows you actually received. The point of the notices is that a list of fifty keywords comes back as fifty accounted outcomes, so a keyword with no jobs on Shine is never confused with a keyword the run never got to.

With onlyChanges on, the two quiet notices, no jobs for this search and filtered out, are not written. A feed is asked for what moved, and answering a quiet morning with a row per search is not a feed. Every other notice is still written, because a search that was blocked or cut short is something you need to hear about even on a morning when nothing moved.

Input reference

InputWhat it does
keywordsJob titles or skills to search for, one per line, for example python developer. Each keyword becomes one search. Optional if you give searchUrls instead, but a run needs at least one of the two.
locationsIndian cities, one per line, for example Bangalore, Mumbai, Delhi NCR, Hyderabad, Pune, Chennai. Every keyword is searched in every city. Leave empty to search all of India.
searchUrlsShine job search addresses to use exactly as they are, for example https://www.shine.com/job-search/python-developer-jobs-in-bangalore. Paste a page two address and paging starts there.
maxJobsPerSearchHow many jobs to take from each search. Shine returns 20 per page. Default 100. Set 0 for everything Shine will serve, up to a safety cap of 250 pages, about 5,000 jobs, per search.
postedWithinDaysKeep only jobs posted in the last N days.
experienceMin, experienceMaxKeep only jobs whose published experience band overlaps this range, in years. A job that publishes no band is kept.
includeDescriptionInclude the full description as HTML and text. On by default. It costs one extra request per job delivered, which is most of what a long run spends its time on. Turning it off makes the dataset several times smaller and a run about twenty times faster.
trackChangesRemember each search between runs and label every row new, closed, salaryChanged, updated or unchanged. Off by default.
onlyChangesDeliver and bill for only the jobs that moved. Turns trackChanges on by itself. Off by default.
maxConcurrencyRequests in flight at once. Capped at 2. This does not make a long run finish sooner, see below.

One run collects at most 50 searches (keywords times cities, plus any search URLs) and at most 20,000 jobs. A run that asks for more searches than that is refused before it makes a single request, with a message telling you to split it, rather than being silently trimmed to a number you did not choose. A single search stops after 250 pages, or after ten pages in a row that produce no new rows, whichever comes first.

Common city names are mapped to the ones Shine's own URLs use, so Bengaluru finds Bangalore and Delhi NCR finds Delhi. If Shine does not recognise a city at all it quietly answers with the all India result set, which would mean paying for rows from the wrong place. This Actor spots that, warns you and skips that search rather than billing you for it.

How long a run takes

This Actor reads a public site politely: at most two requests in flight, and one to two seconds between any two request starts anywhere in the run. That pause is applied to the whole run rather than to each search, which is the only way it can actually hold, and it means a run's length is decided mostly by how many requests your input implies rather than by maxConcurrency. Shine answers in about 1.4 seconds, measured, which is less than the 3 seconds two slots and that pause allow, so in practice raising maxConcurrency does not make a long run finish sooner. It would only help if Shine became slower than about 3 seconds a page.

The arithmetic, at one request every 1.5 seconds on average:

What you asked forRequestsRoughly
1 search, 100 jobs, descriptions on1053 minutes
10 searches, 100 jobs each, descriptions on1,05026 minutes
50 searches, 100 jobs each, descriptions on5,2502 hours 11 minutes
50 searches, 100 jobs each, descriptions off2506 minutes

So an hour holds about 2,250 jobs with descriptions on, or the full 20,000 without them.

Those are best cases, and the run log says so. They assume every page of 20 jobs yields 20 rows you are charged for. Tight postedWithinDays or experience filters mean more pages read per row kept, so a heavily filtered run gets through fewer jobs in the same hour than the table suggests.

Apify gives every run a wall clock limit, one hour by default, and ends the run when it expires. This Actor reads that limit at the start and tells you in the first lines of the log whether what you asked for fits. If it does not, the fix is one of: raise the run's timeout in the run options, lower maxJobsPerSearch, split the keywords across several runs, or turn includeDescription off.

If the clock does run out, the run stops itself just before the limit rather than being killed mid-request, and every search you asked for gets a row: the ones that were cut short and the ones that were never started both arrive with error set to run out of time. Those rows are never charged for. You are only ever billed for job rows you actually received, but without those notices you would have no way to tell a keyword with no jobs from a keyword the run never reached.

How deep a single search goes

Shine tells you how many pages a search has, and it serves the first few of them to anyone. Past that it answers a page number with a redirect back to page one, and how far you get varies by search and by day: a broad all India search often serves two pages, while a narrower city search usually serves more. This Actor watches for that redirect and stops paging there, so you never receive, or pay for, the same twenty jobs a second time. The run log says which page it stopped on.

The way to get more jobs is therefore to go wider rather than deeper: more keywords, and each keyword split across cities. python developer in eight cities will return far more distinct jobs than python developer across all India ever will, and the rows carry searchLocation so you can tell them apart.

The change feed

A scrape answers "what is on Shine right now". A feed answers "what moved since yesterday", and that is the question a recruiter working a city actually has. Turn on trackChanges and every row gains a changeType:

changeTypeWhat it means
newThis job was not in the search the last time this Actor ran it.
closedThis job was in the search before, is not now, and its own page on Shine no longer serves it.
salaryChangedThe published pay moved. previous carries what it was.
updatedSomething else a job seeker can see moved: the title, the company, the cities, the experience band, the employment type, the industry, or the body of the posting.
unchangedNothing moved.

A row that is not new also carries previous, an object holding the title, company, location, salary, experience band and posting date as the last run saw them, so a diff is one column rather than a join.

Add onlyChanges and the unchanged rows are never delivered and never billed. A scheduled run on a quiet morning returns nothing and costs nothing. That is the whole point: you pay for movement, not for a copy of the same list every day.

Each search keeps its own memory, in a key-value store called shine-jobs-scraper-state in your own account. "python developer in Bangalore" and "data analyst in Pune" never mix, and a search you paste as a URL shares a memory with the same search typed as a keyword and a city.

Changing a filter starts that search on a fresh memory. maxJobsPerSearch, postedWithinDays, experienceMin, experienceMax and includeDescription all decide which jobs a search returns at all, so a run with different settings is looking at a different set of jobs. Rather than report everything outside the new settings as closed and bill you for those claims, the Actor starts that search again: one run of new rows, and the feed is honest from the next run on.

A closed row is the one claim this Actor is slowest to make, because it is the one you would act on. Two things must both be true. First, the run read that search from its first page to its last: no page errored, no listing was unreadable, your job limit did not cut it short, paging stopped because the search ended rather than because a safety guard fired, the run did not run out of time or budget, and the previous memory came back whole. Second, the job's own page on Shine was asked about, and said the posting has gone: Shine answers a dead job id with a redirect away from the posting, usually to its own front page, and a live one with a 200 on the job's own page. A 404 counts too. Anything else, a rate limit, a timeout, a server error, or a redirect that lands back on the same job because the site tidied its address, leaves the job open and reports nothing. That request reads only the first answer and never follows the redirect, so the address Shine names is read rather than fetched. If more than 200 jobs go missing from one search at once, that is a search that was read differently rather than two hundred employers withdrawing on the same morning, so the Actor reports none of them and says so in the log.

Those checks cost one request per missing job, through the same politeness rules as everything else, and a run where nothing went missing costs nothing extra at all.

The first run has nothing to compare against, so it delivers everything once and builds the memory. Schedule the second run for the next day.

Worked examples you can run as they are

Each of these is a saved, runnable configuration of this Actor with its input already filled in. Open one, press start, and look at the rows it returns before you write any input of your own.

Pricing

Pay per result. You are charged once for each job row delivered, and nothing else: no charge for a page that fails, no charge for an error row, and no charge for a duplicate a search returns twice. A run with the default input, two broad all India keywords, delivers around 60 rows, because Shine serves only the first pages of a broad search. Add cities to get more. Set maxJobsPerSearch to put a ceiling on what any one search can cost.

A closed row is a job row and is billed like one. With onlyChanges on, a run that finds nothing moved delivers no rows and is charged nothing.

What it does not do

  • No candidate or recruiter contact data. Shine's listing data includes a recruiter phone number and email address in their own fields, and those are never read into a row. Contact details that employers paste into the description or the title are found and replaced with [contact removed], and mailto: and tel: links are removed. You get the company name, the job fields and the public job page link. There is no option to get anything else.
  • No login and no private pages. Only pages any visitor can open, with no account and no session.
  • Only what robots.txt allows, and only search pages. Shine's terms permit automated access as its robots.txt permits, so the file is fetched at the start of every run and every address is checked against it. On top of that there is a positive rule: the only addresses this Actor will request at all are https://www.shine.com/job-search/... search pages, the https://www.shine.com/jobs/<job>/<company>/<id> page of a job it is delivering, and robots.txt. Every redirect is put through both checks again, so nothing can walk the Actor onto another part of the site. If robots.txt ever stops allowing the job search pages, the run stops with a clear message instead of scraping.
  • One job detail page per row delivered, and only when you asked for descriptions. Shine redesigned its search pages on 19 September 2026 and stopped putting the description, the industry, the employment type and the expiry date in the listing. Those are fields this Actor promises, so it reads the job's own page to get them back. That request is the last thing that happens before a row is delivered, so a job that was a duplicate or that your filters removed never costs a request. Set includeDescription to false and no detail page is fetched at all, which makes a run both faster and lighter on the site. A detail page that is missing or that robots.txt disallows costs that row its description and nothing else: the row is still delivered, and still carries everything the listing had.
  • No browser. Plain HTTP, at most two requests at a time, with a one to two second pause between any two requests anywhere in the run, not per search. If the site pushes back with a 403, a 429 or a challenge page, that refusal is retried once and no more, and three refusals in a row stop the whole run rather than pushing harder at a site that has said no.

Freshness

Rows are read live from Shine on every run, never from a cache or a stored copy, and scrapedAt records the moment each row was read. Shine timestamps each listing itself, and postedAt carries that, so you can tell a job posted this morning from one that has been up for a month. New jobs appear on the site continuously, so a daily or hourly schedule with postedWithinDays set to 1 gives you a clean stream of what is new without paying for the same rows again.

What people search for

If you arrived looking for a Shine jobs scraper, this is it. The same Actor answers searches for: Shine jobs scraper, Shine.com jobs, Shine job listings, India jobs scraper, Indian job portal scraper, jobs in Bangalore scraper, Mumbai jobs scraper, Hyderabad jobs scraper, Pune jobs scraper, Delhi NCR jobs scraper, Chennai jobs scraper.