Coding8 min read

Building an Auto-Apply System with Claude Code + Playwright MCP

N

Nyaradzo

March 24, 2026

I built a fully autonomous job application system that browses company career pages, finds relevant jobs, selects the right resume for each role, fills out application forms with pre-written answers, and submits, all hands-free. One command: /auto-apply.

Here's how it works, what broke along the way, and what I learned.

Phase 1: The API Approach (What Didn't Work)

I started by trying to use the Greenhouse ATS API to programmatically submit applications. The system scraped jobs from 10 Greenhouse companies (Pinterest, Airbnb, Stripe, Coinbase, etc.) using their public API, stored jobs in a database with dedup via externalId + companySlug, and built a profile system where users could enter their name, phone, resume, and work authorization answers.

The plan was to POST applications directly to the Greenhouse boards API.

Why it failed: The Greenhouse read API is public, but the submit API requires an API key that only the company's recruiters have. I got HTTP 401 on every submission attempt.

What I kept: The database models, profile system, application tracking, and admin dashboard. All of this infrastructure carried forward into the version that actually works.

Phase 2: The Browser Automation Approach (What Worked)

Inspired by tools like JobPilot and ApplyPilot, I pivoted to Playwright MCP, which lets Claude Code drive a real browser. Instead of calling APIs, Claude navigates to the actual application page, reads the form, fills it in, uploads the resume, and clicks submit. Just like a human would.

This was the breakthrough. No API keys needed. No ATS-specific integrations. If a human can fill out the form, Claude can too.

How It Works

The system runs through this flow:

  1. Load Profile: pulls your name, email, phone, and work authorization from the database
  2. Load Resumes: checks your role-specific resume PDFs and their availability
  3. Load Answers: reads pre-written answers for common application questions
  4. For Each Company: navigates to their careers page, searches for relevant jobs, then for each job: checks if already applied, matches the best resume, fills the form, uploads the PDF, submits, and records the result

All of this happens through Playwright MCP browser actions: real clicks, real typing, real file uploads.

Setup

Install Playwright MCP

claude mcp add playwright -- npx @playwright/mcp@latest

Auto-Allow Permissions

Add "mcp__playwright__*" to your .claude/settings.local.json under permissions.allow. This prevents Claude from asking for approval on every browser action.

Prepare Your Resumes

This is a critical design decision. You could use a single resume for everything, but the system gets significantly more effective when you have multiple resumes tailored to different job families.

Here's why: auto-apply needs to move fast. If you only have one generic resume, it's either too broad to be competitive or you'd need to stop and customize it for every application, which defeats the entire purpose of automation. Instead, you create a small set of resumes upfront, each edited for a different category of role you're targeting. I did one for engineering, one for developer advocacy, and another for the hottest new AI job: storytelling. The system then picks the right one automatically based on the job title.

Think of it like setting up templates. You do the resume work once across a few job families, and every future application gets the right version without you touching anything. Your categories will depend on what roles you're going after. The point is that you batch the personalization upfront so the auto-apply flow runs uninterrupted.

You define each resume in a JSON config file with keywords that map to job titles. The matching script scores each resume against the job title and picks the best fit.

Configure Your Profile

Go to /profile on your site and fill in your first name, last name, phone, US state, country of residence, work authorization status, and sponsorship needs.

Run It

claude
/auto-apply                          # All 20 companies
/auto-apply Stripe, Anthropic       # Specific companies
/auto-apply --add "Ramp" "https://ramp.com/careers"

For fully autonomous operation, use the --dangerously-skip-permissions flag when launching Claude.

The Resume Matching System

This is one of my favorite parts. Each resume has keywords tied to role types. When Claude finds a job, it runs the match script with the job title. The script scores each resume by how many keywords match, with longer and more specific keywords scoring higher.

The key rule: jobs that don't match any resume are skipped entirely. No fallback, no wrong-resume applications. I'd rather miss a job than send the wrong resume.

This is what makes the multi-resume approach worth the upfront effort. You're not just blasting out a generic PDF. You're sending the version of yourself that's most relevant to what the company is actually hiring for.

Application Answers

Pre-written answers are organized into several categories:

Personal Info: name, email, phone, location, LinkedIn, work authorization, start date, and salary expectations.

Self-Identification: disability status, veteran status, ethnicity, race, and gender. All filled in honestly.

Salary Rules. This one required some nuance:

  • Free-text fields get: "Open to discussion based on total compensation and scope of the role."
  • Numeric required fields with a visible range: enter the HIGH end
  • Numeric required with no range: role-specific defaults (SWE/AI at 200K, DevRel at 185K, Content at 160K, Storyteller/PM at 170K)
  • Range selector fields: 150K to 200K

Role-Specific Q&A. Four separate answer sets, one per resume type. Each has tailored answers for "Why this company?", "Why this role?", "Tell me about yourself", greatest strength, greatest weakness, "Why are you leaving?", and a technical/portfolio highlight. All answers are 2-4 sentences, written in first person, grounded in real experience and metrics.

General Q&A. Shared across all roles, covering management style, conflict resolution, learning approach, diversity & inclusion, 5-year vision, uniqueness, handling failure, and staying current in tech.

Triple-Layer Deduplication

Three layers prevent re-applying to the same job:

  1. Database check before applying. Looks up company + job title. If status is submitted or pending, skip.
  2. URL-based dedup on recording. Generates a deterministic ID from the job URL. The database has a unique constraint on this ID + company slug.
  3. Greenhouse jobs. Filters out jobs already in the application table before returning eligible jobs.

Target Companies

The system currently targets 20 companies: Anthropic, OpenAI, Meta, Google, Apple, Microsoft, Netflix, Stripe, Figma, Gamma, Notion, Vercel, Coinbase, Airbnb, Pinterest, Databricks, Robinhood, Lyft, Dropbox, and Salesforce.

Adding a new company is as simple as editing a JSON file with the company name and careers page URL.

Challenges & How I Solved Them

  • Greenhouse API requires auth for submissions. Pivoted to Playwright browser automation.
  • Career pages exceed Playwright's token limit. Created a helper script to parse saved snapshots and extract job links.
  • Inline Python scripts trigger security prompts. Banned inline scripts; use only pre-built TypeScript helpers.
  • Same job could be applied to twice. Triple-layer dedup: company+title check, URL-based ID, database constraint.
  • Different roles need different resumes. Keyword-based resume matching with no fallback (skip if no match).
  • Custom ATS questions vary per job. Question matcher with text patterns + profile field mapping.
  • Country name variations (USA vs United States). Alias mapping in the question matcher.
  • Salary questions need nuanced handling. Rules engine: vague when possible, high end when numeric required.

What Makes This Different

Most auto-apply tools are either API-only (limited to one ATS, need auth keys) or generic (one resume for everything, template answers).

This system is:

  • Browser-based. Works on any career page, any ATS, any form.
  • Resume-aware. Picks the right resume for each role.
  • Answer-aware. Tailored responses per role type, adapted per company.
  • Tracked. Every application recorded in a database with full history.
  • Deduped. Won't apply to the same job twice.
  • Extensible. Add companies by editing a JSON file, add resumes by dropping a PDF.

The Tech Stack

  • Claude Code: the orchestrator that reads pages, fills forms, and makes decisions
  • Playwright MCP: browser automation for navigating, clicking, typing, and uploading
  • Next.js 14: web UI for profile management and application tracking
  • Prisma + SQLite/Turso: database for jobs, applications, and user profiles
  • TypeScript: all helper scripts
  • Vercel: hosting and deployment

What's Next

The system works. It's applied to real jobs at real companies. But there's always more to build. Better error recovery when forms have unusual layouts, smarter answer matching for questions I haven't seen before, and maybe a way to track response rates per resume type to optimize which version gets sent where.

If you're spending hours a week filling out the same forms with the same answers, you don't have to. Build a system that does it for you.

#Claude Code#Playwright#Automation#Job Search#AI#TypeScript

Share this article

Enjoyed this article?

Subscribe to get new posts delivered straight to your inbox.

Subscribe to Newsletter