AI Resume Screener with Gemini
Build a Python tool that screens resumes against a job description using AI.
Introduction
30 Second Summary
Hiring managers often receive hundreds of resumes for a single job posting. Reading through each one manually is slow, inconsistent, and easy to get wrong.
In this project, you will build a command-line Python tool that uses Google Gemini to screen candidate resumes against a job description and produce a ranked shortlist with scores and hiring recommendations.
What You'll Build
You'll run your script, point it at a folder of resumes, and watch it print a ranked candidate shortlist in your terminal with AI-generated scores, matched skills, and a clear SHORTLIST/MAYBE/REJECT recommendation for each person.
By the end of this project, you'll have:
- A working resume screening tool that reads multiple candidate files from a folder and evaluates each one against a job description using the Gemini API.
- Consistent, machine-readable JSON output from the AI every single time, powered by structured outputs and a Pydantic data model you define.
- A ranked shortlist table printed in your terminal showing each candidate's score, matched skills count, and recommendation.
- Secret Mission: Add PDF resume parsing so your tool works with real-world resume files.
Are there any prerequisites?
No prior experience with AI APIs or resume screening is needed. This guide walks you through installing every tool from scratch, including Python, the Gemini SDK, and getting your free API key.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
Set Up Your Environment
You committed to building an AI resume screener. Now it is time to connect to the AI that powers it.
Your screening tool depends on Google's Gemini API to read resumes and judge candidate fit. In this step, you will install the two Python packages your project needs and get a free API key so your code can talk to Gemini.
In this step, get ready to:
- Install the google-genai and pydantic packages.
- Get a free Gemini API key from Google AI Studio.
- Verify your API connection works with a test call.
Install the google-genai and pydantic packages
Your project uses two packages. The google-genai package lets you call the Gemini API from Python. The pydantic package lets you define data models that force the AI to return consistent, structured responses.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type VS Code and press Enter to open it.
- Open the integrated terminal by pressing Ctrl+` (macOS and Windows).
- Install both packages by running this command:
pip install google-genai pydantic
You will see pip downloading and installing both packages along with their dependencies. This may take a minute.
- Confirm the SDK is ready by running this command:
python -c "from google import genai; print('SDK ready')"
You should see SDK ready printed in your terminal.
Seeing an error instead of SDK ready?
If you see ModuleNotFoundError, make sure the pip you ran belongs to the same Python installation you are invoking. Try pip3 install google-genai pydantic instead, or use python -m pip install google-genai pydantic to guarantee the right environment.
help me fix my google-genai import error
Get a free API key from Google AI Studio
Google AI Studio gives you a free API key with no credit card required. The free tier covers everything this project needs.
- Open your browser and go to aistudio.google.com.
- Sign in with your Google account.
- If you are a new user, Google AI Studio automatically creates a project and API key for you after you accept the Terms of Service. Copy the key from the API keys page.
- If you already have a Google Cloud account, click Get API key in the left sidebar, then click Create API key.
- Copy your new API key and save it somewhere safe (a notes app or text file on your machine). You will need it in the next substep.
Why is this free?
The Gemini API free tier has no charges and no billing account requirement. If you exceed the rate limits (10 requests per minute, 250 requests per day for Gemini 2.5 Flash), you receive a 429 error instead of a bill.
That is more than enough for this project.
Having trouble creating a key?
If you see a permissions error, try signing out and signing back in. If the issue persists, make sure you are using a personal Google account rather than a work or school account that may have restrictions.
help me get a Gemini API key from Google AI Studio
Set your API key and verify connectivity
Your Python code reads the API key from an environment variable called GEMINI_API_KEY. Setting it in your terminal keeps the key out of your source code.
- Set the environment variable by running this command in your VS Code terminal (replace your-api-key-here with the key you copied):
macOS
export GEMINI_API_KEY=your-api-key-here
Windows
set GEMINI_API_KEY=your-api-key-here
What does this command do?
The export (macOS) or set (Windows) command stores your key in the terminal session's memory. Your Python code can then read it with os.environ["GEMINI_API_KEY"] without the key ever appearing in a file you might share.
This variable lasts only for the current terminal session. If you close and reopen the terminal, you will need to set it again.
Now let's confirm your key actually works by making a real API call. Before you run the next command, what do you think will happen if your key is valid?
- Test your connection to the Gemini API by running this command:
macOS
python -c "
import os
from google import genai
client = genai.Client(api_key=os.environ['GEMINI_API_KEY'])
response = client.models.generate_content(
model='gemini-2.5-flash',
contents='Say hello in one sentence.'
)
print(response.text)
"
Windows
python -c "
import os
from google import genai
client = genai.Client(api_key=os.environ['GEMINI_API_KEY'])
response = client.models.generate_content(
model='gemini-2.5-flash',
contents='Say hello in one sentence.'
)
print(response.text)
"
You should see Gemini respond with a short greeting in your terminal. The exact words vary each time, but seeing any text response confirms your API key works and your connection to Gemini is live.
Seeing a KeyError or authentication error?
If you see KeyError: 'GEMINI_API_KEY', you did not set the environment variable in the same terminal session. Run the export or set command again in the same terminal where you run the Python command.
If you see a 403 or permission error, double-check that you copied the full key with no extra spaces.
help me fix my Gemini API key connection error
Your environment is ready. You have the SDK installed, a free API key connected, and proof that your code can talk to Gemini. Next up, you will feed it a real resume and see what the AI makes of it.
Analyze Your First Resume
Your environment is set up and connected to Gemini. Now you need to see what AI-powered resume analysis actually looks like before you can build a full screening pipeline around it.
In this step, you'll create sample resumes, write a script that feeds one to Gemini, and watch the AI produce a detailed candidate assessment in your terminal.
In this step, get ready to:
- Create sample resume text files for the AI to analyze.
- Write a script that sends a resume to Gemini with a job description.
- Run the script and see AI-generated analysis output.
Create the sample resume files
Your screening tool needs resumes to analyze. You'll create three sample candidates with different experience levels so you can see how the AI handles variety.
- Create a resumes folder in your project directory by running this command in your VS Code terminal:
mkdir resumes
- In the VS Code Explorer sidebar, right-click the resumes folder and select New File.
- Name the file alice_johnson.txt and paste in the following content:
Alice Johnson
Email: alice.johnson@email.com
Phone: +91 9876543210
SUMMARY
Full-stack developer with 4 years of experience building web applications using Python, Django, and React. Passionate about clean code and scalable systems.
EXPERIENCE
Senior Developer | TechCorp India | 2021 - Present
- Built REST APIs serving 10,000+ daily users using Django REST Framework
- Led migration from monolithic architecture to microservices
- Mentored 3 junior developers
Developer | StartupXYZ | 2019 - 2021
- Developed customer-facing dashboard using React and TypeScript
- Implemented CI/CD pipelines using GitHub Actions
- Reduced page load time by 40% through performance optimization
EDUCATION
B.Tech Computer Science | IIT Bombay | 2019
SKILLS
Python, Django, React, TypeScript, PostgreSQL, Docker, AWS, Git, REST APIs, Microservices
- Right-click the resumes folder again and create a second file named bob_smith.txt with this content:
Bob Smith
Email: bob.smith@email.com
Phone: +91 8765432109
SUMMARY
Recent graduate with internship experience in web development. Eager to learn and grow in a software engineering role.
EXPERIENCE
Intern | WebDev Solutions | Jan 2024 - June 2024
- Assisted in building landing pages using HTML, CSS, and JavaScript
- Fixed bugs in existing PHP codebase
- Participated in daily stand-up meetings
EDUCATION
B.Sc Computer Science | Mumbai University | 2024
SKILLS
HTML, CSS, JavaScript, PHP, MySQL, Basic Python, Git
- Create one more file named carol_davis.txt in the resumes folder with this content:
Carol Davis
Email: carol.davis@email.com
Phone: +91 7654321098
SUMMARY
Backend engineer with 6 years of experience specializing in Python microservices and cloud infrastructure. Strong focus on system design and performance.
EXPERIENCE
Lead Backend Engineer | CloudScale Inc | 2022 - Present
- Architected event-driven microservices handling 1M+ requests/day
- Designed and implemented distributed caching layer reducing latency by 60%
- Led team of 5 engineers across 2 time zones
Senior Python Developer | DataFlow Systems | 2019 - 2022
- Built data pipeline processing 50TB monthly using Python and Apache Kafka
- Implemented automated testing achieving 95% code coverage
- Reduced infrastructure costs by 30% through AWS optimization
Python Developer | CodeCraft | 2018 - 2019
- Developed REST APIs using Flask and SQLAlchemy
- Integrated third-party payment gateways
EDUCATION
M.Tech Software Engineering | IIIT Hyderabad | 2018
SKILLS
Python, Django, Flask, FastAPI, AWS, Docker, Kubernetes, Kafka, Redis, PostgreSQL, System Design, Microservices, CI/CD
- Confirm all three files exist by running:
ls resumes
You should see alice_johnson.txt, bob_smith.txt, and carol_davis.txt listed.
Why three different candidates?
Alice has 4 years of relevant Python experience. Bob is a fresh graduate with mostly web basics. Carol is a senior backend engineer with 6 years of deep Python work.
This spread lets you see how the AI distinguishes between a strong match, a weak match, and an overqualified candidate when you scale up later.
Folder not showing in Explorer?
Make sure your VS Code workspace is open to the correct project folder. If the resumes folder doesn't appear, click File then Open Folder and select the folder where you ran mkdir resumes.
Why isn't my resumes folder showing in VS Code?
Write the resume screening script
Now you'll write the Python script that reads a resume file, combines it with a job description, and sends both to Gemini for analysis.
- In the VS Code Explorer sidebar, click the new file icon at the top of the file tree.
- Name the file screen_resumes.py and paste in the following code:
import os
from google import genai
# Job description to match candidates against
JOB_DESCRIPTION = """
Senior Python Developer
We are looking for a Senior Python Developer with 3+ years of experience to join our backend team.
Required Skills:
- Python (3+ years)
- Django or Flask
- REST API development
- PostgreSQL or similar relational database
- Docker
- Git
Nice to Have:
- AWS or cloud experience
- Microservices architecture
- CI/CD pipelines
- Team leadership experience
Responsibilities:
- Design and build scalable backend services
- Write clean, tested, maintainable code
- Mentor junior developers
- Participate in system design discussions
"""
What does this code do?
This sets up the two inputs your AI needs. The genai import gives you access to the Gemini API client. The JOB_DESCRIPTION string is the benchmark your AI will score candidates against.
Think of it like handing a recruiter a job posting before they review any resumes.
- Add the following code below the JOB_DESCRIPTION string in screen_resumes.py:
# Read the contents of a resume file
def read_resume(file_path: str) -> str:
with open(file_path, "r", encoding="utf-8") as f:
return f.read()
# Create a Gemini client using your API key
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
# Load Alice's resume as a test
resume_text = read_resume("resumes/alice_johnson.txt")
# Build a prompt combining the job description and resume
prompt = f"""You are an expert HR recruiter. Analyze the following resume against the job description.
JOB DESCRIPTION:
{JOB_DESCRIPTION}
RESUME:
{resume_text}
Provide a detailed assessment of the candidate's fit for this role."""
# Send the prompt to Gemini and print the response
response = client.models.generate_content(model="gemini-2.5-flash", contents=prompt)
print(response.text)
What does this code do?
- read_resume() opens a text file and returns its contents as a string.
- genai.Client connects to the Gemini API using the environment variable you set in Step 1.
- The prompt f-string combines the job description and resume text into a single instruction for the AI.
- client.models.generate_content() sends the prompt to the gemini-2.5-flash model and returns the AI's response.
- Save screen_resumes.py.
✔️ Awesome, I've got everything!
Great. Make sure you've saved the file before running it in the next substep.
ⓧ I'd like to double check the full code
Your screen_resumes.py should have two sections in this order:
- The imports (os and genai) followed by the full JOB_DESCRIPTION string.
- The read_resume() function, then the client creation, resume loading, prompt building, API call, and print statement.
Scroll up and confirm both chunks are present in order. The file should start with import os and end with print(response.text).
Seeing a red underline on the import?
If VS Code shows a warning on from google import genai, make sure you installed the package in the same Python environment VS Code is using. Check the Python interpreter in the bottom-left of VS Code and ensure it matches the one where you ran pip install google-genai.
Help me fix the google-genai import error in VS Code
Run the script and observe the AI analysis
Your script is ready. Before you run it, think about what you expect the AI to say about Alice's resume for a Senior Python Developer role.
- Do you think the AI will rate Alice as a strong fit or a weak fit? Why?
- Run your script by entering this command in your terminal:
python screen_resumes.py
You should see a multi-paragraph analysis printed in your terminal. Gemini will assess Alice's skills, experience, and overall fit against the job description.
What should I see?
The output is free-text prose from the AI. It will likely mention Alice's 4 years of Python experience, her Django and REST API work, and her Docker and AWS skills. The exact wording will be different every time you run it.
This variability is the key observation. Run the script again and notice the response is worded differently. That inconsistency is the problem you'll solve in the next step.
- Run the script a second time:
python screen_resumes.py
Compare the two outputs. The content is similar, but the structure, wording, and emphasis change each time.
Seeing a KeyError or authentication error?
If you see KeyError: 'GEMINI_API_KEY', your environment variable is not set in this terminal session. Re-export it in the same terminal where you run the script.
If you see a 429 error, you've hit the free tier rate limit. Wait 60 seconds and try again.
Help me fix my Gemini API authentication error
You've just seen AI-powered resume screening in action. The analysis is impressive, but notice the problem: every response is structured differently. In the next step, you'll force Gemini to return consistent, machine-readable JSON using structured outputs.
Force Consistent JSON with Structured Outputs
In Step 2, you saw Gemini produce a multi-paragraph analysis of Alice's resume. That analysis was useful, but run the same script twice and you get two completely different responses.
Different wording, different structure, different level of detail every single time. That inconsistency makes it impossible to extract scores, compare candidates, or build any automation on top. Structured outputs solve this by forcing Gemini to return valid JSON matching an exact schema you define.
In this step, get ready to:
- Define a Pydantic data model with the exact fields you want from the AI.
- Update your Gemini call to force JSON output matching that model.
- Parse and display a validated, consistent evaluation every time.
Define a Pydantic model for evaluation results
Right now, Gemini returns free-form text. You need a strict contract that says exactly which fields the AI must return. Pydantic models let you define that contract in Python.
- In screen_resumes.py, replace the two import lines at the top of the file with these expanded imports:
import os
import json
from google import genai
from google.genai import types
from pydantic import BaseModel, Field
from typing import List
- Directly below the imports (above JOB_DESCRIPTION), add this class that defines your evaluation schema:
class CandidateEvaluation(BaseModel):
candidate_name: str = Field(description="Full name of the candidate")
relevance_score: int = Field(description="Score from 0-100 indicating how well the candidate matches the job requirements")
matched_skills: List[str] = Field(description="Skills from the job description that the candidate possesses")
missing_skills: List[str] = Field(description="Required skills from the job description that the candidate lacks")
experience_years: int = Field(description="Total years of relevant professional experience")
summary: str = Field(description="Brief 2-3 sentence assessment of the candidate's fit")
recommendation: str = Field(description="One of: SHORTLIST, MAYBE, or REJECT")
What does this code do?
Each field in CandidateEvaluation is a slot the AI must fill. The Field(description=...) tells Gemini what to put in each slot.
- relevance_score forces a numeric score you can sort on.
- matched_skills and missing_skills give you lists you can count and compare.
- recommendation constrains the AI to one of three values: SHORTLIST, MAYBE, or REJECT.
- Save screen_resumes.py.
Seeing a red underline on List?
Make sure you added from typing import List in the imports. Without it, Python does not know what List[str] means.
help me fix import errors in my Pydantic model
Update your Gemini call to use structured outputs
With the schema defined, you can now tell Gemini to return JSON that matches it exactly. The google-genai SDK supports passing a Pydantic class directly as the response_schema.
- In screen_resumes.py, find the read_resume function. Add this new evaluate_candidate function directly below it (above the existing client = genai.Client(...) line):
def evaluate_candidate(client: genai.Client, resume_text: str, job_description: str) -> CandidateEvaluation:
prompt = f"""You are an expert HR recruiter. Analyze the following resume against the job description and provide a structured evaluation.
JOB DESCRIPTION:
{job_description}
RESUME:
{resume_text}
Evaluate the candidate's fit for this role. Be specific about matched and missing skills. Score from 0-100 where 70+ means shortlist-worthy."""
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=prompt,
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=CandidateEvaluation,
),
)
result = CandidateEvaluation.model_validate_json(response.text)
return result
What does this code do?
This function wraps the entire evaluation into one reusable call.
- response_mime_type="application/json" tells Gemini to output raw JSON instead of prose.
- response_schema=CandidateEvaluation passes your Pydantic class so Gemini knows the exact shape to follow.
- CandidateEvaluation.model_validate_json(response.text) parses the JSON string and validates it against your schema, catching any malformed output.
- Save screen_resumes.py.
Red underline on types.GenerateContentConfig?
Check that your imports include from google.genai import types. The types module holds configuration classes like GenerateContentConfig.
help me fix import or config errors in my evaluate_candidate function
Run the script and verify structured JSON output
The final piece is replacing the old free-text code at the bottom of the file with a call to your new function.
- In screen_resumes.py, find and delete everything below the read_resume function AND the new evaluate_candidate function. This is the old code that starts with client = genai.Client(...) and ends with print(response.text).
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
resume_text = read_resume("resumes/alice_johnson.txt")
prompt = f"""You are an expert HR recruiter. Analyze the following resume against the job description.
JOB DESCRIPTION:
{JOB_DESCRIPTION}
RESUME:
{resume_text}
Provide a detailed assessment of the candidate's fit for this role."""
response = client.models.generate_content(model="gemini-2.5-flash", contents=prompt)
print(response.text)
- Replace the deleted code with this if __name__ block:
if __name__ == "__main__":
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
resume_text = read_resume("resumes/alice_johnson.txt")
evaluation = evaluate_candidate(client, resume_text, JOB_DESCRIPTION)
print(evaluation.model_dump_json(indent=2))
What does this code do?
The if __name__ == "__main__" block runs only when you execute the script directly. It creates the API client, reads Alice's resume, evaluates it through your structured function, and prints the result as pretty-printed JSON.
model_dump_json(indent=2) serializes the validated Pydantic object back to a formatted JSON string for easy reading.
- Save screen_resumes.py.
Before you run this, what do you expect the output to look like? Will it be free-form paragraphs like before, or something different?
- Run your updated script by running this command:
python screen_resumes.py
You should see a clean JSON object printed in your terminal with exact fields: candidate_name, relevance_score (an integer), matched_skills (a list), missing_skills (a list), experience_years, summary, and recommendation (one of SHORTLIST, MAYBE, or REJECT).
Run it a second time. The scores may shift slightly, but the structure is identical every time. That is the power of structured outputs.
Seeing a ValidationError or empty response?
- If you see ValidationError, the API returned JSON that did not match your schema. Double-check that response_schema=CandidateEvaluation uses the class name (no parentheses, no quotes).
- If you see a KeyError on GEMINI_API_KEY, make sure you still have the environment variable set in your terminal from Step 1.
- If you see a 429 error, you have hit the free tier rate limit. Wait 60 seconds and try again.
help me debug my structured output Gemini call
✔️ Awesome, I've got everything!
Great. Make sure you have saved screen_resumes.py with all your changes.
ⓧ I'd like to double check the full code
Your screen_resumes.py should contain these sections in order. Scroll up and confirm each is present:
- Imports: os, json, genai, types, BaseModel, Field, List.
- CandidateEvaluation class with 7 fields (candidate_name through recommendation).
- JOB_DESCRIPTION multi-line string (unchanged from Step 2).
- read_resume function (unchanged from Step 2).
- evaluate_candidate function with response_mime_type, response_schema, and model_validate_json.
- if __name__ == "__main__" block calling evaluate_candidate and printing with model_dump_json(indent=2).
You now have a reliable, repeatable evaluation pipeline. Next up, you will screen multiple candidates at once and rank them into a sorted shortlist.
Screen Multiple Candidates and Rank Them
Your screening tool currently evaluates one resume at a time. A real hiring pipeline needs to process an entire stack of resumes and produce a ranked comparison so you can see who stands out.
In this step, you'll build a batch processing function that loops through every resume in your folder, evaluates each one, and prints a sorted shortlist. The top candidate appears first.
In this step, get ready to:
- Write a batch screening function that processes all resumes in a folder.
- Add a formatted shortlist printer that ranks candidates by score.
- Run the pipeline against all three sample resumes and see the ranked output.
Write a function to screen all resumes in a folder
Right now your script hard-codes the path to a single resume. You need a function that discovers every .txt file in the resumes folder, evaluates each one, and returns the results sorted by score.
- In screen_resumes.py, find the if __name__ == "__main__": block at the bottom of your file.
- Add the following function directly above that if __name__ block:
def screen_all_resumes(resumes_folder: str, job_description: str) -> List[CandidateEvaluation]:
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
evaluations = []
for filename in os.listdir(resumes_folder):
if filename.endswith(".txt"):
file_path = os.path.join(resumes_folder, filename)
print(f"Screening: {filename}...")
resume_text = read_resume(file_path)
evaluation = evaluate_candidate(client, resume_text, job_description)
evaluations.append(evaluation)
evaluations.sort(key=lambda e: e.relevance_score, reverse=True)
return evaluations
What does this code do?
- os.listdir(resumes_folder) gets every file name in the folder. The if filename.endswith(".txt") filter ensures you only process text resumes.
- Each resume is read, sent to Gemini via evaluate_candidate(), and the structured result is appended to the list.
- evaluations.sort(key=lambda e: e.relevance_score, reverse=True) sorts candidates from highest score to lowest, so the best fit appears first.
- Save screen_resumes.py.
Print a formatted shortlist and run the full pipeline
The batch function returns a sorted list of evaluations. Now you need a display function that prints a readable table showing each candidate's rank, score, matched skills, and recommendation.
- Add the following function directly below screen_all_resumes(), above the if __name__ block:
def print_shortlist(evaluations: List[CandidateEvaluation]) -> None:
print("\n" + "=" * 60)
print("CANDIDATE SHORTLIST - Ranked by Relevance Score")
print("=" * 60)
for rank, evaluation in enumerate(evaluations, 1):
print(f"\n{'─' * 60}")
print(f"Rank #{rank}: {evaluation.candidate_name}")
print(f"Score: {evaluation.relevance_score}/100 | Recommendation: {evaluation.recommendation}")
print(f"Experience: {evaluation.experience_years} years")
print(f"Matched Skills: {', '.join(evaluation.matched_skills)}")
print(f"Missing Skills: {', '.join(evaluation.missing_skills) if evaluation.missing_skills else 'None'}")
print(f"Summary: {evaluation.summary}")
print(f"\n{'=' * 60}")
shortlisted = [e for e in evaluations if e.recommendation == "SHORTLIST"]
print(f"Total Candidates: {len(evaluations)} | Shortlisted: {len(shortlisted)}")
print("=" * 60)
What does this code do?
- enumerate(evaluations, 1) gives each candidate a rank number starting at 1 (since the list is already sorted by score).
- Each candidate gets a block showing their score, recommendation, experience, and skill matches.
- The final summary line counts how many candidates received a SHORTLIST recommendation out of the total pool.
Now update the main block so it calls both new functions instead of evaluating a single resume.
- Find the existing if __name__ == "__main__": block at the bottom of your file. It currently looks like this:
if __name__ == "__main__":
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
resume_text = read_resume("resumes/alice_johnson.txt")
evaluation = evaluate_candidate(client, resume_text, JOB_DESCRIPTION)
print(evaluation.model_dump_json(indent=2))
- Replace the entire contents of that block (everything indented under if __name__) with this:
if __name__ == "__main__":
evaluations = screen_all_resumes("resumes", JOB_DESCRIPTION)
print_shortlist(evaluations)
What changed?
Instead of creating a client and evaluating one file directly, the main block now delegates everything to screen_all_resumes() (which handles the client, looping, and sorting) and print_shortlist() (which handles display).
- Save screen_resumes.py.
Before you run this, think about what you expect to see. The script will call Gemini three times (once per resume). Do you think the scores will be similar, or will there be a clear gap between candidates?
- Run your complete screening pipeline by running this command:
python screen_resumes.py
You should see progress messages as each resume is processed, followed by a formatted table showing all three candidates ranked by score, highest first.
What should I see?
Your terminal prints "Screening: alice_johnson.txt...", "Screening: bob_smith.txt...", and "Screening: carol_davis.txt..." as each resume is sent to Gemini. This takes a few seconds per resume.
After processing, a ranked table appears. Carol (6 years Python backend) and Alice (4 years full-stack) should score highest and receive SHORTLIST recommendations. Bob (recent graduate, internship only) should score lowest and receive MAYBE or REJECT.
Script not running or showing errors?
- If you see NameError: name 'screen_all_resumes' is not defined, make sure you added the function above the if __name__ block, not inside it.
- If you see a 429 error, you've hit the free tier rate limit (10 requests per minute). Wait 60 seconds and try again.
- If no resumes are found, confirm your resumes folder is in the same directory as screen_resumes.py and contains .txt files.
Help me debug my resume screening script
✔️ Awesome, I've got everything!
Great. Double check you've saved screen_resumes.py and your ranked shortlist printed successfully.
ⓧ I'd like to double check the full code
Here is the complete screen_resumes.py file. Scroll through and confirm each section is present in order: imports, CandidateEvaluation model, JOB_DESCRIPTION, read_resume, evaluate_candidate, screen_all_resumes, print_shortlist, and the main block.
- Imports and CandidateEvaluation model (from Step 3).
- JOB_DESCRIPTION string (from Step 2).
- read_resume() and evaluate_candidate() functions (from Steps 2 and 3).
- screen_all_resumes() function (added in this step).
- print_shortlist() function (added in this step).
- Updated if __name__ == "__main__": block calling both new functions.
Your resume screener is fully operational. You have a complete pipeline that reads resumes, sends them to an AI for structured analysis, and produces a ranked comparison. Next up, you can tackle the Secret Mission to make your tool handle real-world PDF resumes.
Secret mission
Add PDF Resume Parsing
Real resumes arrive as PDFs, not plain text files. Can you extend your screening tool to parse PDF resumes and include them in the ranked shortlist alongside your text-file candidates?
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely on your local machine and uses the Gemini API free tier. There are no ongoing costs or charges to worry about.
Decide whether to keep your resources active, pause them to come back later, or delete them entirely.
Resources you used:
- Google AI Studio API key (free tier, no billing attached).
- Python packages installed locally (google-genai, pydantic, pdfplumber).
- Project files: screen_resumes.py and the resumes/ folder on your Desktop.
- GEMINI_API_KEY environment variable set in your terminal session.
Keep everything running
No action needed. Choose this if you plan to keep experimenting with resume screening or want to reuse your Gemini API key for other projects.
- Your API key stays active on the free tier with no charges.
- Your project files remain on your Desktop ready to run again.
- The installed Python packages stay available for future scripts.
Pause - I'll come back to this later
Free up your terminal session while keeping your files and API key intact. Since the free tier has no charges, pausing is mostly about clearing your environment variable.
- Close the terminal session where you set GEMINI_API_KEY. The variable only exists in that session, so closing it clears it automatically.
- Your project files and installed packages remain in place for next time.
- When you return, set the environment variable again before running your script.
Delete - I don't want to use this again
Remove all project resources and revoke your API key. Follow each step below to clean up completely.
- Delete your API key by opening the Google Cloud API Credentials page at console.cloud.google.com/apis/credentials. Find your Gemini API key, click the Actions icon next to it, then select Delete API key. Confirm deletion in the Delete credential modal by clicking Delete.
- Uninstall the Python packages by running this command in your terminal:
pip uninstall google-genai pydantic pdfplumber -y
- Delete your project files from the Desktop. On macOS or Linux:
cd ~/Desktop
rm -rf resumes screen_resumes.py
- On Windows, run this in PowerShell instead:
cd ~\Desktop
Remove-Item -Recurse -Force resumes, screen_resumes.py
Nice Work!
Nice Work!
You just built a working AI-powered resume screening system using Google Gemini. From a blank script to a ranked candidate shortlist, your tool now does in seconds what takes a human recruiter hours.
You've learned how to:
- Call an AI API from Python and use structured outputs to guarantee consistent, machine-readable JSON from an LLM every time.
- Design a scoring schema with Pydantic data models that validates AI responses into typed Python objects.
- Process multiple files in batch and produce a ranked shortlist with scores, matched skills, and hiring recommendations.
- Secret Mission: Added PDF parsing to handle real-world resume formats alongside plain text files.
Ready to quiz yourself?