Why 99% of resumes get rejected by ATS bots - and how I built a fix with Gemini AI
If you've been applying to tech jobs recently, you know the feeling. You spend hours tweaking your resume, hit submit, and within 24 hours, you receive an automated "we have decided to move forward with other candidates" email.
It feels personal, but it's not. Your resume probably never even reached a human. It was filtered out by an Applicant Tracking System (ATS).
Today, I’m going to explain exactly why this happens, how these bots actually work, and how I built a robust software solution using Google's Gemini AI to beat them at their own game.
The ATS Black Box
Most developers think of an ATS as a simple keyword matcher. While keywords are important, modern ATS platforms (like Workday, Greenhouse, or Lever) do semantic parsing. They don't just look for the word "React"; they try to understand the context. Did you use React for 3 years, or did you just list it in a generic skills section?
Here’s why most resumes fail:
- Formatting Nightmares: Multi-column layouts, weird fonts, and graphics break ATS parsers. The bot reads it as garbled text.
- Missing Contextual Keywords: The job asks for "Cloud Infrastructure." You wrote "AWS and GCP." A human knows what that means; a poorly configured ATS might not.
- Weak Action Verbs: Bots are programmed to look for impact. Starting bullet points with "Responsible for..." scores significantly lower than "Architected..."
Building the Fix
I realized that the only way to consistently beat an ATS is to analyze resumes the exact same way an ATS does—using Natural Language Processing (NLP).
I decided to build a tool that acts as a "pre-ATS parser." It reads a user's resume, reads the target job description, and outputs an actionable score.
To do this, I needed an LLM that was fast, had a massive context window (resumes can get long), and didn't cost a fortune per API call. Enter Gemini AI.
The Architecture
Here is the flow of the application:
-
PDF Extraction: The user uploads a PDF. The frontend parses the text using
pdf.js. - The Prompt Construction: We combine the parsed resume text with the job description.
- The Gemini API Call: We instruct Gemini to act as a strict ATS and return a structured JSON response evaluating the match.
- The UI: We render the JSON into a beautiful dashboard showing missing skills, formatting errors, and suggested rewrites.
The Code: Talking to Gemini
The magic happens in how you structure the prompt. If you just ask an LLM, "is this resume good?", you get vague garbage. You need to enforce structure.
Here is a snippet of how I implemented the Gemini call in a Next.js API route:
import { GoogleGenerativeAI } from '@google/generative-ai';
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);
export async function POST(req: Request) {
const { resumeText, jobDescription } = await req.json();
const model = genAI.getGenerativeModel({
model: 'gemini-1.5-pro-latest',
// We can enforce JSON output
generationConfig: { responseMimeType: "application/json" }
});
const prompt = `
You are a strict Applicant Tracking System (ATS).
Analyze this resume against the job description.
Resume: ${resumeText}
Job Description: ${jobDescription}
Respond STRICTLY with a JSON object containing:
{
"matchScore": number (0-100),
"missingHardSkills": string[],
"formattingIssues": string[],
"actionableAdvice": string[]
}
`;
try {
const result = await model.generateContent(prompt);
const response = await result.response;
const jsonOutput = JSON.parse(response.text());
return Response.json(jsonOutput);
} catch (error) {
return Response.json({ error: 'Failed to analyze resume' }, { status: 500 });
}
}
Notice the use of responseMimeType: "application/json". This feature of the Gemini API is a lifesaver. It guarantees that the LLM will output valid JSON, which means we can directly consume it in our frontend without writing complex regex parsers.
The Result
By feeding resumes through this Gemini-powered evaluator before submitting them, users were able to increase their keyword match rate significantly. The tool highlights exactly which phrases to swap out.
Building AI tools like this isn't magic; it's just tying together the right APIs. The hard part is usually setting up the boilerplate: the auth, the database, the UI components, and the payment gateways.
If you are a developer looking to build your own AI tools—whether it's an ATS scanner, a content generator, or a coding assistant—you shouldn't start from npm create next-app.
I’ve bundled the exact Next.js, Supabase, Stripe, and Gemini architecture I used for this project into a production-ready boilerplate.
🛠️ Stop writing auth and payment code. Start building your product: Get the Complete AI SaaS Boilerplate for $99
Top comments (0)