| name | linkedin-job-auto-apply |
| description | Automate job applications on LinkedIn using Playwright MCP tools. Features Easy Apply support, target-based stopping, keyboard controls (P/R/Q), on-page status indicator, and proven automation patterns. |
LinkedIn Job Application Automation Skill
This skill enables automated job applications on LinkedIn using Playwright MCP browser automation tools with intelligent target-based execution and real-time keyboard controls.
Specialized Agents
This skill is backed by three specialist agents. Invoke them for deeper help:
| Agent | File | When to Use |
|---|
| Strategy Agent | skills/agents/strategy-agent/SKILL.md | Filter jobs by relevance, blocklist, seniority, session budget |
| Automation Agent | skills/agents/automation-agent/SKILL.md | Timing, retry logic, rate limiting, anti-detection |
| Web Structure Agent | skills/agents/web-structure-agent/SKILL.md | Broken selectors, LinkedIn DOM changes, lazy loading |
| QA Agent | skills/agents/qa-agent/SKILL.md | Verify submissions, validate results, session reports |
Orchestrated Run Order
1. QA Agent → preFlightCheck(page) # must PASS — abort if it fails
2. Strategy Agent → filterJobs(jobs, prefs) # score & filter before applying
3. [run automation with filtered job list]
└─ per-job: QA Agent → verifyApplicationSuccess()
4. QA Agent → generateReport() # end-of-session PASS/WARN/FAIL
Escalation during run:
- Selectors return 0 results → Web Structure Agent (selector update)
- High failure rate or detected → Automation Agent (timing / retry review)
- Applying to irrelevant jobs → Strategy Agent (tighten filters)
- Unsure if applications went through → QA Agent (verification)
userProfile — Configure Before Running
Set personal values to avoid detectable generic placeholders:
const userProfile = {
phone: '+1-555-000-0000',
linkedinUrl: 'https://www.linkedin.com/in/yourhandle',
city: 'San Francisco',
zip: '94105',
yearsExp: 5
};
await autoApplyLinkedInJobs(page, {
targetApplications: 20,
searchKeywords: 'software engineer',
userProfile
});
When to Use This Skill
Use this skill when:
- Daily job hunting: Apply to Easy Apply jobs automatically
- Target-based applications: Stop exactly when you reach N applications
- Efficient batch processing: Apply from search results
- Controlled automation: Need pause/resume/quit controls during execution
- Scale applications: Apply to dozens or hundreds of jobs efficiently
- Resume from interruption: Continue from specific page after breaks
Script Files
This skill includes ready-to-use JavaScript files:
Simply copy/paste the functions into your Playwright MCP code block to use them.
Prerequisites
Before using this skill, ensure:
- User is logged in to their LinkedIn account
- Resume is uploaded to LinkedIn
- Profile is complete and up-to-date
- Playwright MCP tools are available
- Stable internet connection
Installation
Claude Code Marketplace (Recommended)
claude
/plugin marketplace add jerryliu/linkedin-skill
/plugin install linkedin-job-auto-apply
/linkedin-job-auto-apply
Local Development
For testing local modifications:
/plugin marketplace add /path/to/linkedin-skill
/plugin install linkedin-job-auto-apply@local
Usage Modes
Mode 1: Single Job Application (Manual Testing)
Use this for testing or applying to one job at a time. Perfect for verifying the automation works before running batch jobs.
Implementation:
Helper functions are available in applySingleJob.js.
Quick Usage:
await page.goto('https://www.linkedin.com/jobs/search/?keywords=software%20engineer&location=United%20States&f_AL=true');
await page.waitForTimeout(3000);
const jobs = await listJobs(page);
console.log('Available Easy Apply jobs:', jobs);
const result = await applySingleJob(page, 0);
console.log(result);
const result2 = await applySingleJob(page, 2);
Returns:
{
status: 'success' | 'failed' | 'skipped' | 'error',
job: {
title: "Software Engineer",
company: "Tech Company",
location: "San Francisco, CA",
hasEasyApply: true
},
finalUrl: "https://www.linkedin.com/jobs/..."
}
💡 Tip: Always test with Mode 1 (single job) before running batch automation to verify the flow works correctly.
Mode 2: Target-Based Automation with Keyboard Controls
Use this for applying to jobs with real-time keyboard controls and target-based stopping.
Features:
- Press P to PAUSE automation
- Press R to RESUME automation
- Press Q to QUIT automation
- Stops automatically when target number of applications is reached
- On-page status indicator showing progress
- Efficient Easy Apply automation
Configuration:
const options = {
startPage: 1,
targetApplications: 20,
maxPages: 20,
searchKeywords: 'software engineer',
location: 'United States',
easyApplyOnly: true
};
Implementation:
The complete automation function is available in autoApplyLinkedInJobs.js.
Quick Usage:
await autoApplyLinkedInJobs(page, { targetApplications: 20 });
await autoApplyLinkedInJobs(page, {
startPage: 1,
targetApplications: 50,
maxPages: 30,
searchKeywords: 'backend developer',
location: 'San Francisco Bay Area',
easyApplyOnly: true
});
Returns:
{
successful: 15,
failed: 2,
skipped: 10,
pages: [
{ pageNumber: 1, successful: 8, failed: 1, skipped: 5 },
{ pageNumber: 2, successful: 7, failed: 1, skipped: 5 }
]
}
💡 Tip: For full implementation details, see autoApplyLinkedInJobs.js. The script includes comprehensive error handling, keyboard controls, and status indicator setup.
Configuration Options
| Option | Type | Default | Description |
|---|
startPage | number | 1 | Starting page number for job search |
targetApplications | number | 20 | Target number of successful applications (stops when reached) |
maxPages | number | 20 | Maximum number of pages to search |
searchKeywords | string | 'software engineer' | Job search keywords |
location | string | 'United States' | Job location |
easyApplyOnly | boolean | true | Only apply to Easy Apply jobs |
delayMin | number | 2000 | Minimum delay between applications (ms) |
delayMax | number | 4000 | Maximum delay between applications (ms) |
Key Features
✨ Easy Apply Support
- Focuses on LinkedIn's Easy Apply feature
- Automatically handles simple application flows
- Skips complex applications requiring additional steps
✨ Target-Based Execution
- Stops automatically when target number of successful applications is reached
- Precise control - won't waste time processing unnecessary jobs
- Example: Target 20 applications = exactly 20 successful submissions
⌨️ Real-Time Keyboard Controls
- P = Pause automation (take a break, check results)
- R = Resume automation (continue from where you paused)
- Q = Quit automation (graceful exit with results summary)
📊 On-Page Status Indicator
- Always visible progress box on page
- Shows: Current progress (e.g., "Applications: 5/20")
- Color-coded: Green (running), Orange (paused), Blue (completed)
🎯 Smart Filtering
- Skip already applied jobs: Detects jobs already applied to
- Easy Apply detection: Only processes Easy Apply jobs (configurable)
- Human-like delays: 2-4 seconds between applications (avoids detection)
- Robust error handling: Continues on failures
Safety Features
- Skip already applied jobs: Saves time and avoids duplicate applications
- Easy Apply filtering: Focus on simple, one-click applications
- Human-like random delays: 2-4 seconds between applications (avoids detection)
- Graceful error handling: One failure doesn't stop the entire process
- Tab cleanup: Always closes application tabs, even on errors
- Target-based stopping: Prevents runaway execution
- Real-time monitoring: Detailed console output and on-page status
- Keyboard interrupt: Press Q anytime to gracefully quit
Technical Implementation Details
Critical Selectors (LinkedIn-Specific)
'button[aria-label*="Easy Apply"]'
'button:has-text("Easy Apply")'
'.job-card-container'
'.jobs-search-results__list-item'
'text=Applied'
'.job-card-container__footer-item'
'button[aria-label="Submit application"]'
'button:has-text("Submit")'
'button[aria-label="Continue to next step"]'
'button:has-text("Next")'
Application Flow Pattern
- Stay on search results page - Process jobs from list
- Check for Easy Apply badge - Filter for Easy Apply jobs only
- Check for already-applied - Look for "Applied" indicator (fast skip)
- Click Easy Apply button - Opens modal (not new tab)
- Handle application modal - May be single-step or multi-step
- Submit application - Click Submit button
- Verify success - Check for confirmation message
- Close modal - Return to search results
Timing Strategy (Optimized)
await page.waitForTimeout(3000);
await page.waitForTimeout(2000);
await page.waitForTimeout(1500);
await page.waitForTimeout(2500);
const delay = 2000 + Math.random() * 2000;
Success Verification
const successIndicator = await page.locator('text=Application sent').isVisible();
if (successIndicator) {
}
const modalClosed = await page.locator('[role="dialog"]').count() === 0;
Limitations
- Easy Apply only: Cannot handle complex applications requiring cover letters, assessments, or multi-page forms
- Cannot handle CAPTCHA: Must be solved manually if encountered
- Cannot handle custom questions: May skip jobs with additional required questions
- Requires browser open: Browser must stay open during execution
- User must be logged in: Must be logged in before starting
- Rate limits: LinkedIn may impose rate limits on automated actions
Troubleshooting
Problem: "Easy Apply button not found"
Solution: Verify user is logged in and ensure you're filtering for Easy Apply jobs only
Problem: Applications failing
Solutions:
- Check internet connection
- Verify account status (LinkedIn may flag automated behavior)
- Increase delays between jobs
- Test with manual single job application first
- Check for CAPTCHA or security challenges
Problem: Script stops unexpectedly
Solutions:
- Check console for errors
- Verify page structure hasn't changed
- Try reducing maxPages value
- Resume from last successful page
- Check for LinkedIn security measures
Problem: "Already applied" not detected
Solution: LinkedIn may have changed the indicator text or structure - verify the selector
Best Practices
Before Running
- Test with 1-2 jobs manually first
- Verify resume and profile are complete
- Check search criteria match your skills
- Ensure stable internet connection
- Review LinkedIn's acceptable use policies
During Execution
- Monitor first few applications
- Be ready to stop if errors occur
- Don't interfere with browser while running
- Watch for CAPTCHA or security challenges
After Completion
- Verify successful applications in LinkedIn account
- Check email for confirmation/interview invites
- Review any failed applications
- Take breaks between automation sessions
Legal & Ethical Usage
Important: This tool is for educational and personal productivity purposes only.
- Only apply to jobs you're genuinely interested in and qualified for
- Do not spam applications
- Respect rate limits and server load
- Use responsibly and in accordance with LinkedIn Terms of Service
- Recommended maximum: 20-50 jobs per session
- Take breaks between sessions (several hours minimum)
- Be aware that excessive automation may result in account restrictions
Example Workflows
Workflow 1: Quick Daily Job Hunt (Recommended)
Apply to 20 Easy Apply jobs starting from page 1, with keyboard controls available.
await autoApplyLinkedInJobs(page, {
targetApplications: 20,
searchKeywords: 'software engineer',
location: 'United States'
});
Workflow 2: Targeted Job Search
Target specific job types and locations.
await autoApplyLinkedInJobs(page, {
targetApplications: 30,
maxPages: 20,
searchKeywords: 'backend developer python',
location: 'San Francisco Bay Area',
easyApplyOnly: true
});
Workflow 3: Resume from Specific Page
If you've already processed pages 1-5, start from page 6.
await autoApplyLinkedInJobs(page, {
startPage: 6,
targetApplications: 20,
maxPages: 15
});
Workflow 4: Conservative Approach
Use longer delays to be more cautious.
await autoApplyLinkedInJobs(page, {
targetApplications: 15,
delayMin: 4000,
delayMax: 7000
});
Workflow 5: Testing with Single Job
Test the automation with Mode 1 (single job) first before batch processing.
await page.goto('https://www.linkedin.com/jobs/search/?keywords=software%20engineer&f_AL=true');
await page.waitForTimeout(3000);
const jobs = await listJobs(page);
console.log(`Found ${jobs.length} Easy Apply jobs`);
const result = await applySingleJob(page, 0);
console.log('Test result:', result);
if (result.status === 'success') {
await autoApplyLinkedInJobs(page, { targetApplications: 20 });
}
Technical Notes
Browser Automation
- Uses Playwright MCP tools for browser control
- Handles modal management (opening/closing Easy Apply modals)
- Implements wait times for page loads and DOM updates
Job Detection
- Identifies Easy Apply jobs using LinkedIn-specific selectors
- Filters out already applied jobs
- Excludes non-Easy Apply positions (if configured)
Application Flow
- Navigate to LinkedIn job search results
- Extract Easy Apply job listings
- For each job:
- Check if already applied
- Click Easy Apply button
- Wait for modal to open
- Handle single-step or multi-step application
- Submit application
- Verify success message
- Close modal
- Move to next page and repeat
Error Recovery
- Try-catch blocks around each job application
- Continues to next job if one fails
- Cleans up modals even on errors
- Provides detailed error messages
What Makes This Skill Different?
This skill is specifically designed for LinkedIn's unique application system:
Key Improvements
-
Easy Apply Focus ⭐
- Optimized for LinkedIn's Easy Apply feature
- Handles single-click applications efficiently
- Skips complex multi-step applications
-
Modal Management ⭐
- LinkedIn uses modals, not new tabs
- Proper modal handling and cleanup
- Works with both simple and multi-step modals
-
Smart Job Filtering ⭐
- Detects Easy Apply badge before clicking
- Identifies already-applied jobs
- Filters by location, keywords, etc.
-
LinkedIn-Specific Selectors ⭐
- Uses LinkedIn's ARIA labels and semantic HTML
- Adapts to LinkedIn's dynamic content loading
- Handles LinkedIn's infinite scroll
-
Keyboard Controls ⭐
- Pause/Resume/Quit anytime with P/R/Q keys
- Full control during automation
- Safe interruption mechanism
-
Target-Based Execution ⭐
- Stop exactly when you reach your target
- No wasted time
- Precise control
Remember: Use this skill responsibly for genuine job applications to suitable positions. Quality matters more than quantity. Be aware of LinkedIn's terms of service and use automation sparingly to avoid account restrictions.