Skip to content

Repository files navigation

Multi-Board Job Agent

Automated job search agent that scans LinkedIn, Dice, CareerBuilder, and supporting boards every 15 minutes, scores jobs against your resume using AI, generates tailored resumes + cover letters, and auto-applies where the board exposes an internal apply flow.


What it does every 15 minutes

Scan LinkedIn + Dice + CareerBuilder → Score jobs (0-100) → Generate tailored resume + cover letter → Auto-apply
Score Action
< 40 Skip silently
40–64 Log only
65–81 Generate apply pack, send email notification (you apply manually)
82–100 Generate apply pack + auto-apply via Easy Apply

Quick Start

cd linkedin_job_agent
./setup.sh              # installs deps + Playwright browser

# Fill in credentials
cp .env.example .env
nano .env               # add AI key + LinkedIn/Dice/CareerBuilder creds

# Fill in your real resume details
cp config.example.yaml config.yaml
cp resume_profile.example.yaml resume_profile.yaml
nano resume_profile.yaml

# Start the agent
source .venv/bin/activate
python main.py

On first run it will restore any saved cookies in the shared Playwright context. If a board session is missing, the agent can fall back to the corresponding board credentials in .env.


Files

linkedin_job_agent/
├── main.py                  ← Orchestrator + APScheduler (runs every 15 min)
├── config.example.yaml      ← Safe template; copy to ignored config.yaml
├── resume_profile.example.yaml ← Fictional template; copy to ignored resume_profile.yaml
├── tracker.py               ← SQLite job database
├── notifier.py              ← Console logging + email alerts
├── linkedin/
│   ├── auth.py              ← Session management (cookies)
│   ├── scanner.py           ← LinkedIn search + description extraction
│   └── applier.py           ← LinkedIn Easy Apply form filler
├── boards/
│   ├── dispatcher.py        ← Platform-aware auto-apply routing
│   ├── dice.py              ← Dice Apply Now adapter
│   ├── careerbuilder.py     ← CareerBuilder Quick Apply adapter
│   └── common.py            ← Shared board form helpers
├── ai/
│   ├── scorer.py            ← Claude relevance scoring (0-100)
│   ├── resume_writer.py     ← Claude resume tailoring
│   └── cover_letter_writer.py ← Claude cover letter generation
├── documents/
│   └── generator.py         ← .docx file creation
├── output/
│   ├── resumes/             ← Generated tailored resumes
│   └── cover_letters/       ← Generated cover letters
├── data/jobs.db             ← SQLite tracking database
└── session/                 ← LinkedIn session cookies

Configuration

config.yaml — key settings

Create it from config.example.yaml. The real file is intentionally ignored so a public commit cannot expose a candidate's contact data or search preferences.

scoring:
  generate_pack_threshold: 65   # score needed to generate resume + cover letter
  auto_apply_threshold: 82      # score needed to auto-apply
  max_applications_per_day: 15  # safety cap

scheduler:
  scan_interval_minutes: 15     # how often to scan all configured boards
  quiet_hours_start: 23         # no scanning between 11pm–7am
  quiet_hours_end: 7

dice:
  enabled: true
  results_per_keyword: 10

careerbuilder:
  enabled: true
  results_per_keyword: 10

resume_profile.yaml — fill in your real data

Create it from resume_profile.example.yaml. The real file is intentionally ignored. The system uses it as the source of truth for all AI generation. Fill in:

  • Your real experience entries with real companies/dates/metrics
  • Certifications you actually hold
  • Real contact info (phone, LinkedIn URL)

Resume variants

The agent selects the most relevant configured resume variant as the base template, then overlays AI-tailored content:

Variant Used when job title contains
tech_pm Technical Project Manager
it_pm IT Project Manager
agile_pm Agile
senior_pm Senior Project Manager
ops_manager Operations Manager
program_manager Program Manager
general_pm Everything else

Monitoring

View job database:

sqlite3 data/jobs.db "SELECT title, company, score, status FROM jobs ORDER BY score DESC LIMIT 20;"

View logs:

tail -f logs/agent.log

View stats:

python -c "import asyncio; from tracker import get_stats; print(asyncio.run(get_stats()))"

Email Notifications

Fill in .env:

NOTIFY_EMAIL=candidate@example.com
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=candidate@example.com
SMTP_PASS=your_gmail_app_password   # Gmail → Settings → App Passwords

You'll get an email when:

  • A pack is generated (score 65–81) — you apply manually
  • An auto-application fires (score 82+)

Safety limits

  • Max 15 applications/day (configurable)
  • Max 3 applications/run
  • Quiet hours: no scanning 11pm–7am
  • Human-like delays between all actions (1.5–4 seconds)
  • Session cookies (not password) used after first login
  • LinkedIn Easy Apply only — no external form navigation

Notes

  • LinkedIn, Dice, and CareerBuilder all rate-limit aggressive automation — the 15-minute interval and human-like delays are designed to keep the browser behaviour conservative
  • If a board presents a security challenge or unexpected login wall, that application will fall back to failed/manual-needed for that cycle
  • The agent never applies to the same job twice (SQLite dedup)
  • All generated files are stored locally — nothing is uploaded anywhere

About

Multi-board AI job workflow for discovery, fit scoring, document generation, and controlled applications.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages