The Mine Works
Compare Salary Bands in Lakhs by City and Experience on Foundit
← All posts
tutorial October 8, 2026 · 10 min read Updated October 8, 2026

Compare Salary Bands in Lakhs by City and Experience on Foundit

Build a salary table for one role across Indian cities and experience bands from Foundit.in listings that publish pay, and drop the placeholder figures.

Try the scraper

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

View the scraper →

An HR team setting pay for a new Hyderabad office, a recruiter answering “what does this role pay in Pune”, and a candidate weighing an offer all want the same table: for one role, what employers in each city say they pay, split by experience. Foundit.in, the job board formerly called Monster India, carries salary bands in lakhs on the listings that publish them. The trouble is finding those listings. Most Foundit jobs hide pay, and a few show figures that are clearly not real.

Try it live: Foundit Monster India Jobs Scraper. Pay per job delivered. Empty searches and summary rows are never charged.

The Foundit Jobs Scraper returns salary as two numbers, salary_min_lakhs and salary_max_lakhs, next to the experience range as two more. With its salary filter set, Foundit returns only listings that publish pay, which turns a sea of hidden salaries into a dataset you can actually summarize.

What you get and who it is for

FieldWhat it holds
salary_min_lakhs, salary_max_lakhsAnnual CTC band in lakhs of rupees, present only when the listing shows pay
salary_confidentialFoundit’s own flag, which does not reliably track whether figures are shown
experience_min_years, experience_max_yearsExperience range in years (0 and 0 means not stated)
locationOne or more cities as Foundit lists them
title, company, company_idThe role and the employer
posted_days_ago, total_applicantsHow fresh the listing is and how many have applied

This is for compensation and HR teams benchmarking an offer, recruiters who need a defensible number for a client, founders deciding where to open a team, and job seekers checking whether a band is fair for their years of experience.

The input

{
  "searchKeywords": ["python developer"],
  "location": "Bangalore",
  "employmentType": "Full Time",
  "salaryMinLakhs": 1,
  "salaryMaxLakhs": 100,
  "postedWithinDays": "30",
  "maxJobs": 300
}
  • salaryMinLakhs and salaryMaxLakhs are integers from 0 to 100, in lakhs per year. Setting either one tells Foundit to return only listings that carry a salary. In the actor’s own test, a 5 to 15 lakh search for sales managers returned 20 rows and all 20 had figures. We keep the range wide, 1 to 100, so the filter works as a “publishes pay” switch and does not cut off the very spread you want to measure. A role paying above 100 lakhs would fall outside it.
  • location is one city per run here. A listing can name several cities, such as “Bengaluru, Chennai, Pune”, so running one search per city and labeling rows with the city you searched is cleaner than splitting the location text afterwards. The Python script below does that.
  • postedWithinDays is a string: "1", "3", "7", "15" or "30". Thirty days gives you more listings with pay; a shorter window gives you a more current picture.
  • maxJobs caps the jobs per run, up to 1,000. The default is 5, so set it.
  • You can also split by experience at the source with experienceMinYears and experienceMaxYears (integers, 0 to 30). Foundit returns jobs whose range overlaps yours, so a 2 to 6 year search also returns a 5 to 7 year role. Grouping by experience_min_years afterwards, as the script does, keeps each job in one band.

What the output looks like

[
  {
    "job_id": "67308586",
    "title": "Python Developer",
    "company": "Astika Software Technologies Private Limited",
    "location": "Hyderabad",
    "experience_min_years": 4,
    "experience_max_years": 8,
    "salary_confidential": false,
    "salary_min_lakhs": 19,
    "salary_max_lakhs": 36.5,
    "employment_type": "Full time",
    "posted_days_ago": 0,
    "total_applicants": 16
  },
  {
    "job_id": "68443648",
    "title": "Python Automation Framework Developer",
    "company": "Hr Remedy India",
    "location": "Bengaluru",
    "experience_min_years": 5,
    "experience_max_years": 7,
    "salary_confidential": false,
    "salary_min_lakhs": 20,
    "salary_max_lakhs": 35,
    "employment_type": "Full time",
    "posted_days_ago": 6,
    "total_applicants": 143
  },
  {
    "job_id": "66558540",
    "title": "Python Developer",
    "company": "Artech Infosystems Private Limited",
    "location": "Bengaluru, Chennai, Pune",
    "experience_min_years": 5,
    "experience_max_years": 10,
    "salary_confidential": false,
    "salary_min_lakhs": 8,
    "salary_max_lakhs": 18,
    "employment_type": "Full time",
    "posted_days_ago": 0,
    "total_applicants": 55
  },
  {
    "job_id": "66571485",
    "company": "Tetra Computing LLC",
    "location": "Remote",
    "experience_min_years": 10,
    "experience_max_years": 14,
    "salary_confidential": true,
    "salary_min_lakhs": 0.5,
    "salary_max_lakhs": 0.5
  }
]

