The Mine Works
Browse on Apify
Building an Automated Naukri Job Alert System with Python
← All posts
tutorial November 3, 2025 · 7 min read

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.

Try the scraper

The actor referenced in this article is live on Apify. Pay only for results delivered.

Open on Apify →

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.

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

  1. Pulls the latest job postings from Naukri for your target keywords
  2. Applies multi-dimensional filters (salary range, experience, location, company size)
  3. Scores and ranks postings by match quality
  4. 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 approximately $0.005-0.01 per job listing. Running daily alerts for 1-2 keyword profiles generating 50-100 results costs roughly $0.15-0.30/day — under $10/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.

Related Actor

Try the scraper referenced in this article — live on Apify, pay only for results.

Open naukri-jobs on Apify →