The Mine Works
← All posts
tutorial October 8, 2026 · 10 min read Updated October 8, 2026

Compare Pay for a Role by Experience Level on Shine

Run one Shine.com search per experience band, keep the listings that show pay, and turn them into salary ranges in lakhs for each band.

Try the scraper

The actor referenced in this article. Pay only for results delivered.

View the scraper →

What does a Python developer with four years of experience get offered in Bangalore? Salary surveys answer once a year, for a broad job family, from people who chose to fill in a form. Job listings answer every day, for the exact title and city, from the people doing the hiring. The catch is that only some listings show pay, the pay is written as text, and a job board’s experience filter is looser than it looks.

Try it live: Shine.com Jobs Scraper. Pay per result delivered. Failed and empty results are never charged.

The Shine.com Jobs Scraper parses both halves for you. Shine’s pay text for a 10 to 22 lakh range becomes salary_min_lakhs: 10 and salary_max_lakhs: 22, and “4 to 6 Yrs” becomes experience_min_years: 4 and experience_max_years: 6. Run one search per experience band, keep the rows that show pay, and you have a pay ladder for the role.

What you get and who it is for

FieldWhat it holds
salary_min_lakhs, salary_max_lakhsThe posted range in lakhs a year, only when pay is shown
salary_textShine’s own pay text, or [Salary Hidden]
experience_min_years, experience_max_yearsThe experience range as numbers
experience_textThe range as Shine shows it, such as “4 to 6 Yrs”
title, companyWho is advertising the range
skillsShine’s keyword tags, to check the role matches
employment_type, shift_typeRegular, contractual, internship or work from home; full or part time
job_id, apply_urlThe listing on shine.com

This is for HR and compensation teams pricing a new opening, staffing firms quoting clients, candidates checking whether an offer is in line before they negotiate, and analysts tracking how advertised pay for a role moves between cities.

The input

One run per experience band, with everything else the same. This is the run for the 3 to 5 year band:

{
  "searchKeywords": ["python developer"],
  "location": "Bangalore",
  "experienceLevel": "3to5",
  "employmentType": "regular",
  "maxJobs": 300
}

Then repeat it with experienceLevel set to each other band you care about. Shine’s bands are lt1, 1to2, 3to5, 6to8, 9to10, 11to15 and gt15.

  • experienceLevel takes one band per run, which is why the Python script below loops. The filter matches any job whose experience range overlaps the band, so bands overlap too.
  • employmentType: "regular" keeps internships and contract roles out. Their pay follows different rules and would pull the low end down.
  • maxJobs should be generous. Only a share of rows show pay, so 300 listings in a band may give you a few dozen offers or fewer. One keyword can reach 800 jobs, and one run can reach 1,000.
  • Leave out salaryRange. It is Shine’s own pay filter, and it is too loose to measure pay with; see the limits below.
  • location takes one city. Leave it empty to compare pay across all of India, then group by the locations field yourself.

What the output looks like

[
  {
    "job_id": "19550923",
    "title": "Urgent Hiring for Python Developer with AWS",
    "company": "VIZLOGIC DIGITAL SOLUTIONS PRIVATE LIMITED",
    "locations": ["Bangalore"],
    "experience_min_years": 3,
    "experience_max_years": 6,
    "experience_text": "3 to 6 Yrs",
    "salary_min_lakhs": 9,
    "salary_max_lakhs": 14,
    "employment_type": "regular",
    "skills": ["python", "devops", "aws"],
    "posted_days_ago": 15
  },
  {
    "job_id": "19623932",
    "title": "Python Developer",
    "company": "REIS Staffing & HR Services Pvt Ltd",
    "locations": ["Bangalore", "Gurugram", "Mumbai City", "Pune", "Chennai"],
    "experience_min_years": 4,
    "experience_max_years": 6,
    "experience_text": "4 to 6 Yrs",
    "salary_min_lakhs": 10,
    "salary_max_lakhs": 20,
    "employment_type": "regular",
    "skills": ["python", "api", "pl"],
    "posted_days_ago": 1
  },
  {
    "job_id": "19654215",
    "title": "Senior Python Developer",
    "company": "Integrated Personnel Services Limited",
    "locations": ["Bangalore", "Pune"],
    "experience_min_years": 5,
    "experience_max_years": 10,
    "experience_text": "5 to 10 Yrs",
    "salary_min_lakhs": 1,
    "salary_max_lakhs": 26,
    "employment_type": "regular",
    "skills": ["python", "django", "microservices", "rest api", "fastapi"],
    "posted_days_ago": 0
  }
]

