Foundit Scraper [$2.50/1K💰] | India Jobs & Salary Data
Pricing
from $2.30 / 1,000 results
Foundit Scraper [$2.50/1K💰] | India Jobs & Salary Data
Build an India job database from Foundit (formerly Monster India) - title, company, city, INR salary, experience band, skills, taxonomy, full description and apply URL. Two-stage run, no browser.
Pricing
from $2.30 / 1,000 results
Rating
0.0
(0)
Developer
Ahmed Jasarevic
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
13 days ago
Last modified
Categories
Share
Foundit Scraper
Build a structured India job database from Foundit (formerly Monster India) — job title, company, city, INR salary, experience band, skills, full taxonomy, complete description and direct apply URL. Built for recruiters, job-board aggregators, HR analytics, salary benchmarking and labor-market research.
Two-stage run: a fast search API fills the dataset first, then full descriptions are backfilled — so your dataset is usable before the slow phase even starts.
Read this before you run it. Foundit sits behind Akamai bot protection that rejects datacenter IPs, and this Actor works around that block by design. That has consequences for you. See Legal & Compliance — it is not a formality.
What you get
INR salary, normalised to a monthly figure
salaryMin, salaryMax, salaryCurrency, salaryPerMonthMin, salaryPerMonthMax, salaryText and salaryHidden. Phase 2 reads Foundit's baseSalary from the page's schema.org JobPosting JSON-LD, which states the unit explicitly (MONTH or YEAR) — so annual postings get converted rather than silently compared against monthly ones. That is the difference between a salary dataset you can actually benchmark and one where every annual row looks 12× too high.
salaryHidden is a first-class flag, not a gap: it is true when the employer hid the figure or when no salary was published at all. Filter on it rather than treating null salary as zero.
Experience bands that don't lie
experienceMinYears, experienceMaxYears, experienceText. Foundit's own middleware API returns an empty result set for some experience filters (experience=3-5 is a known case), which would silently give you zero jobs rather than an error. This Actor filters the experience band client-side after parsing, so a restrictive filter returns correctly filtered jobs instead of nothing.
The full taxonomy, already split into arrays
skills, skillsWithSynonyms, designations, functions, industries, employerTypes, jobType, employmentTypes, workplaceType. Every one of these is a list field, pre-split — not the pipe-delimited string Foundit returns. That means you can build a skills taxonomy or a remote-work filter without regex cleanup.
Application intelligence
applyUrl, jobUrl, companyUrl, externalApply, recruiterName, postedBy, totalApplicants, urgentlyHiring. totalApplicants is the competitive-demand signal: a role with 400 applicants in three days is a market-intelligence fact about that employer, not noise about the job. urgentlyHiring and postedBy help you separate direct employer postings from agency reposts.
Freshness you can filter on
postedDate, lastUpdatedDate, postedRelative, validThrough, enrichedAt, scrapedAt. postedRelative ("3 days ago") comes straight from the API for human-readable recency; validThrough is the closing date; enrichedAt separates phase-2 enrichment time from phase-1 collection time.
How it works
Foundit's own front end calls an unauthenticated JSON middleware endpoint. The Actor calls exactly that same endpoint — this is first-party app traffic, not HTML scraping:
Phase 1 — search
GET https://www.foundit.in/middleware/jobsearch?keyword=…&locations=…&page=…
Returns the job objects directly. Merged across every keyword × location combination and deduplicated by Foundit job ID. Each result is pushed to the dataset immediately with descriptionStatus: "pending".
Phase 2 — descriptions
GET <mirror>/job/<slug>-<jobId>?amp=1
Foundit's middleware payload carries summary data but not the full description, posting/expiry dates or base salary. Those only exist in a schema.org JobPosting JSON-LD block on the detail page. Phase 2 fetches those pages, extracts the JSON-LD, and re-pushes the same jobId with the description merged in and descriptionStatus: "complete" — so you get an updated record, not a duplicate.
Why phase 2 uses regional mirrors — and why that matters
www.foundit.in is fronted by Akamai Bot Manager, which applies aggressive bot protection and 403s most datacenter IP ranges. The Actor therefore fetches detail pages from Foundit's own regional sibling domains, which serve identical job slugs with far weaker edge protection:
https://www.founditgulf.com → https://www.foundit.sg → https://www.foundit.in
They are tried in order, least-protected first, with www.foundit.in only as the last-resort fallback. On a blocked or missing page the Actor hops to the next mirror instead of burning retries on a dead host, and per-job attempt counts stop a dead slug from ever looping through the chain.
The same IP block is why the Actor explicitly warns you at startup when you run without a proxy, recommending the Apify RESIDENTIAL group. Running on datacenter IPs without one is the single most common reason for empty results.
Cost control built in
maxConcurrency(default 5) for phase 1, deliberately low to stay under Foundit's rate limits.detailConcurrency(default 15) for phase 2 — these are static AMP pages on the least-protected mirrors, so they tolerate and benefit from much higher parallelism.- Phase 2 is capped independently by
maxDescriptionItems(default 200), so a large search run doesn't inherit a proportionally large description bill. - A request budget of
maxItems × 3 mirrors + 10bounds the whole run.
Input
| Field | Type | Default | Notes |
|---|---|---|---|
keywords | string[] | ["software engineer"] | Job titles, roles or skills. Each is searched separately, then merged and deduplicated. |
locations | string[] | [] | City or region filters — Bengaluru, Mumbai, Hyderabad, Pune, Delhi NCR. Empty searches all of India. |
experienceMin | integer | 0 | Minimum years of experience. 0 = no minimum. |
experienceMax | integer | 0 | Maximum years. 0 = no maximum. |
maxItems | integer | 50 | Cap on job records in the dataset. |
maxPages | integer | 5 | Safety limit on result pages per keyword/location combination. |
includeDescriptions | boolean | true | Enable phase 2. false gives you the cheapest possible run. |
maxDescriptionItems | integer | 200 | How many collected jobs get a full description in phase 2. 0 = no descriptions. |
maxConcurrency | integer | 5 | Parallel phase-1 requests. Keep at 3–8. |
detailConcurrency | integer | 15 | Parallel phase-2 requests. Safe higher than phase 1. |
proxy | object | { "useApifyProxy": false } | Set this to RESIDENTIAL. Datacenter IPs get 403. |
maxDescriptionItems defaults to 200 while maxItems defaults to 50, so with default input every collected job gets a description. Lower maxDescriptionItems if you're collecting a large volume and don't need all of them described.
Output
One flat dataset of 44 fields per job. Records appear twice by design — once from phase 1 with descriptionStatus: "pending", once from phase 2 with descriptionStatus: "complete" and the description merged. Deduplicate on jobId, keeping the record with descriptionStatus: "complete".
| Group | Fields |
|---|---|
| Identity | jobId, title, company, jobUrl, companyUrl, applyUrl, externalApply |
| Location | location, country |
| Compensation | salaryMin, salaryMax, salaryCurrency, salaryPerMonthMin, salaryPerMonthMax, salaryText, salaryHidden |
| Experience | experienceMinYears, experienceMaxYears, experienceText |
| Taxonomy | skills, skillsWithSynonyms, designations, functions, industries, employerTypes, jobType, employmentTypes, workplaceType |
| Dates | postedDate, lastUpdatedDate, postedRelative, validThrough |
| Description | description, descriptionHtml, descriptionStatus, enrichedAt |
| Employer signal | recruiterName, postedBy, totalApplicants, urgentlyHiring |
| Provenance | source, searchQuery, searchLocation, scrapedAt |
Field notes that change how you read the data:
descriptionStatusispendingafter phase 1,completeafter phase 2. A job may remainpendingforever if it wasn't inmaxDescriptionItemsor no mirror served it — filter oncompleteif you need descriptions.salaryPerMonthMin/salaryPerMonthMaxare populated only when Foundit states aMONTHunit. ForYEARpostings you get the amount insalaryTextand must convert it yourself. ChecksalaryTextbefore assuming the monthly fields exist.postedDateandlastUpdatedDatefrom JSON-LD are in DD-MM-YYYY, not ISO. Reformat before loading to a database.recruiterNamecan be a real person's name — see Legal & Compliance before storing it.searchQueryandsearchLocationrecord which input combination found the job. The samejobIdcan legitimately appear under several; that's your provenance trail.skillsWithSynonymsis broader thanskills— useskillsfor strict matching andskillsWithSynonymsfor recall.descriptionHtmlis the raw HTML from the JSON-LD block, not page chrome.
Example input
Standard recruiter run:
{"keywords": ["software engineer", "backend developer"],"locations": ["Bengaluru"],"maxItems": 100}
Salary research across Indian tech hubs, mid-level only:
{"keywords": ["data scientist", "machine learning engineer"],"locations": ["Bengaluru", "Hyderabad", "Pune"],"experienceMin": 3,"experienceMax": 8,"includeDescriptions": false,"maxItems": 500}
Cheapest possible run — no detail phase at all:
{"keywords": ["accountant"],"locations": ["Mumbai"],"maxItems": 200,"includeDescriptions": false}
Full-description run capped for cost:
{"keywords": ["product manager"],"locations": ["Delhi NCR"],"maxItems": 300,"includeDescriptions": true,"maxDescriptionItems": 150}
Nationwide sweep through a residential proxy:
{"keywords": ["nurse", "teacher"],"locations": [],"maxItems": 1000,"maxPages": 10,"proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }}
Example output
{"jobId": "INV-3xj8k2mq9p4r","title": "Senior Backend Engineer (Go)","company": "Razorpay Software Pvt Ltd","location": "Bengaluru, Karnataka","country": "India","salaryMin": 1800000,"salaryMax": 2600000,"salaryCurrency": "INR","salaryPerMonthMin": 150000,"salaryPerMonthMax": 216666,"salaryText": "1,800,000 INR per year","salaryHidden": false,"experienceMinYears": 4,"experienceMaxYears": 7,"experienceText": "4-7 years","skills": ["Go", "Kubernetes", "PostgreSQL", "gRPC"],"skillsWithSynonyms": ["Go", "Golang", "Kubernetes", "K8s", "PostgreSQL", "Postgres", "gRPC"],"designations": ["Senior Software Engineer", "Backend Developer"],"functions": ["Engineering", "Information Technology"],"industries": ["Banking", "Financial Services", "Fintech"],"employerTypes": ["Full-time", "Regular"],"jobType": "fulltime","employmentTypes": ["Full-time"],"workplaceType": "Hybrid","postedDate": "2026-09-14","lastUpdatedDate": "2026-10-01","postedRelative": "20 days ago","validThrough": "2026-11-14","description": "We are looking for a Senior Backend Engineer to build payment infrastructure serving millions of transactions per day. You will design distributed services in Go, own service reliability, and mentor two mid-level engineers. Our stack is Go, Kubernetes and PostgreSQL.","descriptionHtml": "<p>We are looking for a Senior Backend Engineer to build payment infrastructure…</p>","descriptionStatus": "complete","enrichedAt": "2026-10-04T06:14:02.118Z","applyUrl": "https://www.foundit.in/jobs/senior-backend-engineer-go-inv-3xj8k2mq9p4r","jobUrl": "https://www.foundit.in/jobs/senior-backend-engineer-go-inv-3xj8k2mq9p4r","companyUrl": "https://www.foundit.in/company/razorpay-software-pvt-ltd","externalApply": false,"recruiterName": "Priya Menon","postedBy": "Razorpay Software Pvt Ltd","totalApplicants": 214,"urgentlyHiring": true,"source": "foundit.in","searchQuery": "software engineer","searchLocation": "Bengaluru","scrapedAt": "2026-10-04T06:12:41.220Z"}
Integrations & automation
Call it over the Apify API (POST /v2/acts/ouNFgKxKFYizng7Rh/runs), or from Python, JavaScript, the CLI or MCP. Output exports as JSON, CSV, Excel, XML or RSS.
For recruiter pipelines, run nightly on a keyword × city grid, dedupe on jobId, and alert on postedRelative of "today" or urgentlyHiring. For salary benchmarking, use includeDescriptions: false and rely on salaryMin/salaryMax/salaryText — you get pay bands for a fraction of the cost, and the phase-2 dates aren't needed for aggregation. For employer intelligence, group by company and compare totalApplicants growth against posting cadence.
Typical pipeline: run on schedule → dedupe on jobId keeping descriptionStatus: "complete" → normalise salaryPerMonthMin/salaryMax (converting YEAR yourself) → reformat DD-MM-YYYY dates → explode skills into a bridge table → upsert into your ATS or warehouse.
What a run costs
| Configuration | Results | Cost |
|---|---|---|
50 jobs, includeDescriptions: false | 50 × result | $0.13 |
| 100 jobs, descriptions on | 100 × result | $0.25 |
500 jobs, includeDescriptions: false | 500 × result | $1.25 |
$0.0025 per result. Phase 2 re-pushes the same job with a description merged in, and those re-pushes count as results — which is why a described run costs roughly double an undescribed one of the same size. To describe fewer jobs cheaply, lower maxDescriptionItems rather than maxItems. Free accounts get a trial quota; paying accounts get full maxItems and tiered rates.
Related Actors
| Actor | Why |
|---|---|
naukri-scraper | India's largest job board — the essential cross-check for salary and volume benchmarking against Foundit. |
linkedin-jobs-scraper | Corporate-side hiring signal, complementing a job-board-side dataset. |
indeed-scraper | Global job data for international hiring-market comparison. |
glassdoor-scraper | Company reviews and salary reports to pair with posting-level pay data. |
ambitionbox-scraper | Indian salary and career-intelligence data, useful for enriching Foundit records. |
FAQ
I ran it without a proxy and got nothing. Why?
Because www.foundit.in is behind Akamai Bot Manager, which 403s most datacenter IP ranges — which is exactly where Apify runs without a proxy. Set proxy to { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] } and re-run. The Actor warns you explicitly at startup when it detects it's running without a proxy, because this is by far the most common cause of an empty run.
Why do some jobs appear twice?
By design. Phase 1 pushes every job with descriptionStatus: "pending"; phase 2 re-pushes the same jobId with the description, dates and salary merged in and descriptionStatus: "complete". Deduplicate on jobId and keep the complete record. A job that never reached phase 2 stays pending permanently — filter on descriptionStatus == "complete" when you need descriptions.
Why did an experience filter return zero jobs when the jobs exist?
Because Foundit's middleware API returns an empty payload for some experience bands — experience=3-5 is a known case — rather than an error. This Actor filters the experience band client-side after parsing, so you get correctly filtered results instead of an empty set. If you still get nothing, widen experienceMax or clear locations.
salaryPerMonthMin is null but salaryText shows a number. Why?
The monthly fields are populated only when Foundit states a MONTH unit. For a YEAR posting, the amount lands in salaryText and the monthly fields stay null. Always read salaryText before assuming the monthly fields exist, and divide YEAR figures by 12 yourself if you need a monthly comparison.
salaryMin is a string. Why?
Foundit's middleware returns salary as text (for example "1,800,000"), so it stays a string in salaryMin/salaryMax unless phase 2 replaced it from JSON-LD. Cast to number and strip commas before any arithmetic; salaryPerMonthMin/salaryPerMonthMax are already parsed numbers.
Why are the dates in DD-MM-YYYY?
Because they come straight out of Foundit's schema.org JSON-LD, which uses DD-MM-YYYY. Reformat before loading to anything date-typed, or your recency filters will silently misbehave.
Is there a browser or captcha solving involved?
No browser, and no captcha solving. It's plain HTTP GETs against Foundit's own JSON middleware API and its AMP detail pages. The only obstacle is IP reputation — which is a proxy concern, not a rendering one.
Can I get descriptions without paying for the whole run?
Yes, that's what maxDescriptionItems is for. It caps phase 2 independently of maxItems, so you can collect 500 jobs and describe only the 150 that matter. Leave includeDescriptions: false entirely for the cheapest possible run.
SEO Keywords
foundit scraper, monster india jobs, india job scraper, job board data, india salary data, inr salary database, job market research india, recruiter data, hiring trends india, it job listings, remote work india jobs, skills taxonomy jobs, job posting api, tech jobs india, job aggregation, employment data india, salary benchmarking, job market analysis, talent acquisition data, hiring intelligence, india tech hiring, job description scraping, career data pipeline, workforce analytics
For AI Agents & LLM Apps
Returns Foundit (Monster India) job listings as one flat dataset, 44 fields per record.
Minimal input — smallest input that produces a valid run:
{ "keywords": ["software engineer"] }
Cheapest possible run (no detail phase):
{ "keywords": ["accountant"], "locations": ["Mumbai"], "includeDescriptions": false, "maxItems": 200 }
Salary research, mid-level only:
{ "keywords": ["data scientist"], "locations": ["Bengaluru", "Hyderabad"], "experienceMin": 3, "experienceMax": 8, "includeDescriptions": false, "maxItems": 500 }
Full descriptions, cost-capped:
{ "keywords": ["product manager"], "locations": ["Delhi NCR"], "maxItems": 300, "maxDescriptionItems": 150 }
Output fields (44): jobId, title, company, location, country, salaryMin, salaryMax, salaryCurrency, salaryPerMonthMin, salaryPerMonthMax, salaryText, salaryHidden, experienceMinYears, experienceMaxYears, experienceText, skills, skillsWithSynonyms, designations, functions, industries, employerTypes, jobType, employmentTypes, workplaceType, postedDate, lastUpdatedDate, postedRelative, validThrough, description, descriptionHtml, descriptionStatus, enrichedAt, applyUrl, jobUrl, companyUrl, externalApply, recruiterName, postedBy, totalApplicants, urgentlyHiring, source, searchQuery, searchLocation, scrapedAt
Behaviours an agent should know
- You must set a residential proxy or the run returns nothing.
www.foundit.in403s datacenter IPs. Useproxy: { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }. This is the most common failure mode. - Records appear twice. Phase 1 pushes with
descriptionStatus: "pending", phase 2 re-pushes the samejobIdwithdescriptionStatus: "complete". Dedupe onjobId, keepingcomplete. Jobs outsidemaxDescriptionItemsstaypendingpermanently. - Phase 2 re-pushes are billed too ($0.0025 each). A described run costs roughly double an undescribed run of the same size. To describe fewer, lower
maxDescriptionItems, notmaxItems. maxDescriptionItemsdefaults to 200 whilemaxItemsdefaults to 50, so default input describes everything. Set both explicitly if you want partial description.salaryMin/salaryMaxmay be strings with commas from the API;salaryPerMonthMin/salaryPerMonthMaxare parsed numbers and are populated only for MONTH-unit postings. ReadsalaryTextand convert YEAR figures yourself.salaryHidden: truemeans the employer hid pay or nothing was published. Filter on it — don't read a null salary as zero.- Dates from JSON-LD are DD-MM-YYYY, not ISO. Reformat before date comparisons.
- Experience filtering happens client-side because Foundit's API returns empty results for some bands (e.g.
3-5). Widen filters rather than assuming no jobs exist. - Detail pages are fetched from regional Foundit mirror domains in order (
founditgulf.com→foundit.sg→foundit.in), least-protected first, because the main domain blocks datacenter IPs. - Concurrency is split:
maxConcurrency(default 5) for search,detailConcurrency(default 15) for descriptions. Raise the latter freely, keep the former at 3–8. recruiterNamecan be a real person's name — treat it as personal data.- Keywords and locations combine multiplicatively; results dedupe by
jobId, andsearchQuery/searchLocationshow which combination matched. - No browser, no captcha solving — plain HTTP only.
Legal & Compliance
This Actor is an independent third-party tool. It is not affiliated with, endorsed by, or sponsored by Foundit (Monster India), or by any employer whose listings appear in its output.
Be aware of what this Actor does, because it is not neutral. www.foundit.in is fronted by Akamai Bot Manager, which rejects most datacenter IP ranges with HTTP 403. This Actor deliberately works around that protection: detail requests are routed across Foundit's own regional sibling domains (founditgulf.com, foundit.sg), chosen specifically because they serve identical content with weaker edge protection, and the run is expected to use residential proxies. That is circumvention of a technical access control, and it means:
- You should only run this against Foundit if you have a genuine basis to access its job data — your own listings, an agreement with the company, or another lawful basis.
- If your use is automated, high-volume or repeated, ask Foundit for written permission first. That is the correct route, and it is far less work than defending a circumvention-based collection.
- Review Foundit's Terms of Use, which restrict automated access, and its
robots.txt— which could not be verified because the request was itself blocked.
Data subject rights. This output contains personal data: recruiterName is a named individual's name, and applyUrl/jobUrl identify specific vacancies. Under India's Digital Personal Data Protection Act (DPDP Act, 2023) and the broader GDPR regime, job-seeker and recruiter data attracts specific obligations. You are the data fiduciary in that relationship, not this tool.
If you do process this data lawfully, you must still: give the applicable notice to the people concerned, honour erasure and access requests, and keep the data only as long as a stated purpose requires. Do not use recruiterName for unsolicited outreach that the individual has not consented to or has objected to. Job descriptions belong to the employers who wrote them — do not republish them wholesale without permission.
Terms of the platform. Foundit's Terms of Use reserve rights over the job database, the taxonomy, and the company/recruiter profile data. Building a competing job board, or a scraped-aggregator product, from this data is very likely to breach them.
You are solely responsible for compliance with Foundit's Terms of Use, applicable data-protection law, and any permission you obtain. If you need India job data at scale on a defensible footing, a commercial data licence from Foundit or a partner aggregator is the durable answer.