job-auto-apply
v1.2.0
Published
Autonomous job application engine — Playwright-based form filler with self-learning, OTP handling, and multi-ATS support
Downloads
382
Maintainers
Readme
auto-apply
Autonomous job application engine — give it your resume, profile, and job URLs. It scans the form, maps every field, fills it, submits, handles OTP, and learns from mistakes.
Built on Playwright. Works with Claude Code as a /auto-apply skill.
node cli.mjs apply https://job-boards.greenhouse.io/company/jobs/123Supported Platforms
| ATS | Scan | Fill | Submit | Notes | |-----|------|------|--------|-------| | Greenhouse | Yes | Yes | Yes | React Select, intl-tel-input, embedded forms | | Ashby | Yes | Yes | Yes | Yes/No buttons, typeahead, custom domains | | Lever | Yes | Yes | Yes | /apply/ navigation, ARIA dropdowns | | Workday | Yes | Yes | Yes | Login/account creation, multi-step wizard, conditional fields | | Gem | Yes | Yes | Yes | Direct application pages | | iCIMS | Yes | Yes | Partial | Detection only | | SmartRecruiters | Yes | Yes | Partial | Detection only | | Generic | Yes | Yes | Yes | Any site with Apply button + standard inputs |
How It Works
┌──────────┐
URL ──> │ Scanner │──> scan.json (all fields, types, options)
└────┬─────┘
│
┌────▼─────┐
│ Planner │──> plan.json (field → profile value mapping)
└────┬─────┘
│ ┌──────────────┐
┌────▼─────┐ │ Learner │
│ Engine │◄──┤ (corrections │
└────┬─────┘ │ from past) │
│ └──────────────┘
┌────▼─────┐
│ Verify │──> Re-check every field in DOM
└────┬─────┘
│
┌────▼─────┐ ┌─────────┐
│ Submit │──>│ OTP │──> Gmail IMAP fetch
└────┬─────┘ └─────────┘
│
┌────▼─────┐
│ Reporter │──> CSV log + screenshots
└──────────┘Pipeline per URL: Detect ATS → Scan form fields → Load profile → Pick best resume → Generate fill plan → Apply past learnings → Fill all fields → Verify every field → Retry empties → Submit → Handle OTP → Log result → Screenshot
Quick Start
Option A: npx (recommended)
mkdir my-job-search && cd my-job-search
npx job-auto-apply setup
# or install globally:
npm i -g job-auto-applyThat's it. Edit config/profile.yml, add your resume to resumes/, and start applying.
Option B: Clone & Install
git clone https://github.com/devdattatalele/auto-apply.git
cd auto-apply
npm install
npx playwright install chromium
node cli.mjs setup2. Create Your Profile
setup copies config/profile.example.yml → config/profile.yml. Edit it with your details:
personal:
first_name: "Jane"
last_name: "Doe"
email: "[email protected]"
phone: "5551234567"
linkedin: "https://linkedin.com/in/janedoe"
location: "San Francisco, California"
city: "San Francisco"
state: "California"
postal_code: "94105"
country: "United States +1"
country_phone_code: "United States of America (+1)"
eeo:
gender: "Female"
hispanic_latino: "No"
race: "White"
veteran_status: "I am not a protected veteran"
disability_status: "I do not want to answer"
work_auth:
authorized_us: "Yes"
sponsorship_needed: "No"
education:
degree: "Bachelor of Science"
major: "Computer Science"
university: "Stanford University"
graduation_year: "2020"
experience:
years: "5"
current_company: "Google"
current_title: "Software Engineer"3. Add Your Resume(s)
Place PDF files in resumes/ and configure config/resumes.yml:
default: my-resume
resumes:
- id: my-resume
file: resumes/my-resume.pdf
label: "General"
keywords:
- software engineer
- backend
- pythonThe engine picks the best resume variant by matching JD keywords.
4. Set Up OTP (Optional)
For sites that require email verification after submit:
cp .env.example .envEdit .env with your Gmail App Password (generate one here):
[email protected]
APP_PASSWORD=xxxx xxxx xxxx xxxx5. Apply
# Single application
npx job-auto-apply apply https://job-boards.greenhouse.io/company/jobs/123
# Or use the queue
npx job-auto-apply queue add https://careers.adobe.com/job/R167447 Adobe
npx job-auto-apply queue add https://jobs.lever.co/stripe/abc123 Stripe
npx job-auto-apply batchIf you cloned the repo, use
node cli.mjsinstead ofnpx job-auto-apply.
Commands
node cli.mjs setup Set up your profile
node cli.mjs scan <url> Scan form fields → JSON
node cli.mjs fill <url> [plan.json] Fill form (auto-plan if no plan given)
node cli.mjs apply <url> Full pipeline: scan → plan → fill → submit → OTP
node cli.mjs batch [targets.txt] Apply to all URLs in file (or process queue)
node cli.mjs queue add <url> [company] Add URL to application queue
node cli.mjs queue list Show queue entries
node cli.mjs queue remove <url> Remove URL from queue
node cli.mjs queue clear Clear completed/failed entries
node cli.mjs status Show stats & learningsscan — Inspect a form
node cli.mjs scan https://jobs.ashbyhq.com/company/job-idOutputs forms/{slug}-scan.json with every field: id, name, label, type, options, required status, CSS selector.
fill — Fill without full pipeline
node cli.mjs fill https://example.com/apply # auto-generates plan
node cli.mjs fill https://example.com/apply plan.json # uses custom planapply — Full pipeline
node cli.mjs apply https://job-boards.greenhouse.io/verkada/jobs/5039079007Runs: scan → pick resume → generate plan → apply learnings → fill → verify → submit → OTP → log.
queue — Manage application links
node cli.mjs queue add "https://careers.adobe.com/us/en/job/R167447" Adobe
node cli.mjs queue add "https://jobs.lever.co/stripe/abc123" Stripe
node cli.mjs queue list
# ⏳ pending │ Adobe │ https://careers.adobe.com/...
# ⏳ pending │ Stripe │ https://jobs.lever.co/...
node cli.mjs batch # processes all pending queue entries
node cli.mjs queue list
# ✅ submitted │ Adobe │ https://careers.adobe.com/...
# ✅ submitted │ Stripe │ https://jobs.lever.co/...batch — Bulk apply
node cli.mjs batch # process pending queue entries
node cli.mjs batch targets.txt # process URLs from filestatus — Dashboard
node cli.mjs status
# Overall:
# Total applications: 4
# Submitted: 3
# Failed: 1
# Success rate: 75%
#
# By ATS:
# greenhouse: 2/2 (100%)
# workday: 1/1 (100%)
# ashby: 0/1 (0%)
#
# Queue: 2 pending, 3 applied, 0 failedArchitecture
auto-apply/
├── cli.mjs CLI entry point
├── lib/
│ ├── engine.mjs Core fill engine (15 field type handlers)
│ ├── scanner.mjs Form field scanner
│ ├── planner.mjs Auto plan generation (label → profile mapping)
│ ├── discovery.mjs ATS detection & form navigation
│ ├── fields.mjs Universal field finder & dropdown handler
│ ├── otp.mjs Gmail IMAP OTP extraction
│ ├── workday.mjs Workday login & account creation
│ ├── learner.mjs Self-learning store
│ └── reporter.mjs CSV logging, screenshots & queue management
├── config/
│ ├── profile.yml Your profile (gitignored)
│ ├── profile.example.yml Template
│ └── resumes.yml Resume variants & keywords
├── resumes/ PDF resume files (gitignored)
├── forms/ Scan & plan JSON files (gitignored)
├── data/
│ ├── applied.csv Application log
│ ├── queue.csv Application queue
│ └── learnings.json Self-learning data
├── screenshots/ Pre/post-submit screenshots (gitignored)
└── targets.txt Batch URL listField Type Handlers
| Type | Handler | Example |
|------|---------|---------|
| text, email, tel, textarea | Direct fill | Name, Email, Phone |
| file | setInputFiles | Resume upload |
| select | Native selectOption | Country, State |
| dropdown (React Select) | Type-to-filter + click option | Gender, Race |
| custom-select | 4-strategy handler | Workday, Ashby |
| checkbox | Click toggle | Data consent, Terms |
| radio | Click by value or label | Yes/No, Prior worker |
| phone-country | intl-tel-input search | Country code picker |
| typeahead | Type + pick autocomplete | Location, Company |
| yes-no-button | DOM traversal from label | Visa sponsorship (Ashby) |
| multi-select | Type-filter per value | Languages, Skills |
Self-Learning
The engine records every fill result in data/learnings.json:
- Field failures — which fields failed and which strategy eventually worked
- Option corrections — e.g., profile says "I don't wish to answer" but dropdown has "I do not want to answer"
- Per-ATS success rates — tracks submit success by platform
- Applied automatically —
applyLearnings()corrects plan values before each fill
Dropdown Handler (4 Strategies)
- Native select —
selectOption({ label })for<select>elements - Type-to-filter — Type value into input, wait for React Select / custom dropdown, click best fuzzy match
- Click-scan — Open dropdown, scan all visible options, click best match
- Keyboard navigation — ArrowDown through options, Enter on match
Each strategy includes fuzzy text matching and verification that the value actually stuck.
Claude Code Integration
auto-apply works as a Claude Code skill. Add it to any project:
mkdir -p .claude/skills/auto-applyCreate .claude/skills/auto-apply/SKILL.md — see the included skill file for the full definition.
Then use it in Claude Code:
/auto-apply apply https://greenhouse.io/company/jobs/123
/auto-apply queue add https://careers.adobe.com/job/R167447 Adobe
/auto-apply statusEnvironment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| EMAIL | For OTP | Gmail address for receiving verification codes |
| APP_PASSWORD | For OTP | Gmail App Password (generate) |
| WORKDAY_EMAIL | No | Email for Workday account login |
| WORKDAY_PASSWORD | No | Password for Workday account login |
Test Results
Tested on real job applications:
| Platform | Company | Fields Filled | Result | |----------|---------|---------------|--------| | Greenhouse | Verkada | 14/14 | Submitted | | Greenhouse | Discord | 18/18 | Filled (custom Qs) | | Ashby | Perplexity | 11/11 | Filled (anti-bot) | | Workday | Adobe | 15/15 + 1 conditional | Submitted |
Safety
- Never headless — browser always opens visually so you can see what's happening
- Never auto-submits blindly — verification pass checks every field before submit
- Screenshot everything — pre-submit and post-submit screenshots saved automatically
- Retry with diagnosis — submit errors trigger re-scan and re-fill of empty fields
- No credential storage —
.envis gitignored, passwords never logged
License
MIT
