How to Build a Job Hunt System with Claude Code

July 8, 2026·20 min read·AI · Job Search · Automation · Claude Code · Career · Python · Technical · Tutorial

The follow-up to my job search automation post, written so non-technical readers can actually build one: the repo structure, the CLAUDE.md that holds the scoring rubric, the public ATS APIs that replace scraping, and a month of real usage data, run times, token costs, and which model to actually run it on.

I'm Scotty Peterson. I spent the last few years building lifecycle marketing automation at a B2B SaaS company: the email systems, API integrations, and internal tooling that make campaigns actually run. I got laid off on June 1, and since my day job was already building automated systems with AI, I pointed those same tools at the job search itself. It started small, just pasting job postings into Claude Code with a bare CLAUDE.md for tracking, no rubric, no watchlist, no automation yet. All told, across both eras, it's evaluated 268 roles, helped me send 44 applications, and landed 4 interviews. These days it runs on its own every morning in about 9 minutes.

A lot of people asked how it works after my last post took off on LinkedIn, so this is the write-up: the repo structure, how the AI knows what a good job looks like, how boards get checked without scraping, and how it all gets tracked. You can build a minimal version in an afternoon.

THIS POST IS LONG ON PURPOSE. If you hand it to an AI agent, it has real context to work with, but read it yourself first. And if it's useful, share it with a friend who's job searching too.

One more thing up front: my actual repo isn't public right now, it's specific to me, my resume, my target companies, my rubric, and that specificity is the whole point. This post isn't something to clone. Treat it as a rubric or guideline for building your own workflow, your own system, tailored to your own situation. That's where the power actually comes from. I may build something public down the line, but I'm not sure yet what that would even look like.

The short version: my job search is a git repo. My resume, a scoring rubric, a list of target companies, and a few CSVs live in version control. Claude Code operates on that repo like a codebase, a scheduled agent runs the search daily and commits the results, and there's a digest waiting for me every morning. Any coding agent with file access and web search can run this, Claude Code is what I use daily, but nothing here is locked to one tool.

Prerequisites

You do not need to be a developer to build this (though it helps). You never write code yourself; the agent writes the one script this system uses. If you can install an app, keep files in folders, and understand the basics of AI, and can learn/know the very basics of git/Github, you have the skills. You need a few things:

  • Claude Code with a Claude Pro subscription ($20/month). This is the engine. Claude Code is Anthropic's AI agent: you type plain English ("score this job description against my resume") and it reads your files, does the work, and writes the results back. Install it at claude.com/claude-code.
  • An IDE: VS Code or Cursor. Not strictly necessary, since Claude Code runs in a terminal, but highly recommended. An IDE is just an app for viewing and editing files, and you'll want one for reading digests, reviewing tailored resumes, and glancing at your trackers. VS Code is free; Cursor has a free tier and runs Claude Code in a sidebar so you may never touch a terminal at all.
  • A GitHub account (free). GitHub stores your repo online, which gives you a backup, a full history of every change, and, if you later want the daily run to happen without you, a place a cloud agent can pull the repo from.
  • Python (free), if you want the board-checking script to run. Mac doesn't actually ship this anymore the way people remember. There's a stub at /usr/bin/python3 that prompts you to install Apple's Xcode Command Line Tools the first time you run it, and even then it's an old version. Just grab a current copy directly from python.org instead. You won't be writing any Python yourself, your agent writes the script, you just need Python installed so it can actually run.
  • A basic sense of Markdown and CSV. These are the two file types the whole system runs on. Markdown (.md) is just plain text with a few symbols for formatting, a # makes a heading, a - makes a bullet, that's most of what you need to recognize. CSV (.csv) is a spreadsheet saved as plain text, one row per line, values separated by commas, which is exactly why it opens cleanly in Excel or Google Sheets but is still something your agent can read and edit directly. You don't need to write either by hand. You just need to recognize them enough to open a file and sanity-check what the agent wrote.

Why a Repo and Not a Spreadsheet

Three reasons.

Context persists. Every session, the agent reads the same resume, the same rubric, the same trackers. It never starts from zero, and it never drifts from how I scored jobs last week.

Everything is plain text. Markdown and CSV. The agent can read and write all of it natively, you can diff changes, and nothing is trapped inside a SaaS tool.

Git is the audit log. Every scored job, every digest, every tracker update is a commit. If the automation does something weird, I can see exactly what changed and revert it.

If you're non-technical, git and GitHub are probably the biggest hurdle in this whole setup, more than the AI part. AI is only as good as the person steering it, so it's worth spending an hour on the basics: what a commit is, what a push is, how to undo one if you mess it up. You don't need to be fluent, but understanding the fundamentals means you'll actually trust the audit log instead of being afraid of it.

