Pre-Configured Portal Providers and Search Queries in Career-Ops' portals.yml

The Career-Ops portals.yml configuration includes six pre-configured ATS providers (Greenhouse, Ashby, Lever, Workable, SmartRecruiters, and Recruitee) and dozens of customizable search queries targeting specific job boards and role keywords.

The portals.yml file (generated from templates/portals.example.yml) serves as the central configuration hub for the Career-Ops job-scanning subsystem. It defines both how the scanner retrieves structured data from company Applicant Tracking Systems and where it searches for new opportunities across public job boards. Understanding these pre-configured portal providers and search queries allows you to customize your automated job search without modifying the core source code.

Pre-Configured Portal Providers

The provider list defines the low-level adapters that scan.mjs uses to fetch structured job data from major ATS platforms. These identifiers are auto-loaded from the providers/ directory and mapped to specific URL patterns.

The six default providers implemented in santifer/career-ops are:

  • greenhouse – Targets Greenhouse ATS instances at job-boards.greenhouse.io/<slug> or boards.greenhouse.io/<slug>.
  • ashby – Handles Ashby ATS careers pages at jobs.ashbyhq.com/<slug>.
  • lever – Parses Lever ATS listings from jobs.lever.co/<slug>.
  • workable – Connects to Workable career portals at apply.workable.com/<slug>.
  • smartrecruiters – Supports SmartRecruiters at careers.smartrecruiters.com/<slug> or jobs.smartrecruiters.com/<slug>.
  • recruitee – Handles Recruitee-based sites using the pattern <slug>.recruitee.com.

When scan.mjs processes a careers_url entry in your tracked companies list, it auto-detects the correct provider based on these URL patterns before invoking the provider-specific fetchJobs() method.

Search Queries Configuration

The search_queries section contains WebSearch definitions that feed the scanner with URLs from public job boards. Each entry in templates/portals.example.yml follows a consistent YAML structure with three fields: name, query, and enabled.

A typical search query entry looks like this:

- name: Ashby — AI PM
  query: 'site:jobs.ashbyhq.com "AI Product Manager" OR "Senior Product Manager AI" remote'
  enabled: true

The full catalogue in the example file spans dozens of boards including Ashby, Greenhouse, Workable, Remotive, WeWorkRemotely, and region-specific aggregators for the EU and Turkey. Each query combines site-specific filters with role keywords to return only relevant positions.

How scan.mjs Consumes the Configuration

At runtime, scan.mjs performs a two-phase discovery process using the portals.yml configuration:

  1. Provider Detection – For each company in tracked_companies, it matches the careers_url against the provider patterns defined in the providers/ directory (e.g., providers/greenhouse.mjs, providers/ashby.mjs).

  2. Search Execution – It iterates through the search_queries list and executes only those entries where enabled: true, scraping the results to extract new job postings.

According to the Career-Ops source code, the providers/*.mjs files implement two critical functions: detect() to confirm URL compatibility and fetchJobs() to retrieve structured listing data.

Working with the Configuration Programmatically

You can inspect and manipulate the pre-configured providers and search queries using Node.js before running a scan.

List Available Providers

To see the built-in provider identifiers programmatically:

import fs from 'fs';
import yaml from 'js-yaml';

const portals = yaml.load(fs.readFileSync('templates/portals.example.yml', 'utf8'));

const providers = [
  'greenhouse',
  'ashby',
  'lever',
  'workable',
  'smartrecruiters',
  'recruitee',
];
console.log('Enabled providers:', providers);

Extract Active Search Queries

To filter for only the enabled search definitions:

import fs from 'fs';
import yaml from 'js-yaml';

const data = yaml.load(fs.readFileSync('templates/portals.example.yml', 'utf8'));

const enabledQueries = data.search_queries
  .filter(q => q.enabled)
  .map(q => ({ name: q.name, query: q.query }));

console.table(enabledQueries);

Setting Up Your Local portals.yml

Before running your first scan, copy the example configuration to the project root and customize it:


# Copy the template to create your runtime configuration

cp templates/portals.example.yml portals.yml

# Edit portals.yml to disable unwanted queries or adjust keywords

# Then execute the scanner

node scan.mjs

The scanner automatically picks up the provider definitions from providers/ and respects the enabled flags in your portals.yml file.

Summary

  • Six Pre-Configured Providers: The portals.yml template includes built-in support for Greenhouse, Ashby, Lever, Workable, SmartRecruiters, and Recruitee ATS platforms.
  • Search Query Structure: Each query contains a human-readable name, a site-filtered query string, and an enabled boolean flag.
  • File Architecture: templates/portals.example.yml serves as the reference configuration, while portals.yml (created by copying the template) drives the runtime behavior of scan.mjs.
  • Provider Implementation: Actual fetching logic resides in providers/*.mjs files, which expose detect() and fetchJobs() methods used by the scanner.

Frequently Asked Questions

What is the difference between portals.example.yml and portals.yml?

templates/portals.example.yml is the version-controlled reference file in the santifer/career-ops repository that contains all default providers and search queries. You create portals.yml by copying this template to your project root, then customize it to enable only the queries and companies you want to track. The scanner specifically looks for portals.yml at runtime, not the template file.

How do I disable a specific search query?

Open your local portals.yml file and locate the query entry under the search_queries section. Change the enabled field from true to false. The scanner in scan.mjs filters the list using filter(q => q.enabled) before execution, so disabled entries are skipped automatically without deleting the configuration.

Can I add a custom ATS provider that is not in the default list?

Yes, you can extend the system by creating a new file in the providers/ directory (e.g., providers/custom.mjs) that exports detect() and fetchJobs() functions following the existing provider pattern. However, you must also manually require this provider in scan.mjs or modify the auto-loading logic, as the default list only includes the six pre-configured providers.

Where are the provider implementations located?

The provider logic for each ATS resides in the providers/ directory at the repository root. Each provider (e.g., greenhouse.mjs, ashby.mjs, lever.mjs) contains the platform-specific parsing logic that handles URL detection and job data extraction. These modules are separate from the YAML configuration but are referenced by scan.mjs when processing URLs defined in portals.yml.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →