Shine.com Jobs Scraper | India Jobs Feed, New and Closed
Pricing
from $1.40 / 1,000 scraped jobs
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
Maintained by CommunityActor stats
0
Bookmarked
53
Total users
25
Monthly active users
23 days ago
Last modified
Categories
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:
| Code | What happened |
|---|---|
no jobs for this search | Shine has no jobs for that keyword and city. Not an error, just an answer. |
filtered out | Shine had jobs and your own filters, or a duplicate from an earlier keyword, removed every one of them. |
unrecognised city | Shine did not know the city and answered with all India results, so the search was skipped rather than billed. |
run out of time | The run reached its wall clock limit before this search was finished, or before it was started at all. |
run stopped early | The run stopped for another reason: its job ceiling, its cost ceiling, or Shine refusing three requests in a row. |
change memory unreadable | Only 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 1 | Shine 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 error | Something 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
| Input | What it does |
|---|---|
keywords | Job 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. |
locations | Indian 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. |
searchUrls | Shine 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. |
maxJobsPerSearch | How 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. |
postedWithinDays | Keep only jobs posted in the last N days. |
experienceMin, experienceMax | Keep only jobs whose published experience band overlaps this range, in years. A job that publishes no band is kept. |
includeDescription | Include 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. |
trackChanges | Remember each search between runs and label every row new, closed, salaryChanged, updated or unchanged. Off by default. |
onlyChanges | Deliver and bill for only the jobs that moved. Turns trackChanges on by itself. Off by default. |
maxConcurrency | Requests 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 for | Requests | Roughly |
|---|---|---|
| 1 search, 100 jobs, descriptions on | 105 | 3 minutes |
| 10 searches, 100 jobs each, descriptions on | 1,050 | 26 minutes |
| 50 searches, 100 jobs each, descriptions on | 5,250 | 2 hours 11 minutes |
| 50 searches, 100 jobs each, descriptions off | 250 | 6 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:
changeType | What it means |
|---|---|
new | This job was not in the search the last time this Actor ran it. |
closed | This job was in the search before, is not now, and its own page on Shine no longer serves it. |
salaryChanged | The published pay moved. previous carries what it was. |
updated | Something 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. |
unchanged | Nothing 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.
- Feed an India job board with fresh listings daily. Point it at the roles your board covers, schedule it daily with a posted window of one day, and load the rows straight into your index.
- Find which Indian companies are hiring for a skill. Turns Shine.com search pages into rows of company, role, city, experience band and skills, for recruiting research and for B2B teams who use hiring as a buying signal.
- Get only the India jobs that changed since your last run. Schedule it and every run returns just what moved: roles posted since last time, roles whose salary band changed, and roles taken down, each labelled.
- Benchmark what a role pays in India right now. Pulls every current posting for a role with the advertised band parsed into a minimum and a maximum in lakhs per year, so the benchmark comes from what employers are offering this month rather than from a stale survey.
- Compare salary ranges by role across Indian job ads. Returns the salary exactly as the advert wrote it, plus a parsed minimum and maximum in rupees and the period, so you can compare pay by role and by city.
- Collect fresher and entry level job openings across India. Sets the experience ceiling to one year and returns every fresher opening for the roles you name, with the salary band, the city list and the apply link on each row.
- Scrape Shine.com jobs for a role in any Indian city. Search Shine.com by job title or skill and narrow it to Bangalore, Mumbai, Delhi, Pune, Hyderabad, Chennai or any other Indian city.
- Get the Shine.com postings for a skill from the last day. Schedule it daily with a posted within window and get only the Shine.com roles that the site says went live inside that window.
- Track IT hiring across Bangalore, Hyderabad and Pune. Run one skill across the three cities that carry most of India's IT hiring and compare them in a single dataset.
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], andmailto:andtel: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, thehttps://www.shine.com/jobs/<job>/<company>/<id>page of a job it is delivering, androbots.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
includeDescriptionto 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.