
I Built an AI Job-Hunting Agent for (Almost) $0 — Here's How
8 mins read
Job hunting as a developer is a second full-time job. Every morning it's the same loop: open LinkedIn, Indeed, JobStreet, Wellfound, type "React developer remote," scroll past the same expired postings, open twenty tabs, read job descriptions that don't match, and maybe — just maybe — find two roles actually worth applying to. Then write a cover letter for each one from scratch.
I'm a web developer in the Philippines looking for remote work, and I got tired of doing this manually. So I did what any developer would do: I spent a weekend automating it.
The result is a small agent that reads my resume, searches the web for matching remote roles, scores every posting against my actual profile using Claude, and drafts a tailored cover letter for every job worth applying to. I review everything in a local dashboard over coffee
Here's the part I'm most proud of, though: it costs me almost nothing to run. And that constraint shaped the entire architecture.
The Broke Developer Constraint
Let me be upfront: I'm not running a funded startup. I'm a solo dev paying for my own tools, and every subscription and API bill comes out of my own pocket. So before writing any code, I set one rule — this project must not add a new monthly bill.
That rule immediately ruled out the "obvious" architecture. The standard way to build something like this is to call the Anthropic API directly: send the scraped job listings plus my resume to the Messages API, get back scores and cover letters, pay per token. The API is priced per million tokens (Sonnet-class models are around $3 per million input tokens and $15 per million output tokens), which sounds cheap until you look at what this pipeline actually does. Every run sends my full resume, plus dozens of scraped job postings — full descriptions, sometimes entire listing pages — and generates match analysis for each one, then a batch of cover letters. Do that daily during an active job hunt and the tokens add up to a real bill. Not a huge one, but a recurring one, for a tool that only exists to help me get a job (i.e., precisely when I have no income).
Then it hit me:
I already pay for Claude. Not the API — Claude Code, the CLI agent I use every day for development, on a flat monthly subscription.
Claude Code as a free inference engine
Claude Code has a headless mode most people never touch: claude -p "your prompt" runs a single prompt non-interactively and prints the result to stdout. It can read local files, and its usage is covered by the subscription I'm already paying for as my daily coding tool. The marginal cost of one more pipeline run is zero.
So instead of the Anthropic SDK, my "AI layer" is a Python function that shells out to the CLI:
pythonresult = subprocess.run(
["claude", "-p", prompt, "--output-format", "text", "--allowedTools", "Read"],
capture_output=True, text=True, encoding="utf-8",
)Two details do a lot of work here:
---allowedTools Read lets Claude read `resume.md` and output/raw_jobs.json off disk by itself — I don't have to stuff everything into the prompt.
- Write access is deliberately not allowed, which forces Claude to print its answer to stdout, where my script captures and parses it as JSON.
Each AI step is just a markdown prompt file in prompts/, so tweaking the agent's behavior is editing a text file, not redeploying code. A CLAUDE.md in the repo gives the agent standing context: target roles, remote-only preference, what to avoid.
Is this the "proper" way to build a production AI product? No — if I were serving other users I'd use the API with proper rate limiting, streaming, and structured outputs. But for a personal tool that runs a few times a day on my own machine, piggybacking on a subscription I already need is exactly the right amount of engineering. Frugality is a feature.
The same thinking applied to scraping: Firecrawl's free tier credits cover my searches and scrapes, and the pipeline caps how many pages it scrapes per run so I never blow through them.
How the pipeline works
The whole thing is four steps orchestrated by one Python file:
- Build a search config from my resume. Claude reads
resume.mdand extracts target role titles, my strongest skills, and ~10 ready-to-run search queries withsite:filters for LinkedIn, Indeed, Wellfound, JobStreet, OnlineJobs.ph — plus a few Reddit hiring communities. - Discover and scrape. Firecrawl runs each query, then scrapes every result page. This step has a subtlety: a search hit is often a listing page containing dozens of postings, not a single job. So each page goes through Firecrawl's LLM extraction with a JSON schema that pulls out every individual posting — title, company, location, direct URL, posted date. If a scrape fails, the pipeline falls back to the search snippet so nothing is lost.
- Analyze and score. Claude reads my resume and the raw scraped jobs, then scores each posting 0–100 against my specific profile: stack match, seniority fit, remote-friendliness for my timezone, posting freshness, and red flags (Java-only shops, "US citizens only," expired listings). Each job gets a verdict — apply, review, or skip — plus a one-line "suggested angle" for how I should frame my application.
- Cover letters. For every "apply" verdict, Claude drafts a short, specific cover letter using that suggested angle, mentioning my actual projects by name. Each one lands in
output/cover_letters/as markdown.
On top of that sits a small FastAPI server and a single-file HTML dashboard (vanilla JS + Tailwind, no build step). I hit "Run Agent," watch live progress stream in over Server-Sent Events, then filter jobs by score and verdict, mark them applied or skipped, and read cover letters in a modal. The applied/skipped status persists to a JSON file, so the dashboard doubles as my application tracker.
The pipeline is fully tested with the AI and scraping mocked out, so pytest runs in about a second and I can refactor without fear.
So... did it actually work?
Honest results, because I hate posts that end with "and then everything was perfect."
In the past few weeks of running this agent, it got me 11 initial interviews. Eleven! For a solo dev in the Philippines applying to remote roles, that's a response rate I never came close to when I was applying manually. The pipeline clearly works at what it was built for: finding fresh, well-matched postings and getting my application in front of humans with a cover letter that doesn't sound like a template.
And then: none of them followed up after the first interview. Zero. Every single one went quiet after the initial call.
That stung to type, but it taught me something important about what this tool is — and isn't. The agent solves the top of the funnel: discovery, matching, and first contact. It can't control ghosting, budget freezes, or a company quietly deciding to hire in a different timezone. Eleven first-round conversations is proof the automation works; converting them is a completely different problem, and it's on me — tightening how I present my experience, asking better questions in that first call, and following up harder afterward.
If anything, that's the strongest argument for keeping the cost at zero. A tool with an 11-interviews-but-no-offers-yet track record is easy to justify when it costs nothing to run. If I were paying per-token API bills for it, I'd be doing depressing cost-per-offer math right now.
What I learned
Constraints produce better architecture. "No new bills" forced me toward a design that's also simpler: no SDK dependency for the AI layer, prompts as plain markdown files, JSON files instead of a database, one HTML file instead of a React build.
Treat the LLM as a subprocess and life gets easy. My "integration" with Claude is subprocess.run and json.loads. The error handling is one function: if the output isn't valid JSON, fail loudly with the first 300 characters of what Claude actually said. That's it.
Let the AI read files instead of building prompts. Giving Claude read-only filesystem access meant my prompts stayed short ("Read resume.md, then read output/raw_jobs.json, then...") instead of becoming string-interpolation monsters.
The scoring is the real product. Scraping jobs is commodity work. The thing that saves me hours is Claude reading a posting and telling me "88/100 — exact stack match, but no posted date, freshness unverified" so I only spend attention on the top of the list.
The code is open source on my GitHub — you drop in your own resume.md and Firecrawl key, and the agent tailors itself to your profile, because everything derives from the resume. If you're a fellow broke developer hunting for remote work: this one's for you.
Written by