Repo Structure

hunter/
├── CLAUDE.md              # Instructions the agent reads every session (the brain)
├── profile/
│   ├── og-resume.md       # Master resume, single source of truth
│   ├── linkedin-profile.md # All the copy from my LinkedIn profile
│   └── projects.md        # Projects I've worked on
├── data/
│   ├── evaluated-jobs.csv # Every job ever scored (the dedupe backbone)
│   ├── watchlist.json     # Target companies + which ATS they use
│   ├── outreach-tracker.csv # Every networking touch: who, when, exact message
│   └── volunteer-tracker.csv # Skills-based volunteering leads
├── scripts/
│   ├── check-boards.py    # Queries ATS APIs for watchlist companies
│   └── evaluated-jobs.py  # Dedupe helpers the automation calls
├── daily-digest/          # One markdown file per day: what to apply to
├── resumes/
│   └── <company>/         # jd.md, tailored resume, cover letter, exported PDFs
└── docs/                  # Strategy notes (networking playbook, this file)

The naming matters less than the separation: profile files the agent reads from, data files it writes to, and one instruction file that governs both.

CLAUDE.md: The File That Runs the Show

Claude Code automatically reads a file called CLAUDE.md at the root of the repo at the start of every session. This is where the entire system's judgment lives. Mine contains:

A target profile. Comp band, location constraints (remote US that can hire in my state, or hybrid near me), which role types are primary fit, which are secondary, and which to avoid entirely. This is what keeps a web search from surfacing senior Marketo admin jobs I'd never take.

A scoring rubric. Every job gets a 0-100 score against my master resume. Base score comes from role fit, then penalties apply: a cap if the role wants more years than I have, a deduction for each required platform I don't know, a deduction if the comp floor is way above my band (a sign the role is overleveled for me). AI-forward roles get a small bonus because that's what I'm optimizing for. The anchors are calibrated so 80+ means "natural hire, not a stretch," and any role with a gap that would realistically screen me out is capped below 75.

The exact numbers matter less than having them written down. Before the rubric existed, I'd just have the AI read my profile and the job description fresh each time and tell me how good a match it was. That actually worked okay, but it wasn't consistent, and AI has a real positivity bias: left to its own judgment, it wants to tell you every job is a great fit. Writing the rules down gives it something concrete to follow instead of vibes. Build a scoring system that reflects your actual gaps and priorities, and the agent scores a job the same way on day 25 as it did on day 1. When I disagree with a score now, I tune the rubric instead of re-arguing every job from scratch.

Workflow definitions. What to do when I paste a job description (check for duplicates, score it, log it, stop). What to do when I say "find jobs" (search, verify every link is a live direct posting, score, log, present ranked). What to do when I decide to apply (save the JD, tailor a copy of the resume, export a PDF with a consistent naming convention).

Guardrails. The master resume is never edited unless specifically requested, only copied. Tailoring means reordering and reframing what's true, never fabricating. Aggregator links don't count as verification; the agent has to find the live posting on the company's actual ATS. Scoring a job and applying to it are separate decisions, and the agent never marks anything as applied. I do that manually.

Writing voice rules. Instructions for how NOT to sound like AI in cover letters and application answers: no em dashes, no "Trigger: X. Audience: Y." labeled lists, no three-parallel-examples patterns, vary sentence length, lead with specifics. This section earns its keep every single time. I also run drafts through a dedicated humanizer skill (github.com/blader/humanizer), which is a free, downloadable Claude Code skill built specifically to catch these tells.

If you build nothing else from this post, build a CLAUDE.md with a target profile and a rubric. That alone turns a chat assistant into a consistent evaluator.

Profile Context: What the Agent Knows About You

Three files, three jobs:

  • og-resume.md is the master resume and the single source of truth. Every score is computed against it. Every tailored resume starts as a copy of it. It never gets edited, which means tailoring can't slowly corrupt the record of what I've actually done.
  • linkedin-profile.md is just all the copy from my actual LinkedIn profile, pasted in as one file. It gives the agent extra context beyond the one-page resume, and it doubles as a single place to edit if I come up with a better way to tell my story. Change it there once and it flows into every future cover letter instead of getting rewritten from memory each time.
  • projects.md is a bit more specific to me: real things I've built on the job (internal tools, a full SaaS product, a CMS) that aren't necessarily tied to the marketing roles I'm targeting, but show a different kind of range. It comes with a conditional: the agent only pulls from it when the job description actually signals it wants a builder. CLAUDE.md tells it when that file is relevant and when to leave it alone.

