All work
Case Study — Match Moth Job Search  ·  7 min read
AI Product Side Project Automation

I Built My Own Way Out

I went looking for a UX role in one of the roughest tech markets in years. Instead of grinding LinkedIn every morning, I built an AI tool to run the search for me, end to end: data pipeline, job-fit scoring, daily email digest, all designed and built solo.

Poke at the real thing

Not a mockup. Launch the full, live app loaded with read-only demo data: real listings, real AI job-fit scores, and the actual interface I open every morning.

Launch the live demo (read-only) ↗
$1–2/mo
AI scoring cost, down from about $50 a month
12+
Data sources scanned, tens of thousands of companies daily
5
Factor job-fit score, fully transparent and inspectable
8am
A ranked, AI-evaluated email digest, every morning
Role Solo: product, design, and build
Users Job seekers (built for myself)
Stack React, Next.js, TypeScript, Supabase, Vercel, scheduled crawl via GitHub Actions + cron-job.org
Built with Claude Code, Claude, and ChatGPT for the build; ATS, aggregator, and board APIs (Ashby, Greenhouse, Lever, Workday, BambooHR, iCIMS, Adzuna, JSearch, The Muse, Dribbble, Built In)

Scratch your own itch

I went looking for a UX role in one of the roughest tech markets in years. The obvious move was to open LinkedIn every morning and start scrolling. Instead I asked a different question: I'm a product designer who works on AI products all day. Could I build my own?

So I did. Match Moth kicks off every morning at 8am, takes around twenty minutes to scan more than a dozen job sources, scores every role against my actual experience, and emails me a ranked, AI-evaluated shortlist. I designed it, specced it, and built it solo, using a stack of APIs and tools I had never touched before. AI wrote most of the code. I made the decisions.

Product manager for a team of one

I scoped this the way I'd scope any product: the smallest thing that proves the idea. A search feed, basic filters, a database. I ideated the architecture with Claude and ChatGPT, weighing options I didn't fully understand at first and learning enough to make an informed call on each one.

The stack came together fast, and most of it was free. Supabase for the database, GitHub to host the repo, Vercel for the app, React, Next.js, and TypeScript for the UI. The crawl runs on a schedule, kicked off by cron-job.org, an external timer service that pings the job on time, after the scheduler I started with kept drifting and skipping runs. Hosting, database, and email all sit on free tiers, which still feels a little like magic after years of paying a hundred dollars a year just to host a basic site. The real costs are the parts doing the thinking: a $20 a month Claude subscription that powers the build through Claude Code, plus the data and AI APIs I get to below.

TRIGGER Timer cron-job.org · 8am daily WORKFLOW GitHub Actions daily, fast, and paced lanes 24 SOURCES Mixed Sources ATS · aggregators · boards GLOBAL JOB LIST Dedup + Classify role, seniority, salary QUEUES WHAT CHANGED PER USER Keyword Gate recall-tuned first pass OPENAI Job-Fit Scoring 0–100 across 5 factors ON THE GOOGLE CLOUD RUN WORKER DATABASE Supabase Postgres · row-level security DELIVER Daily email digest Resume tailoring READS QUEUES RESCORES & TAILORING ON VERCEL Match Moth Web App Next.js · job feed, score breakdowns, application tracker, resume workspace
The pipeline: a timer kicks off staggered crawl lanes across 24 sources, then dedup and classify. The crawl saves the listings and queues what changed; a Google Cloud Run worker picks it up, runs the per-user keyword gate and the OpenAI fit-scoring calls, and writes into Supabase. The Match Moth web app reads that database to serve the feed, and queues the on-demand work back to the worker.

The hard part was the data

The feed is only as good as what feeds it. My first version pulled from a small, hardcoded list of companies and ran them one at a time. Fine for a demo. Useless for an actual search.

So the real engineering challenge became sourcing. The crawler pulls from more than a dozen sources of three kinds: the major applicant tracking systems companies post through directly (Ashby, Greenhouse, Lever, Workday, BambooHR, iCIMS), aggregator APIs like Adzuna, JSearch, and The Muse, and design-specific boards like Dribbble and Built In. The ATS pages matter most. They're the canonical source, before a listing ever syndicates out to LinkedIn or Indeed and goes stale. I scaled the company list from a few dozen to tens of thousands across the ATS platforms and added a concurrency pool so the crawl fires dozens of requests in parallel instead of plodding through them one by one. One of the aggregator APIs runs about $25 a month, and I blew past its request cap once before I added usage tracking. Going from a handful of companies to tens of thousands is the difference between a toy and a tool, and most of the hard problems lived right here.

