# How ATS Checking Verifies CV Parsing for Applicant Tracking Systems

> Learn how ATS checking verifies CV parsing for applicant tracking systems by extracting hidden text, validating content, and confirming keyword visibility for successful job applications.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: how-to-guide
- Published: 2026-08-05

---

**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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/apply.md) (lines 251-281) and executes during **step 5d – ATS & keyword verification (CV)**:

```bash
pdftotext -layout cv/main_example.pdf -  # outputs clean text to stdout

```

Install the optional dependency via:

```bash

# 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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/verify_pdf.py) (lines 46-68) enforces three critical checks:

1. **Page count validation** via `pdfinfo` — confirms exact length (typically 2 pages)
2. **Text layer completeness** — verifies non-empty extraction, minimum character thresholds
3. **Required content presence** — ensures contact details appear verbatim without corruption

The function raises `VerificationError` immediately upon any failure:

```python
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:

```bash
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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.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:

```python
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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/verify_pdf.py)** — Core verification script implementing `verify_pdf()` with page count, text layer, and content validation
- **[`.claude/commands/apply.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/apply.md)** — Defines the `/apply` workflow including step 5d ATS verification
- **[`.claude/skills/job-application-assistant/05-cv-templates.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-application-assistant/05-cv-templates.md)** — ATS-parseability guidelines, contact-detail rules, and keyword matching strategy
- **[`SETUP.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SETUP.md)** — Installation instructions for optional `pdftotext` dependency

## Summary

- **ATS checking validates the PDF text layer**, not visual rendering, using `pdftotext -layout` extraction
- **[`tools/verify_pdf.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/verify_pdf.py)** enforces page count, minimum content length, and required string presence through `verify_pdf()`
- The **`/apply` command** 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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/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`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.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.