That last point is the general pattern: don't just give the agent context, give it rules for when each piece of context applies.

The APIs: Checking Job Boards Without Scraping

This is the part people assume requires scraping. It doesn't. Most tech companies run their careers page on one of a few Applicant Tracking Systems, and the major ones expose public JSON APIs that power their own job board widgets:

Greenhouse:  https://boards-api.greenhouse.io/v1/boards/<slug>/jobs
             (add /<job_id> for the full description)
Lever:       https://api.lever.co/v0/postings/<slug>?mode=json
Ashby:       https://api.ashbyhq.com/posting-api/job-board/<slug>
Workday:     https://<tenant>.<host>.myworkdayjobs.com/wday/cxs/<tenant>/<site>/jobs
             (POST, paginated 20 at a time)

Here's how you actually find which one a company uses, and it's the same move for all four: go to their careers page and look at where the "view open roles" button sends you. The domain gives it away.

  • boards.greenhouse.io/company or job-boards.greenhouse.io/company → Greenhouse. The company part of that URL is the slug.
  • jobs.lever.co/company → Lever, same idea.
  • jobs.ashbyhq.com/company → Ashby, same idea.
  • Something ending in myworkdayjobs.com, usually on the company's own domain → Workday. The tenant/host/site are all in that URL too, just messier to pull apart.

You don't have to go do this by hand either, and honestly I don't. It's part of the automation itself: when the daily search finds a company worth watching, the agent works out which platform it's on and what the slug is, and flags it as a watchlist suggestion in the digest with that info already filled in. I just review the suggestions and approve the ones worth adding. I never go looking for URLs myself.

Once you know that, the actual build is one conversation with your agent. You don't need to write or understand the script yourself, you just need to hand over the right ask. Something like: "Here are the public JSON API patterns for Greenhouse, Lever, Ashby, and Workday job boards [paste the four endpoints above]. Write me a script that checks a list of companies against these APIs, filters the results for job titles matching my target keywords, and skips anything I've already logged in evaluated-jobs.csv." That prompt, plus a few rounds of back and forth fixing edge cases, is genuinely how check-boards.py got built. It's a single Python file with no dependencies beyond what already ships with Python, so there's nothing extra to install.

watchlist.json is just a list of companies with their platform and slug:

[
  {"company": "Figma", "platform": "greenhouse", "slug": "figma"},
  {"company": "Klaviyo", "platform": "greenhouse", "slug": "klaviyo"}
]

Mine has nearly 100 companies on it, and it grows: the daily digest ends with a "watchlist suggestions" section, so when web search surfaces a company with a relevant team, it gets added and monitored from then on.

The script loops through the watchlist, hits each API, filters titles against keywords like "lifecycle," "marketing operations," and "martech," and diffs the results against evaluated-jobs.csv by both title and job ID. Output is only the roles I haven't seen before, each with its description attached so the agent can score it without another fetch.

Dedup: The Boring Part That's Actually the Hard Part

Every ATS API check and every web search candidate has to run past evaluated-jobs.py, and this is the one piece of the system I'd call genuinely engineered rather than just prompted. It matches on two separate keys, not one:

  • A normalized company and role name. Lowercased, punctuation stripped, leading "the" dropped, whitespace collapsed. That catches "The Acme Co." and "Senior Marketing Ops Manager" matching against "Acme Co" and "senior marketing ops manager," even when the formatting is slightly different between two listings of the same role.
  • A job ID pulled out of the URL. Different regex per platform: Greenhouse's gh_jid parameter or numeric /jobs/<id> path, or the UUID pattern Lever and Ashby both use. This is what catches the case the title match misses entirely: a company reposts the exact same job under a new title. Same posting, same URL, different name. The ID doesn't change even when the title does.

Both keys run through a batch mode: a whole list of candidates goes in as one piped command, and the script returns NEW or DUP for each line, catching duplicates against the CSV and duplicates within that same batch (two search results that turned out to be the same job) in a single pass. That's the mechanism that lets the agent dedupe 250+ logged roles without ever loading the CSV into its own context.

There's also a cleanup command that scans the whole CSV for rows that slipped through as duplicates anyway and merges them, and it has one deliberate rule: if two rows match but only one has Applied = Yes, it keeps that one. A naive cleanup that just kept "the first row it saw" could quietly erase the fact that I actually applied to something. Small rule, but it's the kind of edge case that only shows up after you've been running the system for weeks and looking closely at what it removed.

Two more lessons from running this for a month:

  • Duplicates are still duplicates when nobody actually moved the ID. Job aggregators and even ATS platforms will surface a slightly reworded version of a listing that maps to the same underlying job. The ID-based key exists specifically because title-only matching would let that slip through.
  • Some sites can't be scripted, and that's fine. Indeed's careers page blocks scripts at the TLS level. Instead of fighting it, that one check runs as a browser-capable agent task with its own prompt file, and it appends findings to a log that a regular session scores later. When a source resists automation, route around it instead of building something fragile.

Data Tracking

Four files, all plain CSV or markdown.

evaluated-jobs.csv is the backbone. Every job that has ever been scored gets a row: company, role, base score, each penalty applied, final score, whether I applied, platform, URL, and where it was found (API check, web search, or pasted by me). 250+ rows and counting. Its most important function is negative: it's the memory that prevents the system from surfacing the same job twice. Every workflow checks it before scoring anything.

outreach-tracker.csv logs every networking touch: date, person, company, platform, status, follow-up date, the exact message sent, and notes on who they are. I'm logging the exact message sent because I figure I can gather data on what works.

job-tracker.csv tracks application status: Applied, Interview, Offer, Rejected, Withdrawn, with a separate "Reached Interview" yes/no column. That separate column exists because a status field alone loses information: "rejected after two interviews" and "rejected by the ATS filter in an hour" are wildly different signals, and the second column preserves the difference. That's the level of detail you need if you ever want to actually compute your funnel conversion rates instead of guessing at them.

daily-digest/ gets one markdown file per automation run: new roles ranked by score with links and one-line fit notes, everything logged below the threshold, and a "skipped" section listing every dead link, duplicate, and ineligible role with the reason. The skipped section looks like bureaucracy but it's the trust mechanism. When the digest says a role was dropped because its remote policy excludes my state, I don't have to wonder whether the automation is silently eating good jobs.

The Daily Automation

First, a reassurance: the scheduled part is optional. Once everything above is set up, you can run the whole daily search manually by opening the repo and pointing your agent at the automation prompt. Same search, same digest, and you skip the cloud setup entirely. The schedule only changes when it happens, not what happens. I'd actually recommend running it manually for the first week regardless, because you want to catch the rubric's bad calls while you're still watching.

The scheduled version runs as a recurring background cloud agent that pulls the repo from GitHub each morning and runs the prompt, Cursor's cloud agents and Claude Code's cloud routines both support this. Or you can skip clouds entirely and just make it your own morning coffee ritual. Whatever runs it, the prompt walks through six steps:

  1. Get today's date.
  2. Run check-boards.py and score whatever's new.
  3. Web search for up to five new roles, filter on snippets before fetching anything, pipe every surviving candidate through a single batched dedupe script call (rather than reading the whole, growing CSV into context), then verify each survivor with at most one fetch (preferring the ATS JSON APIs above).
  4. Score everything against the rubric and append to the CSV in one batched write.
  5. Write the daily digest.
  6. Dedupe, commit, push.

The prompt embeds the full rubric and target profile so the run is self-contained, and it's explicit about efficiency: dedupe in one batched call instead of once per role, one fetch max per candidate, don't explore the repo, and never pad with weak matches to hit the quota ("fewer is fine" is written into it, because an agent told to find five roles will otherwise find five roles whether they exist or not).

The most important line in the whole prompt is about failure: if a script errors or a link is dead, report it in the digest and move on. Never fabricate. Agents fill gaps with plausible-looking output if you let them, so you have to make "nothing found" an acceptable answer.

Proof It Actually Runs: A Month of Real Data

Talk is cheap, so here's the detail behind the numbers up top. evaluated-jobs.csv has 268 roles logged. 146 of those were scored under the current rubric (the rest predate it). Of the scored roles, 33 came back at 70% or higher, the "apply first" tier in the daily digest, and 56 cleared 65%. That's the actual output of the scoring engine described above: not a vague sense of what looks promising, a specific, ranked shortlist every morning.

Broken down by day: across 26 digests, the system evaluates about 7 new roles a day. That number comes from both the board check and the web search combined, about 3.6 a day from the board check, about 2.9 a day from web search, which has a cap of 5. Of the 7 evaluated, about 1.6 a day clear the 70%+ "apply first" bar and about 3 a day clear 65% overall. Some mornings that's nothing, some mornings it's two or three roles worth applying to before I've had coffee.

On the application side, job-tracker.csv shows 44 applications submitted off the back of this system. 3 of those reached an interview stage, 4 interviews total since one company brought me back for a second round. No offer yet, the rest split between still-awaiting-response and rejected. That's the number I actually care about, not roles scored, not roles found, applications that turned into a real conversation with a human.