These rows are real but do not come from the salary-filtered input above, which we have not run. They come from searches without a salary filter: the Hr Remedy row from our September 30, 2026 run for "python developer" in Bangalore, full time, posted within 7 days; the other three from our daily all-India "python developer" check runs on September 12 and 16, 2026. They are trimmed to the fields that matter here.

The last row shows why the clean-up step exists. Half a lakh a year for a role asking 10 to 14 years of experience is a placeholder, not a salary. Across all our runs of this actor (179 distinct jobs), 23 jobs carried salary figures and 7 of those were flagged salary_confidential: true. All 7 had figures like this one: 0.5 to 0.5, 1 to 1, 1 to 2 lakhs for 8 to 13 years, or 2 to 3 lakhs for 9 to 17 years. The script drops every row flagged confidential and anything with a top figure under 1 lakh.

The same runs show the other reason a filter matters. Our Bangalore run of September 30 returned 49 jobs, and only 2 had figures, one of them a placeholder. One usable salary out of 49 jobs is not a benchmark.

Run it in Apify Console

  1. Open https://apify.com/themineworks/foundit-jobs-scraper and click Try for free.
  2. In Search keywords, enter the role, one keyword per line. Use the title as employers write it, such as python developer or sales manager.
  3. Set Location to one city.
  4. Set Minimum salary to 1 and Maximum salary to 100.
  5. Set Posted within to Last 30 days and Max jobs to 300, then click Start.
  6. Repeat for each city, or save it as a task and change only the Location between runs.
  7. Export each run as CSV or Excel, filter out rows where salary_confidential is true, and pivot on experience_min_years.

Run it from Python

pip install apify-client
import csv
from collections import defaultdict
from statistics import median
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
ROLE = "python developer"
CITIES = ["Bangalore", "Hyderabad", "Pune"]

def band(job):
    lo, hi = job["experience_min_years"], job["experience_max_years"]
    if lo == 0 and hi == 0:
        return "not stated"
    if lo <= 2:
        return "0 to 2 yrs"
    if lo <= 5:
        return "3 to 5 yrs"
    if lo <= 9:
        return "6 to 9 yrs"
    return "10+ yrs"

rows, seen = [], set()
for city in CITIES:
    run = client.actor("themineworks/foundit-jobs-scraper").call(run_input={
        "searchKeywords": [ROLE],
        "location": city,
        "employmentType": "Full Time",
        "salaryMinLakhs": 1,
        "salaryMaxLakhs": 100,
        "postedWithinDays": "30",
        "maxJobs": 300,
    })
    for job in client.dataset(run["defaultDatasetId"]).iterate_items():
        if "_type" in job or "salary_min_lakhs" not in job:
            continue  # summary rows and jobs without pay
        if job.get("salary_confidential"):
            continue  # figures on these looked like placeholders in our runs
        lo, hi = job["salary_min_lakhs"], job["salary_max_lakhs"]
        if hi < lo or hi < 1:
            continue
        if (city, job["job_id"]) in seen:
            continue
        seen.add((city, job["job_id"]))
        rows.append({"city": city, "band": band(job), "company": job["company"],
                     "title": job["title"], "min_lakhs": lo, "max_lakhs": hi,
                     "mid_lakhs": (lo + hi) / 2, "url": job["apply_url"]})

groups = defaultdict(list)
for r in rows:
    groups[(r["city"], r["band"])].append(r)

summary = []
for (city, b), g in sorted(groups.items()):
    summary.append({
        "city": city, "experience": b, "listings": len(g),
        "companies": len({r["company"] for r in g}),
        "median_min_lakhs": median(r["min_lakhs"] for r in g),
        "median_max_lakhs": median(r["max_lakhs"] for r in g),
        "median_mid_lakhs": median(r["mid_lakhs"] for r in g),
    })

for name, data in (("salary_listings.csv", rows), ("salary_by_city_band.csv", summary)):
    if data:
        with open(name, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=list(data[0].keys()))
            writer.writeheader()
            writer.writerows(data)

for s in summary:
    flag = "" if s["companies"] >= 5 else "  (thin)"
    print(f"{s['city']:<10} {s['experience']:<11} {s['listings']:>3} jobs  "
          f"median {s['median_min_lakhs']} to {s['median_max_lakhs']} lakhs{flag}")

You get two files. salary_listings.csv keeps every usable listing with its city, band and link, so you can check any number against its source. salary_by_city_band.csv holds one row per city and experience band, with the median low end, high end and midpoint of the published bands, plus how many listings and how many distinct companies sit behind each median. Cells backed by fewer than five companies are marked thin in the printout.

Two choices in the script are worth knowing. It counts companies as well as listings because one employer can post the same role many times: across all our runs, 8 of the 16 usable salary rows were internship listings from a single company, Maxgen Technologies, each covering a different set of Maharashtra and Gujarat towns. And it keeps rows where experience is “not stated” in their own band rather than guessing.

Refresh it every month

Pay bands move slowly, so a monthly refresh is enough. Save one task per city in Apify Console, open Schedules, create a schedule with a cron expression such as 0 6 1 * * (06:00 on the first of each month) and add all the tasks to it. With postedWithinDays at "30", each month’s runs cover the month just ended.

Send each run to a Google Sheet with Apify’s Google Sheets integration and keep a tab per month, or let a webhook trigger the script above when the last run finishes. Comparing this month’s medians with last month’s shows you which city is moving, as long as you read the company counts next to them.

What it costs

You pay per job saved to your dataset: $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 separate compute charge.

The script runs three cities with a cap of 300 jobs each, so at most 900 jobs: $1.80 on Bronze, $1.58 on Silver or $1.35 on Gold. A salary-filtered search usually returns fewer jobs than the cap, since only listings with pay qualify, and you pay only for jobs delivered. Five cities refreshed monthly at the full cap is 1,500 jobs, $3.00 a month on Bronze.

Compare that with searching without the filter. At the rate in the actor’s 1,000-row test, 9 jobs with pay per 1,000, you would pay for about 110 jobs per usable salary. The filter is what makes this affordable.

Limits worth knowing

  • Published pay is a minority, and not a random one. Only employers who choose to show salary appear in this dataset. Across all our runs, none of the 43 Infosys and Tata Consultancy Services listings showed pay, so the table describes employers who publish, not the whole market. Running the same roles through Naukri’s job data in Python widens the sample where Foundit is thin.
  • Watch for placeholder figures. Drop rows flagged salary_confidential and anything under 1 lakh, as the script does, and glance at the listing file for bands that do not fit the experience asked.
  • One employer can dominate a cell. Staffing firms and training companies repost the same role across many towns. Read the company count before the median.
  • Multi-city listings. A job listed for “Bengaluru, Chennai, Pune” can turn up in more than one city’s search. The script keeps it once per city searched, which is right for a per-city table but means the same job can count toward three cities.
  • Experience 0 to 0 means not stated. In a 1,000-row test, 660 rows had 0 for both experience fields. Those land in the “not stated” band.
  • Up to 1,000 jobs per run. Split a large search by city or experience band rather than raising one cap.
Related Actor

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

Frequently asked questions

Why do most Foundit jobs have no salary? +

Most employers on Foundit do not publish pay. In a 1,000-row all-India test without a salary filter, 9 rows had figures. Setting salaryMinLakhs or salaryMaxLakhs makes Foundit return only listings that show a salary.

What unit are the salary figures in? +

Lakhs of rupees per year, as annual CTC. A row with salary_min_lakhs 8 and salary_max_lakhs 18 means 8 to 18 lakhs a year.

Can I trust salary_confidential? +

Not as a has-salary flag. Check whether salary_min_lakhs is present instead. In our own runs, every row flagged confidential that still carried figures had ones that looked like placeholders, such as 0.5 to 0.5 lakhs for 10 to 14 years of experience, so the guide drops them.

How many listings do I need for a fair comparison? +

There is no fixed number, but a median from two or three listings tells you about those employers, not the market. The script reports the listing and company count next to every median so you can grey out thin cells.

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.