How ATS Checking Verifies CV Parsing for Applicant Tracking Systems
ATS checking ensures a PDF résumé can be correctly parsed by extracting the hidden text layer, validating page count and content integrity, and confirming keyword visibility before submission.
The MadsLorentzen/ai-job-search repository implements a three-stage verification workflow that mirrors how actual ATS platforms process résumés. Rather than relying on visual appearance, the system validates the machine-readable text layer—the same data employers' parsing engines consume. This prevents silent rejections caused by garbled Unicode, missing contact details, or improperly embedded fonts.
How the ATS Verification Pipeline Works
The verification process runs automatically during the /apply command workflow or can be executed standalone via tools/verify_pdf.py. It operates on a core principle: what humans see on screen differs from what ATS parsers read.
Stage 1: Extract the PDF Text Layer with pdftotext
The system invokes the poppler utility pdftotext with the -layout flag to extract embedded text while preserving visual structure. This mimics commercial ATS behavior—parsers access the hidden text stream, not rendered pixels.
The extraction step is defined in the /apply command workflow at .claude/commands/apply.md (lines 251-281) and executes during step 5d – ATS & keyword verification (CV):
pdftotext -layout cv/main_example.pdf - # outputs clean text to stdout
Install the optional dependency via:
# macOS
brew install poppler
# Debian/Ubuntu
sudo apt install poppler-utils
Stage 2: Validate Structural and Content Integrity
The verify_pdf() function in tools/verify_pdf.py (lines 46-68) enforces three critical checks:
- Page count validation via
pdfinfo— confirms exact length (typically 2 pages) - Text layer completeness — verifies non-empty extraction, minimum character thresholds
- Required content presence — ensures contact details appear verbatim without corruption
The function raises VerificationError immediately upon any failure:
from pathlib import Path
from tools.verify_pdf import verify_pdf, VerificationError
pdf_file = Path("cv/main_example.pdf")
try:
verify_pdf(
pdf_file,
expected_pages=2, # enforce exact page count
min_chars=1, # non-empty text layer
required_text=[
"your.email@domain.com",
"+123 456 7890",
],
)
print("ATS-ready PDF")
except VerificationError as err:
print(f"ATS verification failed: {err}")
Run verification directly from command line:
python3 tools/verify_pdf.py cv/main_example.pdf \
--pages 2 \
--min-chars 1 \
--contains "your.email@domain.com" \
--contains "+123 456 7890"
Stage 3: Confirm Keyword Coverage for ATS Matching
After confirming parseability, the framework scores keyword alignment against the job posting. According to .claude/skills/job-application-assistant/05-cv-templates.md (lines 230-247), matching uses literal string comparison to reflect actual ATS behavior—engines check for exact term presence, not semantic similarity.
Only keywords supported by verified candidate data are injected into the CV. This prevents "keyword stuffing" that triggers rejection by sophisticated ATS filters.
Integration with the Job Application Workflow
The /apply command orchestrates full ATS compliance before any submission:
def apply_job():
compile_cv() # lualatex → cv/main_example.pdf
if not run_ats_check("cv/main_example.pdf"):
raise RuntimeError("CV failed ATS verification")
# Workflow halts; user must fix template issues
score_keywords() # only runs after parseability confirmed
# Continues to cover-letter generation...
This enforcement guarantees that garbled characters ((cid:…) markers), missing contact fields, or font encoding errors are caught during development—not after application submission.
Common ATS Parsing Failures Detected
The verification system specifically targets these real-world rejection causes:
| Failure Mode | Detection Method | Source Location |
|---|---|---|
| Corrupted Unicode glyphs | (cid:…) pattern scanning |
tools/verify_pdf.py text analysis |
| Empty or truncated text layer | min_chars threshold check |
verify_pdf() parameter validation |
| Missing required contact details | required_text string matching |
verify_pdf() content verification |
| Incorrect page length | pdfinfo page count extraction |
verify_pdf() structural check |
| Invisible hyperlink content | Extraction without -raw flag behavior |
pdftotext -layout configuration |
Key Implementation Files
tools/verify_pdf.py— Core verification script implementingverify_pdf()with page count, text layer, and content validation.claude/commands/apply.md— Defines the/applyworkflow including step 5d ATS verification.claude/skills/job-application-assistant/05-cv-templates.md— ATS-parseability guidelines, contact-detail rules, and keyword matching strategySETUP.md— Installation instructions for optionalpdftotextdependency
Summary
- ATS checking validates the PDF text layer, not visual rendering, using
pdftotext -layoutextraction tools/verify_pdf.pyenforces page count, minimum content length, and required string presence throughverify_pdf()- The
/applycommand integrates verification at step 5d, halting workflow on any parseability failure - Keyword coverage scoring uses literal matching to simulate actual ATS behavior
- Optional poppler utilities provide production-grade extraction matching commercial parser capabilities
Frequently Asked Questions
What makes a PDF fail ATS verification in this system?
A PDF fails when verify_pdf() detects page count mismatches, empty or truncated text layers below min_chars, missing required_text strings, or corrupted Unicode sequences like (cid:…) markers. These conditions trigger VerificationError with specific failure details.
Is the pdftotext dependency mandatory for ATS checking?
No—pdftotext is optional but strongly recommended. Without it, the system cannot perform text-layer extraction and validation. Install via brew install poppler (macOS) or apt install poppler-utils (Debian/Ubuntu) as documented in SETUP.md.
How does keyword verification differ from standard spell checking?
Keyword verification confirms that job-posting terms appear exactly in the CV text layer, using literal string matching per .claude/skills/job-application-assistant/05-cv-templates.md (lines 230-247). Unlike spell checking, it validates strategic term placement for ATS ranking algorithms, not linguistic correctness.
Can I run ATS verification without using the full /apply command?
Yes—execute python3 tools/verify_pdf.py path/to/cv.pdf with --pages, --min-chars, and --contains arguments. Programmatic use via from tools.verify_pdf import verify_pdf is also supported for custom workflows.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →