<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: MI Srabon</title>
    <description>The latest articles on DEV Community by MI Srabon (@srabonmonoarul).</description>
    <link>https://dev.to/srabonmonoarul</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3999927%2F556beabc-aa37-4bcb-b8a7-6bedb288b5c8.png</url>
      <title>DEV Community: MI Srabon</title>
      <link>https://dev.to/srabonmonoarul</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/srabonmonoarul"/>
    <language>en</language>
    <item>
      <title>I Built a Python Bot That Applies to LinkedIn Jobs While I Sleep</title>
      <dc:creator>MI Srabon</dc:creator>
      <pubDate>Wed, 24 Jun 2026 06:56:05 +0000</pubDate>
      <link>https://dev.to/srabonmonoarul/i-built-a-python-bot-that-applies-to-linkedin-jobs-while-i-sleep-26ki</link>
      <guid>https://dev.to/srabonmonoarul/i-built-a-python-bot-that-applies-to-linkedin-jobs-while-i-sleep-26ki</guid>
      <description>&lt;p&gt;Last month I was applying to jobs manually. Copy-paste the job title. Upload CV. Answer the same 5 questions. Click submit. Repeat 40 times a day.&lt;/p&gt;

&lt;p&gt;I'm a developer. I refused to do this.&lt;/p&gt;

&lt;p&gt;So I built ApplyHero — a Python bot that scrapes LinkedIn, scores jobs against my CV using AI, auto-fills Easy Apply forms, and tracks everything in Google Sheets. Here's exactly how I built it.&lt;/p&gt;

&lt;p&gt;The Problem&lt;br&gt;
LinkedIn Easy Apply is supposed to make job hunting easier. But "easier" still means:&lt;/p&gt;

&lt;p&gt;Search for jobs manually&lt;br&gt;
Open each one&lt;br&gt;
Check if it's relevant to your skills&lt;br&gt;
Fill out the same form fields again and again&lt;br&gt;
Track which ones you applied to (or forget and apply twice)&lt;br&gt;
Multiply that by 50 applications a day and it's a full-time job just applying for jobs.&lt;/p&gt;

&lt;p&gt;The Architecture&lt;br&gt;
The system has 5 components:&lt;/p&gt;

&lt;p&gt;main.py&lt;br&gt;
├── scraper/linkedin.py       → Playwright browser automation&lt;br&gt;
├── ai_matcher/evaluator.py   → Gemini AI job-CV scoring&lt;br&gt;
├── ai_matcher/form_filler.py → AI-powered form answering&lt;br&gt;
├── email_manager/monitor.py  → Gmail inbox watcher&lt;br&gt;
└── sheet_manager.py          → Google Sheets tracker&lt;br&gt;
Step 1: Scraping LinkedIn with Playwright&lt;br&gt;
I used Playwright instead of Selenium because it's faster, more reliable, and handles modern JS-heavy pages better.&lt;/p&gt;

&lt;p&gt;python&lt;/p&gt;

&lt;p&gt;from playwright.sync_api import sync_playwright&lt;br&gt;
def run_scraper():&lt;br&gt;
    with sync_playwright() as p:&lt;br&gt;
        browser = p.chromium.launch(headless=False)&lt;br&gt;
        page = browser.new_page()&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    # Login to LinkedIn
    login(page, username, password)

    # Search and apply for each keyword/location combo
    for keyword in keywords:
        for location in locations:
            search_and_apply(page, keyword, location, cv_text)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;One challenge: LinkedIn's CAPTCHA. The bot pauses and waits for you to solve it manually, then continues automatically once you're logged in.&lt;/p&gt;

&lt;p&gt;Step 2: AI Job Scoring with Gemini&lt;br&gt;
This is the key piece. Before applying to anything, the bot sends the job description + your CV to Gemini and asks for a match score from 0–100.&lt;/p&gt;

&lt;p&gt;python&lt;/p&gt;

