Job Scout: A Daily AI Agent That Finds and Scores Job Openings
Overview
During my career break I found myself checking the same careers pages every few days: AI labs, AI safety organizations, robotics companies, VC job boards and fellowships. Most of the time nothing had changed, and when something did it was easy to miss. After a few weeks of this I decided it was a good problem to hand to an agent, and a good excuse to build one properly.
Job Scout runs every morning at 8 AM. It reads about 80 sources, drops anything I’ve already seen, scores each new opening from 1 to 10 against my profile, and saves the best matches to a tracker page with a one-line reason for each score. When I go through them I mark jobs Interested or Rejected (with a reason), and the next run uses those decisions to adjust its scores.
I built it with Claude Code over three days, with most of the work done in the first two. It runs as a Claude Code skill, scheduled daily from the Claude desktop app. Claude wrote most of the code and also makes the judgment calls when it runs, so this post covers both sides.
![]()
The tracker after a run. Each saved listing gets a score, a category and a one-line reason; “+1 P6” means a learned rule raised the score.
How It Works
Every run follows the same steps:
- Load my profile, background, learned preferences, and every job already in the tracker.
- Turn any new Interested or Rejected decisions into scoring rules.
- Pull listings from job-board feeds (Greenhouse, Lever, Ashby, Workday and a few others) with a Python script, and have Claude read the sources that don’t have a feed, like fellowship pages.
- Drop listings I’ve already seen, ones that fail a keyword prefilter, and duplicates of jobs already saved.
- Have Claude score what’s left against my profile and write a one-line reason for each score.
- Save the top matches in each category (AI safety, robotics, product, other and fellowships) to the tracker.
- Log what it checked, what broke, and what it changed.
The tracker is a Claude artifact with its own database, and it works in both directions. I read the results there, but I also steer the agent from it: rejecting a job with a reason, pasting in a careers page link for a source it couldn’t read, adding a company to watch, or approving and declining requests it makes.
Design Decisions
The first decision was what should be code and what should be the model. Fetching feeds, remembering what has been seen, prefiltering and deduplicating always have the same right answer, so they live in a Python script that is cheap to run and easy to test. A typical run fetches somewhere between 1,500 and 3,000 listings, and the script cuts that down to a few hundred before Claude reads anything. Claude handles the parts a script can’t: deciding whether a vaguely titled role is actually a fit, reading a careers page with no feed, explaining a score, and turning “I rejected these three because they’re web-dev heavy” into a rule.
The second was keeping the pipeline fixed and making sources plug-ins. The early versions were just a written process that changed every time I added a new kind of source. After a few runs it was clear the variation was all in where listings came from, so each kind of source (a Greenhouse board, a Workday site, a plain careers page) became a plug-in with its own instructions. Now adding a company is one line in a watch list instead of a change to the agent.
The third was that every run starts with no memory. All of its state lives in files in the skill’s git repo and in the tracker’s database. That makes runs repeatable, and every change the agent makes to its own files shows up as a git commit I can read or revert.
Finally, each category has a daily cap (15 jobs and 2 fellowships in total), so a busy day doesn’t bury me in listings, and ties are broken by fixed rules so the same inputs give the same list.
Features
Sources
Most companies post jobs through an applicant tracking system like Greenhouse, Lever, Ashby or Workday, and most of those have a public feed. So the script reads those feeds directly, which is fast, free and reliable. Job boards like 80,000 Hours, YC’s job pages and Wellfound are searched every day. Anything without a feed, like a fellowship page or a custom careers site, is read by Claude.
Checking all 80 sources every day would be slow and mostly pointless, so they rotate. Priority companies, the job boards and fellowships are checked daily, and the rest are split across the week: AI labs and AI safety organizations on Monday, robotics on Tuesday and Wednesday, big tech and security on Thursday, and VC firms on Friday.
Finding the right feed for a company turned out to be a lot of the work. The script has a discover command that looks for a known feed behind a careers page, and when it can’t find one I can paste a careers page or job link into the tracker as a hint. The next run tries to find a feed from it, falls back to reading the page, and writes back whether it worked and why. I can also add a company from the tracker and it gets picked up on the next run.
Proxy for Blocked Sites
Some careers sites block anything that isn’t a browser, or only render their listings with JavaScript. For those the agent can use Bright Data, a paid proxy and scraping service, but only as a last resort. It has to try the free methods first, and if they fail it doesn’t use the proxy on its own. Instead it marks the source on the tracker with the observed cause (for example “403: bot protection”) and an estimate of the cost, and I approve or decline it. Approved sources are limited to about 25 page reads per run against a fixed credit budget, and the agent counts and logs every call.
This worked out better than expected because trying to read a site through the proxy often turned up a free feed. A couple of sources that looked like they needed it ended up as free feeds instead.
Proposed Sources
Once a week the agent looks for new places to search based on what it has already seen: job boards that good listings came from, companies that keep showing up in robotics and AI safety funding news, VC firms backing companies I’m interested in, and new fellowships. It proposes at most three, each with a one-line reason and an example listing it would have found. I approve or reject them on the tracker, and rejected ones are never proposed again (unless I change my mind, which I did once).
Referrals
The tracker has a Referrals tab where I list people I know at different companies. Any job at one of those companies gets a point added to its score, a badge on its card, and a note in its reason. When I add or remove a referral, jobs already in the tracker are re-scored the next run, and I can filter to only jobs where I have a referral.
Minimizing Token Use
A run can see thousands of listings, and having Claude read every one of them every day would burn through tokens quickly. Most of the design is about making sure Claude only reads what’s actually new and plausibly a fit.
The biggest savings come before Claude sees anything. The script keeps a local cache of every posting it has seen, so a listing is only ever evaluated once. It then runs a keyword prefilter on titles and locations (about 130 patterns) that drops obvious misses like chip design or data center roles. On the October 27 run, 1,681 listings were fetched, 747 passed the prefilter, and only 85 were new, so Claude only had to look at 85 listings instead of 1,681. I kept the prefilter generous on purpose, since anything it drops is never scored.
What Claude does read is trimmed down too. The script strips the HTML from each posting and cuts the description to 1,500 characters. When there are more than 60 new listings, like on the first run, Claude first looks at one-line summaries, picks the plausible ones, and only reads those in full. Only a one-line reason is stored for each saved job, never the full description, so loading the tracker at the start of each run stays small.
The rest comes from not doing work twice. Sources with a feed are read by the script instead of Claude, so the more feeds the agent finds, the cheaper each run gets. The rotation means most sources are only checked once a week. And when a site needed the proxy, that’s noted in the watch list so later runs skip the free attempt that’s known to fail.
Guardrails
This is the part I spent the most time on, and it’s the part closest to what I worked on in robot safety. An agent that runs every day without me watching needs clear limits on what it is allowed to change, so I wrote those limits into the skill:
- It can’t edit its own instructions, my profile or the prefilter. If it thinks the prefilter is wrong it can propose a change, but I make it.
- When a source breaks, it can only fix that one source’s plug-in. It gets two attempts, has to prove the fix by fetching real listings, and commits the change on its own. If both attempts fail it reverts and falls back to reading the page.
- Failures are logged with what actually happened (“403: bot protection”, “page needs JavaScript”) rather than a plausible guess.
- Listings are only marked as seen after they’re saved, so a run that fails halfway doesn’t lose anything.
- The proxy needs my approval for each source, and it won’t ask again if I decline.
- New sources are proposed, not added.
- The only statuses it writes are New and Closed. Interested, Applied and Rejected are mine.
Learning From Feedback
When I reject a listing I can tag why, like “wrong domain” or “wrong role type”. The agent turns those into rules about the kind of role, never a single company, for example avoiding roles centered on web development. It has learned seven so far.
I didn’t want it to quietly filter out good jobs, so learning is limited. Rules can only nudge a score by up to 2 points in total and never drop a listing outright. If I mark something Interested that a rule penalized, the rule’s effect is halved until I decide whether to keep it, and after three contradictions it retires on its own. It also saves a few “second looks” each week, strong listings that a rule pushed below the line, so I can tell it when a rule has gone too far.
Building It With Claude
The skill’s git repo has 61 commits, 58 of them from the first two days, and all of them were written with Claude. My part was describing what I wanted (“watch VC job boards”, “learn from what I reject”), pushing back on the design, and reviewing the changes. Claude wrote the instructions, the Python script and the tracker page.
A few things made this work better than I expected. Real runs turned out to be the best tests: the first run checked every source once and found feeds I didn’t know existed, and a test across sources caught duplicates like “Engineer & Researcher” vs “Engineer and Researcher”. Partway through I also had Claude review the whole design from scratch, which fixed thresholds, caps, duplicate matching and time zones in a single pass. Some of the commits now come from the agent itself, repairs to broken sources and new learned rules, each in its own commit so I can see exactly what it changed.
Results
After 6 runs between October 21 and 27, it has fetched about 13,700 listings and saved 99 to the tracker, most of them scoring a 7 or 8. The one-line reasons have been the most useful part, since they make it quick to decide which postings are worth reading in full.