These rows come from our September 30, 2026 run of python developer in Bangalore with the 3to5 band, regular and full time, 100 jobs. It matches the input above except for maxJobs and the full time shift filter. We trimmed the description, salary_text (Shine’s original Lakh/Yr text), the dates and the recruiter name, which is often a person’s name.

Here is what those 100 rows gave once processed with the script below:

StepRows
Listings returned100
Listings showing pay14
Distinct offers after merging reposts10
Offers with a usable range (low at least a fifth of high)7
Companies behind those 73
Median range of the 710 to 20 lakhs

Two things stand out. Four of the seven usable offers came from one staffing firm, so one recruiter’s brief sets most of the picture. And the third row above, 1 to 26 lakhs for a 5 to 10 year role, says almost nothing about pay; the script flags ranges like that instead of averaging them in.

Run it in Apify Console

  1. Open https://apify.com/themineworks/shine-jobs-scraper and click Try for free.
  2. In Search keywords, enter the role. Keep it to the title people use on Shine.
  3. Enter the city in Location, or clear it for all of India.
  4. Pick the first Experience level band, set Employment type to Regular and Salary range to Any.
  5. Set Max jobs to 300 and click Start.
  6. Change Experience level to the next band and start again. Each run has its own dataset.
  7. Export each dataset as CSV or Excel from the Storage tab, or use the script below to do all bands at once and merge them.

Run it from Python

pip install apify-client
import csv
import statistics
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
ROLE = "python developer"
CITY = "Bangalore"
BANDS = ["1to2", "3to5", "6to8", "9to10", "11to15"]

jobs = {}  # job_id -> row, so a job found in two bands counts once
for band in BANDS:
    run = client.actor("themineworks/shine-jobs-scraper").call(run_input={
        "searchKeywords": [ROLE],
        "location": CITY,
        "experienceLevel": band,
        "employmentType": "regular",
        "maxJobs": 300,
    })
    for item in client.dataset(run["defaultDatasetId"]).iterate_items():
        if "_type" in item:
            continue  # free summary and info rows
        jobs.setdefault(item["job_id"], item)

def bucket(min_years):
    for top, label in [(0, "0 yrs"), (2, "1 to 2 yrs"), (5, "3 to 5 yrs"),
                       (8, "6 to 8 yrs"), (10, "9 to 10 yrs"), (15, "11 to 15 yrs")]:
        if min_years <= top:
            return label
    return "16+ yrs"

offers = {}
for job in jobs.values():
    low, high = job.get("salary_min_lakhs"), job.get("salary_max_lakhs")
    if low is None or high is None:
        continue  # pay hidden, or only one figure shown
    # The same role reposted by the same recruiter is one offer, not five.
    key = (job["title"].strip().lower(), job["company"], low, high,
           job["experience_min_years"], job["experience_max_years"])
    offers.setdefault(key, {
        "band": bucket(job["experience_min_years"]),
        "title": job["title"],
        "company": job["company"],
        "experience": job["experience_text"],
        "low_lakhs": low,
        "high_lakhs": high,
        "wide_range": low * 5 < high,  # e.g. 1 to 26 lakhs says little
        "apply_url": job["apply_url"],
    })

