Scan Job Portals
Set up and run the portal scanner to find new job openings automatically.
This guide walks through setting up portals.yml and running the portal scanner to find new job openings automatically.
Before you start
Make sure you have completed the Getting Started guide. You need cv.md and modes/_profile.md in place before scanning is useful.
Set up portals.yml
portals.yml is the file that controls what the scanner looks for and where it looks. You create it once from the included template, then edit it to match your target roles.
Run this command from the project folder:
cp templates/portals.example.yml portals.ymlOpen portals.yml in any text editor. The file has three sections. The steps below walk through each one.
Configure your title filter
The title filter is how the scanner decides if a job is worth showing you. It checks the job title against two lists of keywords.
Find the title_filter section near the top of the file:
title_filter:
positive:
- "AI"
- "ML"
- "Product Manager"
negative:
- "Junior"
- "Intern"
seniority_boost:
- "Senior"
- "Staff"
- "Lead"Replace the positive list with keywords that match your target roles. A job must match at least one keyword from this list to pass through. The check is not case-sensitive, so "AI" also catches "ai engineer".
Replace the negative list with keywords that rule a job out. If any of these appear in the title, the job is skipped — even if it matched a positive keyword.
Leave seniority_boost as is unless you want to add or remove seniority levels. These keywords do not filter anything out — they just move matching jobs higher in the results.
Example: If you are looking for Rails engineering roles, your filter might look like this:
title_filter:
positive:
- "Rails"
- "Ruby"
- "Full Stack"
- "Backend"
negative:
- "Junior"
- "Intern"
- "PHP"
- "Java"
seniority_boost:
- "Senior"
- "Staff"
- "Lead"Tip
Start with a small positive list of three to five keywords. You can always add more later. A long list makes it harder to see why a job passed or failed.
Add companies to track
The tracked_companies section is a list of specific companies you want the scanner to check every time. The scanner goes directly to each company's job page and reads the open roles.
Find the tracked_companies section near the bottom of the file. Each entry looks like this:
tracked_companies:
- name: Anthropic
careers_url: https://job-boards.greenhouse.io/anthropic
api: https://boards-api.greenhouse.io/v1/boards/anthropic/jobs
enabled: true
- name: OpenAI
careers_url: https://openai.com/careers
enabled: trueTo add a company, copy an existing entry and change the values:
| Field | What to put |
|---|---|
name | The company name as you want it to appear in results |
careers_url | The direct URL to the company's job listings page |
api | Optional. The Greenhouse API URL if the company uses Greenhouse. Leave it out if you are not sure. |
enabled | Set to true to include the company or false to skip it without deleting the entry. |
To find a company's careers_url, go to the company's website, click Careers or Jobs, and copy the URL of the page that lists open roles. That is the URL to use.
To disable a company without deleting it, set enabled: false:
- name: Stripe
careers_url: https://stripe.com/jobs/search
enabled: falseNote
Every company in tracked_companies must have a careers_url. Without it, the scanner has no page to visit and will skip that entry.
Review the search queries
The search_queries section runs broader web searches to find roles at companies not in your tracked list. The template includes many pre-built queries.
You do not need to change this section to get started. The pre-built queries cover major job boards including Greenhouse, Ashby, among others.
When you are ready to customize, each query looks like this:
search_queries:
- name: Greenhouse — Rails Engineer
query: 'site:job-boards.greenhouse.io "Rails Engineer" OR "Ruby on Rails" remote'
enabled: trueTo turn off a query you do not need, set enabled: false. To add a new one, copy an existing entry, give it a new name, and update the query text.
Run a scan
There are two ways to run a scan. Use the one that fits what you need.
Option A — Run the script directly
npm run scanRun this from the project folder. The script reads portals.yml, hits each company's API directly, and writes any new jobs to data/pipeline.md. It uses no AI tokens and runs in about 30 seconds.
This option only works for companies that use Greenhouse, Ashby, or Lever. Companies without one of those platforms are skipped.
Preview results before saving them:
npm run scan -- --dry-runThis runs the full scan but does not write anything to disk. Use it to check your filter settings before committing results.
Scan a single company:
npm run scan -- --company AnthropicReplace Anthropic with any company name from your tracked_companies list. The match is not case-sensitive.
Option B — Run the AI-powered scan inside Claude Code
Open Claude Code in the project folder and type:
/career-ops scanThis version visits each company's job page directly using a browser, so it works even for companies that do not have a public API. It also runs the search_queries from your portals.yml to find roles at companies not in your tracked list.
Use this option when:
- A company in your list does not use Greenhouse, Ashby, or Lever
- You want to discover new companies, not just check known ones
- The script scan missed something you expected to see
Tip
Option B uses Claude API tokens and takes longer than the script. For a quick daily check, Option A is faster.
Read the scan output
When the scan finishes, you will see a summary like this:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Portal Scan — 2026-04-13
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Companies scanned: 42
Total jobs found: 318
Filtered by title: 291 removed
Duplicates: 4 skipped
New offers added: 23
New offers:
+ Anthropic | AI Engineer | San Francisco, CA
+ ElevenLabs | Solutions Architect | Remote
...
→ Run /career-ops pipeline to evaluate new offers.Here is what each line means:
| Line | What it tells you |
|---|---|
| Companies scanned | How many companies from tracked_companies were checked |
| Total jobs found | All open roles seen across those companies, before any filtering |
| Filtered by title | Jobs removed because the title did not match your title_filter |
| Duplicates skipped | Jobs already in your pipeline or tracker — not added again |
| New offers added | Jobs that passed all filters and are new to you |
If New offers added is 0, your title filter may be too strict. Try adding a keyword to your positive list and run the scan again.
Where new jobs go: Each new offer is added as a line in data/pipeline.md. That is the list of jobs waiting to be evaluated.
What data/scan-history.tsv does: Every URL the scanner sees — whether it was added, filtered, or skipped — gets logged here. This is how the scanner avoids showing you the same job twice across future scans. You do not need to edit this file.
Evaluate new offers from a scan
After a scan adds new offers to data/pipeline.md — your list of pending job offers — run the pipeline command to evaluate each one against your profile. This step uses Claude to read the job description, compare it against your cv.md and modes/_profile.md, and produce a scored summary for each role.
Open Claude Code in the project folder and type:
/career-ops pipelineClaude reads every pending URL in data/pipeline.md, visits each job posting, and evaluates it. Results come back in batches. Each batch shows a table like this:
Batch 1 done (Anthropic, ElevenLabs, Mistral):
| # | Company | Role | Score | Legitimacy |
|---|---|---|---|---|
| 024 | Anthropic | AI Engineer | 4.8/5 | High Confidence |
| 025 | ElevenLabs | Solutions Architect | 4.1/5 | High Confidence — fast-growing |
| 026 | Mistral AI | Product Manager | 3.2/5 | Proceed w/ Caution — role closed |
Here is what each column means:
| Column | What it tells you |
|---|---|
| # | The report number. Use it to find the full report in reports/ — for example, report 024 is reports/024-anthropic-2026-04-13.md |
| Score | How well the role fits your profile, from 1.0 to 5.0 |
| Legitimacy | Whether the posting looks real and active. "High Confidence" means the job is live and the details check out. "Proceed w/ Caution" means something raised a flag — read the note next to it. |
To read the full evaluation for a role, you have two options:
- Open the report file directly — find it in the
reports/folder using the number from the table. - Use the dashboard — run
./dashboard/career-dashboardfrom the project folder to browse all your reports in a terminal interface. You can filter by score and status, and open any report without leaving the terminal.
What to do next
When /career-ops pipeline finishes, each job has a score from 1.0 to 5.0. You can find the full report for any role in the reports/ folder. The score shows how well the role matches your profile.
Use the score as your starting point:
| Score | Recommended action |
|---|---|
| 4.5 or higher | Strong match. Run /career-ops apply right away. |
| 4.0 – 4.4 | Good fit. Run /career-ops apply. You can also run /career-ops contacto first to reach out to someone on the team before applying. |
| 3.5 – 3.9 | Could go either way. Run /career-ops deep to learn more before you decide. |
| Below 3.5 | Skip it unless you have a specific reason to apply. |
Here is what each command does:
/career-ops apply— Opens the application form and writes answers tailored to your profile. It stops before submitting so you can review everything first./career-ops contacto— Finds a hiring manager or team member on LinkedIn and drafts a short outreach message for you to send./career-ops deep— Researches the company and the role in depth. Use this when you are on the fence and want more information before deciding./career-ops tracker— Shows the status of every role in your pipeline. Run this any time you want to see where things stand.
Tip
You do not have to go in order. If a role scores 4.8 but you have never heard of the company, run /career-ops deep first. The commands work on their own — use which ever one fits your situation.