Building an Automated Naukri Job Alert System with Python
How to build a custom Naukri job monitoring system that filters by salary, location, and skills, and sends instant alerts when relevant jobs post.
The actor referenced in this article. Pay only for results delivered.
Naukri’s built-in job alerts work but are limited: you cannot filter by minimum salary threshold, combine multiple skill requirements with AND logic, or receive alerts only for companies above a certain funding stage or size. Building a custom alert system gives you full control over the filtering logic.
Try it live: Naukri Jobs Scraper, 21 Fields, 7 Filters, New-Job Monitor. Pay only for results delivered, no result no charge.
TL;DR: Build a custom Naukri alert system beyond the platform’s built-in filters: fetch jobs via the Apify Naukri Scraper, apply multi-dimensional filters (minimum salary LPA, experience range, required skills with AND logic, work mode), score matches by preferred companies and skills, and send a formatted daily email digest. Typical output: 15-40 curated matches per day for a narrow profile instead of hours of manual browsing.
This guide shows how to build a Naukri job monitoring system that runs on a schedule, applies custom filters, and delivers a curated daily digest.
What the System Does
- Pulls the latest job postings from Naukri for your target keywords
- Applies multi-dimensional filters (salary range, experience, location, company size)
- Scores and ranks postings by match quality
- Sends a formatted digest email with the top matches
Step 1: Data Collection
from apify_client import ApifyClient
from datetime import datetime
client = ApifyClient('YOUR_API_TOKEN')
def fetch_naukri_jobs(keywords: list[str], location: str = None, max_jobs: int = 100) -> list[dict]:
"""Fetch recent Naukri jobs for multiple keyword searches."""
all_jobs = []
for keyword in keywords:
run = client.actor('themineworks/naukri-jobs').call(run_input={
'searchKeywords': keyword,
'location': location,
'maxJobs': max_jobs,
'includeJobDescription': True,
'sortBy': 'date', # Most recent first
})
jobs = list(client.dataset(run['defaultDatasetId']).iterate_items())
for job in jobs:
job['_search_keyword'] = keyword
all_jobs.extend(jobs)
# Deduplicate by job ID
seen = set()
deduped = []
for job in all_jobs:
if job['jobId'] not in seen:
seen.add(job['jobId'])
deduped.append(job)
return deduped
Step 2: Custom Filtering
def parse_salary_range(salary_str: str) -> tuple[int, int] | None:
"""Parse '₹15-25 Lacs PA' → (1500000, 2500000)"""
if not salary_str:
return None
import re
# Match patterns like "15-25 Lacs", "15L-25L", "15 - 25 LPA"
match = re.search(r'(\d+(?:\.\d+)?)\s*[-–]\s*(\d+(?:\.\d+)?)\s*(?:L|Lac|LPA|lakh)', salary_str, re.IGNORECASE)
if match:
min_l = float(match.group(1))
max_l = float(match.group(2))
return (int(min_l * 100000), int(max_l * 100000))
return None
def filter_jobs(jobs: list[dict], criteria: dict) -> list[dict]:
"""Apply filtering criteria to job list."""
filtered = []
for job in jobs:
# Salary filter
if criteria.get('min_salary_lpa'):
salary = parse_salary_range(job.get('salaryRange', ''))
if salary:
max_salary_lpa = salary[1] / 100000
if max_salary_lpa < criteria['min_salary_lpa']:
continue
# Experience filter
exp = job.get('experience', {})
if criteria.get('max_experience_years') and exp.get('max'):
if exp['max'] > criteria['max_experience_years']:
continue
if criteria.get('min_experience_years') and exp.get('min'):
if exp['min'] < criteria['min_experience_years']:
continue
# Required skills filter (AND logic — all must be present)
if criteria.get('required_skills'):
desc = (job.get('description', '') + ' ' + job.get('skills', '')).lower()
if not all(skill.lower() in desc for skill in criteria['required_skills']):
continue
# Excluded keywords
if criteria.get('excluded_keywords'):
title = job.get('title', '').lower()
if any(kw.lower() in title for kw in criteria['excluded_keywords']):
continue
# Work mode filter
if criteria.get('work_modes'):
job_mode = job.get('workMode', '').lower()
if not any(mode.lower() in job_mode for mode in criteria['work_modes']):
continue
filtered.append(job)
return filtered
Step 3: Match Scoring
Rank filtered jobs by how well they match your ideal profile:
def score_job(job: dict, preferences: dict) -> float:
"""Score a job 0.0-1.0 based on match to preferences."""
score = 0.5 # Baseline
desc = (job.get('description', '') + ' ' + job.get('skills', '')).lower()
# Bonus for preferred skills
for skill in preferences.get('preferred_skills', []):
if skill.lower() in desc:
score += 0.05
# Bonus for preferred companies
company = job.get('company', '').lower()
for pref_company in preferences.get('preferred_companies', []):
if pref_company.lower() in company:
score += 0.15
# Bonus for salary disclosure
if job.get('salaryRange'):
score += 0.05
# Bonus for application recency
posted_days = job.get('daysAgo', 30)
if posted_days <= 1:
score += 0.1
elif posted_days <= 3:
score += 0.05
return min(1.0, score)
Step 4: Digest Email
def format_digest_email(jobs: list[dict], date: str) -> str:
if not jobs:
return f"No new matching jobs found on {date}."
lines = [f"📊 Naukri Job Digest — {date}", f"Found {len(jobs)} matching positions\n", "─" * 50]
for i, job in enumerate(jobs[:15], 1):
salary = job.get('salaryRange', 'Not disclosed')
location = job.get('location', 'Not specified')
posted = f"{job.get('daysAgo', '?')} day(s) ago"
score = job.get('_match_score', 0)
lines.extend([
f"\n{i}. {job['title']}",
f" Company: {job.get('company', 'Unknown')}",
f" Salary: {salary}",
f" Location: {location}",
f" Posted: {posted}",
f" Match: {'⭐' * round(score * 5)}",
f" URL: {job.get('url', '')}",
])
return '\n'.join(lines)
# Send email (using any SMTP provider)
import smtplib
from email.mime.text import MIMEText
def send_digest(body: str, recipient: str):
msg = MIMEText(body)
msg['Subject'] = f"Naukri Digest — {datetime.now().strftime('%d %b %Y')}"
msg['From'] = 'alerts@yourdomain.com'
msg['To'] = recipient
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls()
smtp.login('your_email', 'your_app_password')
smtp.send_message(msg)
Step 5: Putting It Together
import schedule
import time
MY_CRITERIA = {
'min_salary_lpa': 20,
'min_experience_years': 3,
'max_experience_years': 7,
'required_skills': ['python'],
'excluded_keywords': ['intern', 'trainee', 'fresher'],
'work_modes': ['hybrid', 'remote', 'wfh'],
}
MY_PREFERENCES = {
'preferred_skills': ['fastapi', 'aws', 'kubernetes', 'llm', 'langchain'],
'preferred_companies': ['google', 'microsoft', 'amazon', 'flipkart', 'razorpay'],
}
def daily_job_check():
keywords = ['senior python developer', 'python backend engineer', 'python ml engineer']
jobs = fetch_naukri_jobs(keywords, location='Bangalore', max_jobs=200)
filtered = filter_jobs(jobs, MY_CRITERIA)
for job in filtered:
job['_match_score'] = score_job(job, MY_PREFERENCES)
ranked = sorted(filtered, key=lambda j: j['_match_score'], reverse=True)
digest = format_digest_email(ranked, datetime.now().strftime('%d %b %Y'))
send_digest(digest, 'your-email@example.com')
print(f"Daily check: {len(jobs)} total → {len(filtered)} matches → digest sent")
schedule.every().day.at('07:30').do(daily_job_check)
while True:
schedule.run_pending()
time.sleep(60)
The system typically finds 15-40 matching jobs per day for a narrowly specified profile in a major city. Compared to manual Naukri browsing, this takes 30 seconds to review instead of 30 minutes.
Frequently Asked Questions
What custom filters can you apply to Naukri job alerts that the built-in system does not support?
Naukri’s built-in alerts filter by keyword, location, experience, and industry, but lack: minimum salary thresholds (you can set salary range but not a hard minimum), AND-logic for multiple required skills (built-in uses OR), specific company inclusion/exclusion lists, work-mode filtering (remote/hybrid/office), and match-quality scoring. A custom Python system using the Apify Naukri scraper can apply all of these simultaneously, plus rank results by weighted criteria.
How do you parse salary ranges from Naukri job postings in Python?
Naukri salary data comes as strings like “8-12 Lacs PA” or “Not Disclosed”. Use a regex pattern to extract min/max: re.search(r'(\d+\.?\d*)\s*-\s*(\d+\.?\d*)\s*Lacs?', salary_str). Convert to a standard unit (LPA as float). Handle edge cases: “15+ Lacs” (set min=15, max=None), “Not Disclosed” (set both to None), and “20,000-30,000 per month” (convert to annual LPA). Filter by salary_min >= your_threshold and skip postings where salary is None if you have strict requirements.
How does AND-logic skill filtering work for Naukri job matching?
Naukri’s search returns jobs matching any of your keywords (OR logic). To apply AND logic, fetch a broader result set and filter client-side: download all postings for your primary keyword, then check each job’s description and title for the presence of all required skills. For example, to find “Python AND Kubernetes AND AWS”, fetch Python jobs, then filter to those containing both “kubernetes” or “k8s” and “aws” or “amazon web services” in the combined title+description text. Case-insensitive regex matching handles variations.
How do you score and rank job postings by match quality?
Assign point weights to criteria: preferred company match (+3), primary skill in title (+2), secondary skill in description (+1), salary above threshold (+2), work mode preference match (+1). Sum the points for each job and sort descending. A job at a preferred company mentioning your primary skill in the title with salary above your floor scores highest. This ranking surfaces your best 5-10 matches at the top rather than requiring you to scan all 40 daily results.
How much does it cost to run an automated Naukri job monitoring system?
The Apify Naukri Jobs scraper under PPE billing costs $0.0014 per job listing. Running daily alerts for 1-2 keyword profiles generating 50-100 results costs roughly $0.07-0.14/day, under $5/month. If you add Claude Haiku to score match quality (rather than rule-based scoring), add another $0.02-0.05/day. Total: under $15/month for a custom daily alert system that outperforms Naukri’s built-in alerts significantly.
Explore the scraper referenced in this article: inputs, outputs, and pricing, then run it on Apify.
GitHub Repo Intelligence: Vet Any Dependency Before You Add It
Pull stars, forks, contributors, commit recency, latest release and full README for any public GitHub repo in one call. No token required, MCP-ready for agents doing dependency checks.
Bilibili Scraper: Video, Creator and Search Data in Python
Pull Bilibili videos by keyword, URL or creator ID: titles, view and like counts, duration, author stats. Plain HTTP, no cookies, no login required.
SEEK Jobs Scraper: Australian Job Listings in Python
How to pull SEEK.com.au job listings by keyword, location, work type and salary band in Python: title, company, salary range, apply link, no login or API key.