Look, I’ll be honest – job hunting sucks.
It’s this soul-crushing cycle of copying and pasting the same information over and over again, tweaking your resume for the 100th time, and writing cover letters that make you sound desperate without actually sounding desperate.
But here’s the thing: repetitive tasks + structured process = perfect automation candidate.
So I did what any sane developer would do – I built a system to automate the whole damn thing. By the end, I had sent out 250 job applications in 20 minutes. (The irony? I got a job offer before I even finished building it. More on that later.)
Let me walk you through how I did it.
The Job Application Process is Broken
Think about it – every job application follows the same basic pattern:
-
Find job posting
-
Check if you’re qualified
-
Research company (let’s be real, most people skip this)
-
Submit resume + cover letter
-
Wait… and wait… and wait…
It’s like a really boring video game where you do the same quest over and over, hoping for different results.
Building the Proof of Concept
I started by writing some quick Python scripts to test if this crazy idea could work. Here’s how I broke it down:
Step 1: Getting the Job Listings (The Manual Part)
First challenge: getting job listings at scale. I tried web scraping but quickly realized something: job boards are like snowflakes – each one is uniquely annoying to scrape.
I tested dumping entire web pages into an LLM to clean the data, but:
So I went old school – manual HTML copying. Yes, it’s primitive. Yes, it works. Sometimes the simplest solution is the best solution.
Step 2: Cleaning the Raw HTML
The raw HTML was a mess, but I needed structured data like this:
{
"job_link": "https://example.com/job/12345",
"job_id": "12345",
"job_role": "software developer",
"employer": "Tech Corp Inc",
"location": "San Francisco, CA",
"work_arrangement": "Remote",
"salary": "$150,000"
}
Pro tip: You can just show ChatGPT a sample of your HTML and the output format you want, and it’ll write the parsing script for you. Work smarter, not harder.
Step 3: Getting the Full Job Details
This part was straightforward but required some finesse. For each job listing, I made a GET request to fetch the full description. Each request returns raw HTML that still has all the website scaffolding – navigation bars, popups, footer junk, the works.
I wrote a simple HTML parser to strip out everything except the actual job description. Sometimes you’ll hit extra hurdles – like having to click a button to reveal the recruiter’s email or company details. The good news? Since you’re working with one job board at a time, you only need to figure out these patterns once.
Pro tip: Always add delays between requests. I set mine to 2-3 seconds. Sure, it makes the process slower, but it’s better than getting your IP banned. Don’t be that person who DDOSes job boards – I added delays between requests because I’m not a monster.
Step 4: Converting Raw HTML to Structured Data
This is where it gets interesting. Job postings are like people – they all have the same basic parts but the organization is chaos. Some list skills at the top, others bury them in paragraphs of corporate speak.
Enter the LLM prompt that saved my sanity:
const prompt = `Please analyze these HTML contents from a job posting and extract information into a structured JSON format.
[... HTML content ...]
Format the response as valid JSON object with these exact keys:
- contact_email
- application_instructions
- job_posting_text (in markdown)
- job_posting_link
- additional_info (salary, location, etc.)
- job_title
- job_company
- job_department
- job_location
- job_skills
- job_instructions (how to apply)
optional keys
- hiring_manager_name
-
- job_portal
`
Step 5: Generating Cover Letters That Don’t Suck
The secret to good cover letters? Context. I fed my resume into the LLM along with the job details. This way, the AI could match my experience with their requirements. Suddenly, those “I’m excited about this opportunity” letters actually had substance.
Here’s the prompt that made it happen:
const prompt = `Please help me write a professional job application email based on the following information:
=== MY RESUME ===
${resumeMarkdown}
=== JOB DETAILS ===
Job Title: ${job_title}
Company: ${job_company}
Department: ${job_department || ''}
Location: ${job_location || ''}
Job Description: ${job_posting_text }
Required Skills: ${job_skills?.join(', ') || ''}
Application Instructions: ${job_instructions || ''}
Additional Context:
- Hiring Manager Name: ${hiring_manager_name || ''}
- Referral Source: ${referral_source || 'Job board'}
- Application Portal: ${job_portal || ''}
Instructions:
1. Create an email that is ready to send without any placeholders or edits needed
2. If any critical information is missing (like company name or job title), respond with an error message instead of generating incomplete content
3. Skip any optional fields if they're empty rather than including placeholder text
4. Use natural sentence structure instead of obvious template language
5. Include specific details from both the resume and job description to show genuine interest and fit
6. Any links or contact information should be properly formatted and ready to use
Format the response as a JSON object with these keys:
{
"status": "success" or "error",
"error_message": "Only present if status is error, explaining what critical information is missing",
"email": {
"subject": "The email subject line",
"body_html": "The email body in HTML format with proper formatting",
"body_text": "The plain text version of the email",
"metadata": {
"key_points_addressed": ["list of main points addressed"],
"skills_highlighted": ["list of skills mentioned"],
"resume_matches": ["specific experiences/skills from resume that match job requirements"],
"missing_recommended_info": ["optional fields that were missing but would strengthen the application if available"],
"tone_analysis": "brief analysis of the email's tone"
}
}
}
Critical required fields (will return error if missing):
- Job title
- Company name
- Job description
- Resume content
Recommended but optional fields:
- Hiring manager name
- Department
- Location
- Application instructions
- Referral source
- Required skills list
Please ensure all HTML in body_html is properly escaped for JSON and uses only basic formatting tags (p, br, b, i, ul, li) to ensure maximum email client compatibility.
`
The prompt does a few clever things:
-
Forces structured output – no wishy-washy responses
-
Tracks which of your skills match the job requirements
-
Identifies any missing info that could strengthen the application
-
Generates both HTML and plain text versions (because some job portals hate formatting)
And here’s the kicker – it fails fast if critical info is missing. No more generic “I saw your job posting” emails. Either the cover letter has substance, or it doesn’t get sent. Period.
(I start all all my prompts with ‘please’, so that when AI eventually takes over, they would consider me friendly 😁)
Step 6: Sending the Emails (The Moment of Truth)
Last step – actually sending these beautifully crafted applications. Sounds simple, right? Just hook up an email service and blast away?
Not so fast. I needed a way to:
-
Send professional-looking emails
-
Track what was actually sent
-
Monitor responses (can’t ghost the recruiters)
-
Not get flagged as spam (crucial!)
For testing, I sent all emails to a test account first. Pro tip: when you do send to actual recruiters, BCC yourself. Nothing worse than wondering “did that email actually go through?”
At this stage of the POC, I just used a simple email provider like Mailgun. Quick, dirty, but effective. Don’t worry – in Part 2, I’ll tell you about the rabbit hole I went down trying to build a full email management system. (Spoiler: it involves rejected AWS applications and a failed attempt at running my own email server. Good times.)
The Results
The proof of concept worked better than expected. I could take a job board, extract listings, parse them, and generate personalized applications – all with a few Python scripts.
But this was just the beginning. The real challenge? Turning these scripts into a proper application that could:
In Part 2, I’ll show you how I built the actual application, complete with all the technical decisions, trade-offs, and “what was I thinking” moments.
Stay tuned – it gets even better.
Want to know when Part 2 drops? Follow me on Twitter or LinkedIn. And yes, I’ll eventually tell you how I got a job offer before finishing this project. It’s a good story.