Post directly · canonical
ATS platforms
GreenhouseLeverAshbyWorkdaySmartRecruitersRecruiteeBreezyBambooHRWorkableiCIMSOracle CloudEightfold
Straight from the company
Employer careers sites
AmazonAppleGoogle38 Fortune 500 RSS feeds
Broad coverage
Aggregators & remote boards
AdzunaJSearchRemoteOKWe Work RemotelyJobicyHimalayasRemotiveWorking Nomads
24 sources of three kinds, across four staggered crawl lanes. The ATS platforms matter most: the canonical source companies post to directly, before listings syndicate out and go stale.
Before dedup
~80,000
listings, many duplicated
After dedup
~7,000
unique roles
Spring 2026: tightening dedup cut ~80,000 listings to ~7,000 unique roles. Since loosened — it was collapsing large employers to one role apiece.

Sorting signal from noise

Early on the feed was a mess. I'd ask for senior roles and get junior ones. I'd ask for product designer and get product managers, because the word "product" sat in the title.

I built a first-pass scoring layer with Claude to push the stronger listings up: correct titles, salary listed, benefits mentioned, AI in the mix. It was mostly keyword matching, and it did its job. It screened out the obvious noise. But the top of the stack was still haphazard. Every promising role still needed me to read it and judge whether my background actually fit. That was the next problem worth solving.

Opening the black box

This is the part I care about most, because it's the same problem I design for professionally.

I added an AI job-fit evaluation layer. Every promising role gets read by an LLM and compared against a detailed record of my experience that lives in the project. The model returns a 0 to 100 job-fit score, and suddenly the roles I'd actually want were rising to the top on their own.

But a single number is a black box. What does an 80 even mean? Why 80 and not 65? Any user would ask that, so I did the work to make the score defensible. I researched how to evaluate job fit more rigorously, then broke the score into five dimensions: Role and skills, Company health, Compensation, Industry fit, and Growth signals. I added a short AI summary on each listing and a full breakdown on the detail page, so the score isn't a verdict handed down. It's a transparent argument I can inspect.

This is LLM-as-judge work, and it's the same evaluation-interface and model-trust design I do at the day job: deciding what the model scores, how it explains itself, and where the human stays in control. I just pointed it at my own problem.

Where the AI made odd choices

Letting AI write the code was a genuine accelerant. Whenever I hit something I didn't know how to do, the answer came back in seconds. But AI doesn't flag its own bad decisions. You only find them by paying attention.

Two of them cost me real money. The app was re-scoring my entire database on every crawl, thousands of paid API calls to re-evaluate roles it had already scored. I rewrote the logic to check for an existing score before calling the model. The bigger one was duplicates. My first dedup pass looked robust and wasn't. When I tightened it, the database dropped from around 80,000 listings to roughly 7,000. I'd been storing, scoring, and paying for the same jobs over and over.

Then I right-sized the model. I'd started on Claude Opus 4.7, a top-tier model that was overkill for comparing a job post to a resume and was burning $30 to $50 a month. I moved to Claude Haiku for the same quality at a fraction of the cost, then to ChatGPT nano, which dropped it again. The scoring now runs for a dollar or two a month.

MONTHLY AI SPEND $30–50 / mo Claude Opus 4.7 top-tier, overkill $10–20 / mo Claude Haiku same quality $1–2 / mo ChatGPT nano runs here now Right-sizing the model took scoring from $30–50 to $1–2 a month.
Monthly AI spend dropped across model swaps: Claude Opus 4.7, then Claude Haiku, then ChatGPT nano.

Making it mine

The first UI was a generic component-library default, and it felt like one. For a tool I'd open every morning, that wasn't good enough. I rebuilt it in the dark, glassmorphic style of my portfolio, with the small interaction details that make something feel considered. If I was going to live in it, I wanted it to be something I was proud of.

The real problem isn't finding jobs

Finding roles turned out to be the easy half. The hard half is reaching a human on the other side. Applications vanish into a black hole of hundreds or thousands of applicants, where no feedback comes back save the automated rejection email. Referrals and recruiter conversations are what actually move.

So that's where the product goes next. The roadmap maps a user's network against companies that are hiring, surfacing where a warm referral is one introduction away, and drafts personalized recruiter follow-ups after you apply. One rule sits underneath all of it: the AI drafts, it never sends. That's the same human-in-control principle I hold to in my professional AI work. The tool does the legwork. The person owns every message that goes out.

Did it work?

Yes, with an honest caveat. Every morning I review a ranked, scored, pre-summarized shortlist in a few minutes instead of scrolling job boards for an hour. The signal-to-noise is dramatically better, and the time it hands back is real.

What it hasn't solved is the brutal math of the current market. A strong tech role still draws hundreds of applicants, and a better-organized search doesn't change those odds on its own. That's exactly why the next chapter is about people, not listings.

But the project answered the question I started with. As a product designer, can I independently scope, design, and ship a working AI product? This is the proof. Not a prototype, not a mockup. Something I use every single day.

← Back to all work
AI Model Training Concept preview

Let's
talk.

Open to Senior or Staff UX design roles in enterprise, AI, or consumer. Available now.