🔧 Programmierung 🕛 vor 2 Monaten 9 Min Lesezeit
0

I Built a LinkedIn Easy Apply Bot in Python Here’s What I Learned About Browser Automation

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Job searching often involves repeating the same steps again and again.



Open LinkedIn. Search for roles. Filter by location. Check whether the job supports Easy Apply. Fill in the same contact details. Upload the same CV. Answer similar questions. Track which jobs were already applied to.



As a developer, I wanted to explore whether this repetitive workflow could be improved using browser automation — not as a spam tool, but as a controlled, human-supervised productivity assistant.



That led me to build LinkedIn Easy Apply Assistant, a Python-based automation project that uses Selenium to help with LinkedIn Easy Apply workflows.



The project is open source on GitHub:




CODE
https://github.com/Arul1998/linkedin-easy-apply









The Problem



Applying for jobs online can become repetitive very quickly.



Many application forms ask for the same basic information:




  • First name

  • Last name

  • Email

  • Phone number

  • City

  • CV upload

  • Work authorization

  • Notice period

  • Years of experience

  • Salary expectation



When someone is actively searching for jobs, they may fill the same information many times across different listings.



The goal of this project was simple:




Can I build a small automation assistant that reduces repetitive form filling while keeping the user in control?







What the Project Does



The project is a Python CLI tool that opens Chrome, logs into LinkedIn, searches for Easy Apply jobs, fills simple application forms, uploads a CV when required, and records successful applications.



At a high level, the workflow is:




  1. Read user configuration from config.json

  2. Read LinkedIn login credentials from .env

  3. Open Chrome using Selenium

  4. Log in to LinkedIn

  5. Search for jobs using configured filters

  6. Find Easy Apply jobs

  7. Open each job application modal

  8. Fill known fields from saved answers

  9. Upload the configured CV

  10. Answer simple questions using saved answers and resume-derived information

  11. Submit the application only when the form is manageable

  12. Save the application record to avoid duplicates



The assistant also includes a --dry-run mode so the user can test login and search without submitting any applications.






Important Note About Responsible Use



This project is intended as a personal productivity and learning project.



It is not designed for spam applying, bypassing platform protections, or violating website rules. Browser automation should be used carefully and responsibly.



For that reason, I added safeguards such as:




  • Dry-run mode

  • Confirmation mode

  • Rate limiting between actions

  • Rate limiting between applications

  • Duplicate tracking

  • Manual CAPTCHA / 2FA handling

  • Skipping complex or unknown forms

  • Configuration validation before running



The goal is not to apply to hundreds of jobs blindly. The goal is to reduce repetitive work while keeping the process controlled and human-supervised.






Tech Stack



The project uses:




  • Python

  • Selenium

  • Chrome WebDriver

  • python-dotenv

  • webdriver-manager

  • pypdf

  • JSON / CSV tracking



The main project files are:




CODE
main.py                 # CLI entry point
config.py # Loads and validates configuration
linkedin_automation.py # Selenium browser automation
resume_profile.py # Extracts resume information
tracker.py # Tracks applied jobs
session_store.py # Stores/reuses LinkedIn session cookies
errors.py # User-friendly error handling









Project Architecture



The project is split into small modules so that each file has a clear responsibility.






main.py



This is the entry point of the application.



It handles:




  • CLI arguments

  • Config loading

  • Validation

  • Browser startup

  • Login flow

  • Job search navigation

  • Application loop

  • Run summary



Some useful commands are:




CODE
python main.py --validate-only






This checks the setup without opening the browser.




CODE
python main.py --dry-run






This logs in and searches jobs but does not submit applications.




CODE
python main.py --confirm --pause-on-challenge --max-applications 5






This runs the assistant with user confirmation, CAPTCHA/2FA support, and a maximum application limit.






Configuration Design



The project separates secrets from normal configuration.



LinkedIn credentials are stored in .env:




CODE
[email protected]
LINKEDIN_PASSWORD=your-password






The job search settings and personal answers are stored in config.json:




CODE
{
"search": {
"keywords": "software engineer",
"location": "United Kingdom",
"work_type": "2",
"job_type": "F",
"date_posted": "r604800",
"experience_level": "3,4"
},
"max_applications": 5,
"resume_path": "C:/path/to/resume.pdf",
"tracking": {
"output_file": "applications.json",
"format": "json"
},
"saved_answers": {
"first_name": "Arul",
"last_name": "Cornelious",
"email": "[email protected]",
"phone": "your-phone-number",
"city": "St Albans",
"salary": "Negotiable",
"sponsorship": "Yes",
"start_date": "Immediately"
},
"custom_answers": {
"years of experience with angular": "3",
"are you willing to relocate": "Yes"
}
}






This design keeps sensitive credentials out of the main configuration file.






Building the LinkedIn Job Search URL



Instead of manually clicking filters, the assistant builds a LinkedIn job search URL using query parameters.



For example, it can include:




  • Keywords

  • Location

  • Easy Apply filter

  • Work type

  • Job type

  • Date posted

  • Experience level

  • Few applicants filter

  • LinkedIn geo ID



The Easy Apply filter is applied through the URL so the assistant focuses only on jobs that support LinkedIn’s Easy Apply workflow.



This makes the search flow simpler and more predictable.






Selenium Automation



The browser automation is handled with Selenium.



The assistant opens Chrome, logs into LinkedIn, searches jobs, and interacts with the Easy Apply modal.



One challenge with browser automation is that websites often change their HTML structure. To make the project more stable, I used multiple CSS selectors and XPath fallbacks for important elements like:




  • Job cards

  • Easy Apply buttons

  • Modal buttons

  • Submit buttons

  • Next buttons

  • Review buttons



For example, the assistant does not rely on only one selector for the Easy Apply button. It checks multiple possible selectors and also uses text-based fallback logic.



This makes the automation more resilient when LinkedIn changes small parts of the UI.






Login and Session Reuse



Logging in every time can trigger extra verification.



To reduce that, the assistant stores session cookies after a successful login and reuses them in later runs.



The login flow supports:




  • Normal email/password login

  • Saved session reuse

  • Fresh login mode

  • CAPTCHA / 2FA pause mode



If LinkedIn asks for verification, the assistant can pause and allow the user to complete the challenge manually in the browser.



Example:




CODE
python main.py --pause-on-challenge






This keeps the process human-supervised instead of trying to bypass security checks.






Resume-Based Question Answering



One of the most interesting parts of the project is the resume-based question answering system.



The assistant can read the configured CV and extract useful information such as:




  • Skills

  • Total years of experience

  • Skill-specific experience

  • Work authorization text

  • Notice period

  • Education level

  • Email

  • Phone number



It supports PDF extraction using pypdf.



The answer priority is:




CODE
custom_answers → resume-derived profile → saved_answers






This means manually configured answers always win.



For example, if the application asks:




CODE
How many years of experience do you have with Angular?






The assistant checks:




  1. Is there a matching custom answer?

  2. Is Angular found in the resume?

  3. Can it estimate experience from the resume?

  4. If not, should the question be skipped?



This prevents the assistant from guessing too aggressively.






Handling Unknown Questions



Not every application form is simple.



Some forms include custom questions, long text answers, dropdowns, multi-step flows, or questions that require human judgement.



The assistant is designed to skip forms it cannot confidently complete.



If it finds a question it cannot answer, the user can add it later to custom_answers.



Example:




CODE
{
"custom_answers": {
"do you require visa sponsorship": "Yes",
"what is your expected salary": "Negotiable",
"are you willing to work hybrid": "Yes"
}
}






This makes the system improve over time while still keeping the user in control.






Tracking Applications



The assistant records every successful application in a tracking file.



Example JSON output:




CODE
[
{
"job_title": "Software Engineer",
"company_name": "Example Company",
"job_url": "https://www.linkedin.com/jobs/view/123456789/",
"date_applied": "2026-07-03 12:00:00",
"status": "applied"
}
]






This solves two problems:




  1. The user can review application history.

  2. The assistant can avoid applying to the same job twice.



The project supports both JSON and CSV tracking.






Rate Limiting



Rate limiting is important in browser automation.



The project includes two types of delay:




CODE
delay_between_actions_sec
delay_between_applications_sec






The first delay controls normal browser actions like clicks and page loads.



The second delay controls how long the assistant waits after submitting an application.



This helps keep the automation slower, safer, and more human-like.






CLI Flags



I added several CLI flags to make the tool safer and easier to test.




CODE
--dry-run






Logs in and searches jobs but does not apply.




CODE
--confirm






Shows a confirmation prompt before live application submission.




CODE
--max-applications 5






Limits how many applications can be submitted in one run.




CODE
--pause-on-challenge






Pauses when CAPTCHA or 2FA appears.




CODE
--fresh-login






Ignores saved session cookies and logs in again.




CODE
--validate-only






Checks .env and config.json without opening the browser.



These flags are useful because browser automation should be tested carefully before any real action is performed.






Challenges I Faced






1. LinkedIn UI changes



LinkedIn’s DOM can change, which means selectors can break.



To handle this, I used multiple selector strategies and fallbacks.






2. Easy Apply forms are not always the same



Some applications are one step. Some are multiple steps. Some ask custom questions. Some require dropdowns, radio buttons, or file uploads.



The assistant handles simple and predictable forms, but skips complex ones.






3. Avoiding duplicate applications



Raw LinkedIn job URLs can include tracking parameters, so the same job can appear with different URLs.



To fix this, I normalized job URLs into a cleaner format before tracking them.






4. Not over-automating



The project needed a balance between automation and responsibility.



That is why I added dry-run mode, confirmation mode, manual challenge handling, delays, and skipping logic.






What I Learned



This project helped me understand several practical engineering concepts:




  • Browser automation with Selenium

  • CLI design in Python

  • Configuration management

  • Environment variable handling

  • Resume parsing

  • Form-filling logic

  • URL normalization

  • Error handling

  • Rate limiting

  • Session cookie reuse

  • Designing safer automation workflows



It also reminded me that automation is not just about making things faster. Good automation should also be controlled, explainable, and respectful of user intent.






Future Improvements



Some improvements I would like to add next:




  • Better dashboard for application history

  • Export reports by date, company, and role

  • Better support for dropdowns and radio buttons

  • More detailed skipped-job reasons

  • Safer preview mode before submitting each application

  • Local encrypted credential storage

  • Unit tests for resume parsing and answer matching

  • GitHub Actions workflow for linting and tests

  • Optional manual review step before final submit






Final Thoughts



This project started as a simple idea: reduce repetitive job application steps.



But it became a useful engineering exercise in browser automation, form intelligence, configuration design, safety controls, and responsible automation.



The biggest lesson I learned is that automation should not remove human judgement. It should support it.



For job applications, that means helping with repetitive form filling while still allowing the applicant to choose the right roles, review their details, and stay in control.



GitHub repository:




CODE
https://github.com/Arul1998/linkedin-easy-apply






Thanks for reading. I’m open to feedback, suggestions, and ideas for making this project safer and more useful.

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
9 Quellen
CVE-2022-44169 | Tenda AC15 15.03.05.18 formSetVirtualSer buffer overflow (EUVD-2022-47119)
1 Quelle
Best early October Prime Day deals: Save on TVs, smartwatches, and more tech
1 Quelle
I gave Claude Code $100 and 30 days to make a profit. Day 1, it built a product. Here's the pattern it used.
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten I Built a LinkedIn Easy Apply Bot in Python Here’s What I Learned About Browser Automation

Thematisch verwandte Begriffe: Built, LinkedIn, Easy, Apply · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...