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.
I'm a developer. I refused to do this.
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.
The Problem
LinkedIn Easy Apply is supposed to make job hunting easier. But "easier" still means:
Search for jobs manually
Open each one
Check if it's relevant to your skills
Fill out the same form fields again and again
Track which ones you applied to (or forget and apply twice)
Multiply that by 50 applications a day and it's a full-time job just applying for jobs.
The Architecture
The system has 5 components:
main.py
├── scraper/linkedin.py → Playwright browser automation
├── ai_matcher/evaluator.py → Gemini AI job-CV scoring
├── ai_matcher/form_filler.py → AI-powered form answering
├── email_manager/monitor.py → Gmail inbox watcher
└── sheet_manager.py → Google Sheets tracker
Step 1: Scraping LinkedIn with Playwright
I used Playwright instead of Selenium because it's faster, more reliable, and handles modern JS-heavy pages better.
python
from playwright.sync_api import sync_playwright
def run_scraper():
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
# 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)
One challenge: LinkedIn's CAPTCHA. The bot pauses and waits for you to solve it manually, then continues automatically once you're logged in.
Step 2: AI Job Scoring with Gemini
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.
python
def evaluate_job(job_description: str, cv_text: str) -> int:
prompt = f"""
You are an expert technical recruiter.
Score how well this CV matches this job description (0-100).
Only output the integer score, nothing else.
Job Description: {job_description}
Candidate CV: {cv_text}
"""
response = call_gemini_api(prompt)
return int(response.strip())
If the score is below my threshold (I use 70), it skips. This means the bot only applies to jobs I'd actually want.
The Gemini free tier is enough — I'm running hundreds of evaluations per day at zero cost.
Step 3: Auto-Filling Easy Apply Forms
Easy Apply forms ask things like "How many years of experience do you have with Python?" or "What's your expected salary?".
Instead of hardcoding answers, I feed the question + my CV to Gemini and let it answer:
python
def answer_question(question: str, cv_text: str) -> str:
prompt = f"""
You are filling out a job application.
Based on this CV, answer the following question concisely.
Just the answer — no explanation.
CV: {cv_text}
Question: {question}
"""
return call_gemini_api(prompt)
The bot then types the answer directly into the form field and clicks "Next".
Step 4: Tracking in Google Sheets
Every job gets logged automatically:
Platform Job ID Title Company Match Score Status
LinkedIn 123456 Python Developer Acme Corp 84 Applied
LinkedIn 789012 Software Engineer TechCo 91 Applied
Using the gspread library with a Google service account — setup takes about 5 minutes.
Step 5: Email Monitor
The bot also monitors Gmail for interview requests. It connects via IMAP and checks for emails containing keywords like "interview", "next steps", "offer".
python
def check_for_interviews():
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login(email, app_password)
mail.select('inbox')
_, 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)
Results
After running this for a few weeks:
Applied to 200+ jobs automatically
Only wasted time on jobs with 70+ AI match score
Tracked every application without manual effort
Got interview requests while I was asleep
What I Learned
Playwright > Selenium for modern web scraping. Period.
Gemini Flash is free and fast — perfect for high-volume text classification tasks
YAML config > hardcoded values — makes it trivial to change job titles, locations, thresholds
Browser automation still needs a human for CAPTCHAs — fully unattended isn't realistic yet
Get the Source Code
I've packaged the full project and released it for $19 on Gumroad — includes all source code, setup guide, and a working config template.
👉 Get ApplyHero on Gumroad
Setup takes about 10 minutes. The Gemini API free tier is sufficient — no credit card needed for that part.
Have questions about how any part of this works? Drop them in the comments — happy to go deeper on any component.
Top comments (0)