Obviously, your results will vary based on how strong a candidate you actually are for the roles you're targeting. This system doesn't change that, it's not a hack that gets you interviews you're not qualified for. What it's actually for is giving you an honest, consistent read on how strong a match your profile is to a given job description, so you spend your effort on the roles worth it instead of guessing.

So how much time did this actually save? A manual version has to recheck all 99 watchlist companies every day, not once, since a new posting can show up any morning: 99 × 26 days = 2,574 checks, about 21 hours at 30 seconds each. Reading and scoring the 178 roles it found, 5 minutes apiece, is another 15 hours. That's roughly 36 hours of manual work compressed into about 4 hours of unattended runtime. And honestly, nobody actually checks 99 career pages a day by hand, this didn't just save time on a task I was doing, it did a version of the search I never would have kept up with manually.

Now the infrastructure side: 29 scheduled runs between June 12 and July 8, averaging about 9 minutes from trigger to done, most finishing in under 15. Total token usage across the month was a little over 112 million tokens, averaging roughly 3.9 million tokens per run. All of it ran under a flat monthly plan, none of it billed separately per run.

I switched models a handful of times over the month, partly testing, partly just whatever was available, and the differences were bigger than I expected:

  • composer-2.5 (3 runs): fastest and leanest by a wide margin, averaging 4 minutes and about 1.7 million tokens per run.
  • claude-4.5-haiku-thinking (6 runs): nearly as fast, about 3.8 minutes and 2.3 million tokens per run on average.
  • claude-4.6-sonnet-high-thinking (15 runs, the workhorse for most of the month): about 11 minutes and 4.4 million tokens per run on average.
  • claude-sonnet-5-thinking-high (2 runs): about 10 minutes and 6.3 million tokens per run on average.
  • claude-fable-5-thinking-xhigh (2 runs): slowest and priciest, averaging nearly 20 minutes and 5.7 million tokens per run.

My actual recommendation: use a Sonnet-class model for the daily run, not the lightest option and not the heaviest extended-thinking tier. This is a rule-following task, not an open-ended reasoning problem, but it still has to read messy job descriptions and apply a multi-part rubric correctly, more than I'd trust to the lightest model. Sonnet is the middle ground: reliable without paying the time and token premium the heaviest tiers charge.

That said, if you can, actually test a few models yourself. See what works for your rubric and your target companies and what doesn't, my numbers are from my specific setup, not a universal law.

What Stays Manual

The automation finds and scores. I decide. Reading the digest, choosing what to apply to, reviewing and editing every tailored resume and cover letter line by line before it becomes a PDF, sending every outreach message: all manual, on purpose. Nothing represents me to another human without me reading it first, and the Applied column only changes when I actually applied.

That division is the design principle for the whole system. Automate the funnel, not the judgment.

Build the Minimal Version

One theme runs through all of this: you don't have to do the setup work yourself. Claude Code can do almost every step below for you if you just ask, it's not limited to scoring job descriptions once everything else already exists.

  1. Install the prerequisites above: Claude Code, an IDE if you want one, Python, and a GitHub account.
  2. Make a folder, git init it, and drop your resume in as profile/resume.md. Paste in your LinkedIn profile too if you have one, as its own file.
  3. Open Claude Code in that folder and just talk to it: tell it your target comp band, location constraints, the role types you want and the ones you don't, and your real experience gaps. Ask it to turn that conversation into a CLAUDE.md with a target profile and a scoring rubric built around what you actually said, not a generic template. Push back on the first draft if the rubric doesn't feel honest, that back and forth is most of the value.
  4. Ask it to set up data/evaluated-jobs.csv with a header row for you.
  5. Paste in a real job description and see what happens. It should score it against your rubric and log it. That loop alone already beats tab-hopping between forty postings.
  6. Once you know which companies you actually care about, tell your agent their names. Have it figure out which ATS each one runs on, build watchlist.json, and write the board-checking script, using the four endpoint patterns above as a starting point. Mine did all of this from a conversation, not from me hand-writing config files.
  7. Once the manual loop feels trustworthy, schedule it in the cloud if you want the before-you-wake-up version. Or don't. Running it yourself each morning works just as well.

The origin story, the results after 25 days of runs, and why I think about job searching as a funnel in the first place are in the original post.

One more plug while I have you: reach out through the form below, or follow me on LinkedIn. I'm job searching too, lifecycle marketing, marketing operations, marketing technologist, or an AI-forward GTM or growth engineering role. So if you've got a lead, know someone hiring, or get stuck building your own version of this, that's exactly what the form is for. And if you made it this far, share this with a friend who's job searching too, it might save them a few hours.

Reach out

Interested in working together?

Drop me a message about the role, the stack, or what you're trying to build.