rows = sorted(offers.values(), key=lambda r: (r["band"], r["low_lakhs"]))
if rows:
    with open("shine_pay_by_experience.csv", "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
        writer.writeheader()
        writer.writerows(rows)

print(f"{len(jobs)} listings, {len(offers)} distinct offers with pay")
for band in sorted({r["band"] for r in rows}):
    usable = [r for r in rows if r["band"] == band and not r["wide_range"]]
    if not usable:
        continue
    lows = [r["low_lakhs"] for r in usable]
    highs = [r["high_lakhs"] for r in usable]
    firms = len({r["company"] for r in usable})
    print(f"{band:>12}: {len(usable):3} offers from {firms:2} companies, "
          f"median {statistics.median(lows)} to {statistics.median(highs)} lakhs")

The script does three things a plain export does not:

  • Groups by the parsed experience, not the filter. Our 3 to 5 year run returned 22 rows listed as “0 to 4 Yrs” and 24 as “5 to 9 Yrs”. Bucketing by experience_min_years puts each offer where its own minimum says it belongs.
  • Merges reposts. Staffing firms post one brief several times with new job ids. Same title, company, pay and experience counts once.
  • Flags placeholder ranges. A range whose low end is under a fifth of its high end is kept in the CSV but left out of the medians.

The printout ends with one line per band: how many usable offers, from how many companies, and the median low and high. Always read the company count next to the median. Seven offers from three companies is a signal, not a benchmark.

Refresh it every month

Pay ranges on Shine change with demand, so a monthly snapshot is enough. Save one task per band in Apify Console, open Schedules, and add all of them to one schedule with a cron expression such as 0 6 1 * * (06:00 on the first of each month) and your time zone set to Asia/Kolkata. Each run gets its own dataset. If you prefer code, skip the Console schedule and run the Python script once a month from cron on your own machine; it starts the five runs itself.

To build a trend, append each month’s band medians to a Google Sheet with a date column, or send the datasets there with Apify’s Google Sheets integration and chart low and high per band over time. After a few months of bands sitting next to each other, it is a short step to a hiring dashboard built from free job data. Leave monitor mode off for this job: you want the whole current market each month, not only what is new.

What it costs

Billing is per job delivered: $2.00 per 1,000 jobs on the Bronze plan, $1.75 on Silver, and $1.50 on Gold and above. There is no start fee and no charge per run, and summary rows and empty searches are free.

  • Our 100-job run for the 3 to 5 year band cost $0.20 on Bronze.
  • The five-band script above is at most 1,500 jobs: $3.00 on Bronze, about $2.63 on Silver and $2.25 on Gold. Because each band is its own run, a job that overlaps two bands is charged in both runs, even though the script counts it once.
  • A monthly refresh of the same five bands is at most $36 a year on Bronze.
  • The cheaper option is one run with no experienceLevel and maxJobs: 800, bucketed by the same script: at most $1.60 on Bronze. You lose control over how many rows each band gets, since Shine ranks by relevance, not by experience.

Limits worth knowing

  • Most rows have no pay. About a third of Shine listings show a figure, and for some roles far fewer: 14 of 100 in our python developer run. Pay comparisons need volume, so widen with more keywords for the same role, such as python developer and django developer, or with a larger maxJobs.
  • A few recruiters can dominate a band. Four of our seven usable 3 to 5 year offers came from one staffing firm. Check the company count before you quote a median.
  • The salary filter is loose. Our 5-job test with salaryRange: "6to8" for python developer in Bangalore, 3 to 5 years, returned rows titled Business Analyst, Data Analyst, Data Engineer and Software Developer, and the ranges that showed pay were 4 to 6, 8 to 14 and 7 to 10 lakhs. Use the parsed numbers, not the filter.
  • Experience bands overlap. The filter returns any overlapping range, so the same job can come back in two bands. The script keeps one copy by job_id.
  • Single figures are left out. When Shine shows only a lower figure, salary_max_lakhs is missing and the script skips the row.
  • Search matching can drift. Relevance sorting returns jobs whose title or skills match. In our run, 94 of 100 titles named Python, and the rest were roles like Android Developer that list Python as a skill. Filter on title if you want the exact role only.
  • Posted ranges, not closed offers. The data shows what recruiters advertise on Shine, much of it through agencies, not what people accept.
Related Actor

Explore the scraper referenced in this article: inputs, outputs, and pricing, then run it on Apify.

Frequently asked questions

Why do so few Shine listings show pay? +

Recruiters choose whether to publish a figure. Across 1,000 rows in our tests about a third showed pay; in a Bangalore python developer search with the 3 to 5 year band it was 14 of 100. Hidden pay reads [Salary Hidden] and the number fields are left out.

Can I use the salaryRange filter to find what a role pays? +

Not for benchmarking. Shine's salary band matches loosely: our 6 to 8 lakh test for python developer returned analyst and data engineer roles with ranges such as 4 to 6 and 8 to 14 lakhs, and it does not raise the share of rows that show pay.

Does the experienceLevel filter return only that band? +

No. It returns jobs whose experience range overlaps the band. Our 3 to 5 year search included 0 to 4 year and 5 to 9 year listings, so this guide groups rows by the parsed experience_min_years instead of by the filter.

What does a salary_min_lakhs of 0.5 mean? +

Shine writes its lowest band in rupees, such as under Rs 50,000. The actor converts it to 0.5 lakhs. When Shine's text starts with a less-than sign, read 0.5 as up to Rs 50,000.

Are these the salaries people actually earn? +

No. They are the ranges recruiters post on Shine, often by staffing firms for a client. They tell you what the market is advertising for a role and band, not what offers close at.

Apify Store

Find a ready-made scraper for your job

Apify has over 80,000 scrapers and automations, ours included. Start free with $5 of platform credit every month.