CASE STUDY · APR 2025 → NOW
cold emails that read like I actually looked at the company
I was applying to a lot of places badly. So I built the thing that reads my resume, reads the company, and writes the email I would have written if I had two more hours.
pauseddeploy's paused, not the project
drop the main screenshot for this project
1 minRESUME UPLOAD → PERSONALISED DRAFT
Upload, parse, enrich with company context, generate. The rate limiter exists because the first version could have made me a spammer in an afternoon, and I'd rather the tool have a conscience than not.
PDF→JSONRESUME PARSING
per-domainRATE LIMITING
FastAPIBACKEND
draftsNEVER AUTO-SENDS
FIG.2 — FROM A PDF TO SOMETHING WORTH SENDING
01upload
resume inPDF, whatever format02parser
structure itskills, projects, dates03context
read the companywhat they build, who for04llm
write the draftspecific, short, not fawning05limiter
throttle mecaps per domain per day- fastapi — upload, parse, generate
- worker — parsing and enrichment off the request path
- store — drafts and per-domain send counters
DECISIONS, AND WHAT THEY COST ME
nothing here was free- I CHOSE
- drafts only, never auto-send
- BECAUSE
- An automated system emailing strangers in my name is a reputational risk I'm not taking for convenience.
- IT COST ME
- I still have to press send, which means I sometimes don't
- I CHOSE
- rate limiting from day one
- BECAUSE
- The honest version of this tool sends fewer, better emails. Volume is what makes it spam.
- IT COST ME
- deliberately capped the thing's main capability
- I CHOSE
- parse to structured JSON first
- BECAUSE
- Handing a raw PDF to a model gave vague emails. Structure made the output specific.
- IT COST ME
- parsing is brittle across odd resume layouts
- I CHOSE
- company context before generation
- BECAUSE
- "I admire your work" is worthless. Naming what they actually build isn't.
- IT COST ME
- another API call and a cache to keep it cheap