&lt;p&gt;def evaluate_job(job_description: str, cv_text: str) -&amp;gt; int:&lt;br&gt;
    prompt = f"""&lt;br&gt;
    You are an expert technical recruiter. &lt;br&gt;
    Score how well this CV matches this job description (0-100).&lt;br&gt;
    Only output the integer score, nothing else.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Job Description: {job_description}
Candidate CV: {cv_text}
"""

response = call_gemini_api(prompt)
return int(response.strip())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If the score is below my threshold (I use 70), it skips. This means the bot only applies to jobs I'd actually want.&lt;/p&gt;

&lt;p&gt;The Gemini free tier is enough — I'm running hundreds of evaluations per day at zero cost.&lt;/p&gt;

&lt;p&gt;Step 3: Auto-Filling Easy Apply Forms&lt;br&gt;
Easy Apply forms ask things like "How many years of experience do you have with Python?" or "What's your expected salary?".&lt;/p&gt;

&lt;p&gt;Instead of hardcoding answers, I feed the question + my CV to Gemini and let it answer:&lt;/p&gt;

&lt;p&gt;python&lt;/p&gt;

&lt;p&gt;def answer_question(question: str, cv_text: str) -&amp;gt; str:&lt;br&gt;
    prompt = f"""&lt;br&gt;
    You are filling out a job application.&lt;br&gt;
    Based on this CV, answer the following question concisely.&lt;br&gt;
    Just the answer — no explanation.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CV: {cv_text}
Question: {question}
"""
return call_gemini_api(prompt)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The bot then types the answer directly into the form field and clicks "Next".&lt;/p&gt;

&lt;p&gt;Step 4: Tracking in Google Sheets&lt;br&gt;
Every job gets logged automatically:&lt;/p&gt;

&lt;p&gt;Platform    Job ID  Title   Company Match Score Status&lt;br&gt;
LinkedIn    123456  Python Developer    Acme Corp   84  Applied&lt;br&gt;
LinkedIn    789012  Software Engineer   TechCo  91  Applied&lt;br&gt;
Using the gspread library with a Google service account — setup takes about 5 minutes.&lt;/p&gt;

&lt;p&gt;Step 5: Email Monitor&lt;br&gt;
The bot also monitors Gmail for interview requests. It connects via IMAP and checks for emails containing keywords like "interview", "next steps", "offer".&lt;/p&gt;

&lt;p&gt;python&lt;/p&gt;

&lt;p&gt;def check_for_interviews():&lt;br&gt;
    mail = imaplib.IMAP4_SSL('imap.gmail.com')&lt;br&gt;
    mail.login(email, app_password)&lt;br&gt;
    mail.select('inbox')&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_, messages = mail.search(None, 
    '(SUBJECT "interview") (UNSEEN)')

for msg_id in messages[0].split():
    # Parse and log the interview request
    process_interview_email(mail, msg_id)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Results&lt;br&gt;
After running this for a few weeks:&lt;/p&gt;

&lt;p&gt;Applied to 200+ jobs automatically&lt;br&gt;
Only wasted time on jobs with 70+ AI match score&lt;br&gt;
Tracked every application without manual effort&lt;br&gt;
Got interview requests while I was asleep&lt;br&gt;
What I Learned&lt;br&gt;
Playwright &amp;gt; Selenium for modern web scraping. Period.&lt;br&gt;
Gemini Flash is free and fast — perfect for high-volume text classification tasks&lt;br&gt;
YAML config &amp;gt; hardcoded values — makes it trivial to change job titles, locations, thresholds&lt;br&gt;
Browser automation still needs a human for CAPTCHAs — fully unattended isn't realistic yet&lt;br&gt;
Get the Source Code&lt;br&gt;
I've packaged the full project and released it for $19 on Gumroad — includes all source code, setup guide, and a working config template.&lt;/p&gt;

&lt;p&gt;👉 Get ApplyHero on Gumroad&lt;/p&gt;

&lt;p&gt;Setup takes about 10 minutes. The Gemini API free tier is sufficient — no credit card needed for that part.&lt;/p&gt;

&lt;p&gt;Have questions about how any part of this works? Drop them in the comments — happy to go deeper on any component.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>career</category>
      <category>python</category>
    </item>
  </channel>
</rss>
